2 using System
.Collections
.Generic
;
3 using System
.Diagnostics
;
4 using System
.Globalization
;
8 using System
.Text
.RegularExpressions
;
9 using System
.Threading
;
11 using Mono
.Unix
.Native
;
12 using Newtonsoft
.Json
;
14 // Shut up CLS compliance warnings from Json.NET.
15 [assembly
: CLSCompliant (true)]
17 namespace Mono
.Profiling
.Tests
.Stress
{
19 // https://github.com/xamarin/benchmarker/blob/master/tools/libdbmodel/Benchmark.cs
20 sealed class Benchmark
{
22 public string Name { get; set; }
23 public string TestDirectory { get; set; }
24 public bool OnlyExplicit { get; set; }
25 public string[] CommandLine { get; set; }
26 public string[] ClientCommandLine { get; set; }
27 public string[] AOTAssemblies { get; set; }
29 public static Benchmark
Load (string file
)
31 return JsonConvert
.DeserializeObject
<Benchmark
> (File
.ReadAllText (file
));
35 sealed class TestResult
{
37 public Benchmark Benchmark { get; set; }
38 public ProcessStartInfo StartInfo { get; set; }
39 public Stopwatch Stopwatch { get; set; }
= new Stopwatch ();
40 public int? ExitCode { get; set; }
41 public StringBuilder StandardOutput { get; set; }
= new StringBuilder ();
42 public StringBuilder StandardError { get; set; }
= new StringBuilder ();
45 static class Program
{
47 static readonly TimeSpan _timeout
= TimeSpan
.FromHours (6);
49 static string FilterInvalidXmlChars (string text
) {
50 return Regex
.Replace (text
, @"[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]", string.Empty
);
55 var depDir
= Path
.Combine ("..", "external", "benchmarker");
56 var benchDir
= Path
.Combine (depDir
, "benchmarks");
57 var testDir
= Path
.Combine (depDir
, "tests");
59 var benchmarks
= Directory
.EnumerateFiles (benchDir
, "*.benchmark")
60 .Select (Benchmark
.Load
)
61 .Where (b
=> !b
.OnlyExplicit
&& b
.ClientCommandLine
== null)
62 .OrderBy (b
=> b
.Name
)
65 var monoPath
= Path
.GetFullPath (Path
.Combine ("..", "..", "runtime", "mono-wrapper"));
66 var classDir
= Path
.GetFullPath (Path
.Combine ("..", "..", "mcs", "class", "lib", "net_4_x"));
68 var rand
= new Random ();
69 var cpus
= Environment
.ProcessorCount
;
71 var results
= new List
<TestResult
> (benchmarks
.Length
);
73 var sw
= Stopwatch
.StartNew ();
75 for (var i
= 0; i
< benchmarks
.Length
; i
++) {
76 var bench
= benchmarks
[i
];
78 var sampleFreq
= rand
.Next (0, 1001);
79 var sampleMode
= rand
.Next (0, 2) == 1 ? "real" : "process";
80 var maxSamples
= rand
.Next (0, cpus
* 2000 + 1);
81 var heapShotFreq
= rand
.Next (0, 11);
82 var maxFrames
= rand
.Next (0, 33);
83 var allocMode
= rand
.Next (0, 2) == 1 ? "alloc" : "noalloc";
85 var profOptions
= $"sample=cycles/{sampleFreq},sampling-{sampleMode},maxsamples={maxSamples},heapshot={heapShotFreq}gc,maxframes={maxFrames},{allocMode},output=/dev/null";
87 var info
= new ProcessStartInfo
{
88 UseShellExecute
= false,
89 WorkingDirectory
= Path
.Combine (testDir
, bench
.TestDirectory
),
91 Arguments
= $"--debug --profile=log:{profOptions} " + string.Join (" ", bench
.CommandLine
),
92 RedirectStandardOutput
= true,
93 RedirectStandardError
= true,
96 info
.EnvironmentVariables
.Clear ();
97 info
.EnvironmentVariables
.Add ("MONO_PATH", classDir
);
99 var progress
= $"({i + 1}/{benchmarks.Length})";
101 Console
.ForegroundColor
= ConsoleColor
.Blue
;
102 Console
.WriteLine ($"[{sw.Elapsed.ToString ("G")}] {progress} Running {bench.Name} with profiler options: {profOptions}");
103 Console
.ResetColor ();
105 var result
= new TestResult
{
110 using (var proc
= new Process ()) {
111 proc
.StartInfo
= info
;
113 proc
.OutputDataReceived
+= (sender
, args
) => {
114 if (args
.Data
!= null)
115 result
.StandardOutput
.AppendLine (args
.Data
);
118 proc
.ErrorDataReceived
+= (sender
, args
) => {
119 if (args
.Data
!= null)
120 result
.StandardError
.AppendLine (args
.Data
);
123 result
.Stopwatch
.Start ();
127 proc
.BeginOutputReadLine ();
128 proc
.BeginErrorReadLine ();
130 if (!proc
.WaitForExit ((int) _timeout
.TotalMilliseconds
)) {
131 // Force a thread dump.
132 Syscall
.kill (proc
.Id
, Signum
.SIGQUIT
);
137 } catch (Exception
) {
140 result
.ExitCode
= proc
.ExitCode
;
142 result
.Stopwatch
.Stop ();
145 var resultStr
= result
.ExitCode
== null ? "timed out" : $"exited with code: {result.ExitCode}";
147 Console
.ForegroundColor
= result
.ExitCode
!= 0 ? ConsoleColor
.Red
: ConsoleColor
.Green
;
148 Console
.WriteLine ($"[{sw.Elapsed.ToString ("G")}] {progress} {bench.Name} took {result.Stopwatch.Elapsed.ToString ("G")} and {resultStr}");
149 Console
.ResetColor ();
151 if (result
.ExitCode
!= 0) {
152 Console
.ForegroundColor
= ConsoleColor
.Red
;
153 Console
.WriteLine ("===== stdout =====");
154 Console
.ResetColor ();
156 Console
.WriteLine (result
.StandardOutput
.ToString ());
158 Console
.ForegroundColor
= ConsoleColor
.Red
;
159 Console
.WriteLine ("===== stderr =====");
160 Console
.ResetColor ();
162 Console
.WriteLine (result
.StandardError
.ToString ());
165 results
.Add (result
);
170 var successes
= results
.Count (r
=> r
.ExitCode
== 0);
171 var failures
= results
.Count (r
=> r
.ExitCode
!= null && r
.ExitCode
!= 0);
172 var timeouts
= results
.Count (r
=> r
.ExitCode
== null);
174 var settings
= new XmlWriterSettings
{
175 NewLineOnAttributes
= true,
179 using (var writer
= XmlWriter
.Create ("TestResult-profiler-stress.xml", settings
)) {
180 writer
.WriteStartDocument ();
181 writer
.WriteComment ("This file represents the results of running a test suite");
183 writer
.WriteStartElement ("test-results");
184 writer
.WriteAttributeString ("name", "profiler-stress-tests.dummy");
185 writer
.WriteAttributeString ("total", results
.Count
.ToString ());
186 writer
.WriteAttributeString ("failures", failures
.ToString ());
187 writer
.WriteAttributeString ("not-run", "0");
188 writer
.WriteAttributeString ("date", DateTime
.Now
.ToString ("yyyy-MM-dd"));
189 writer
.WriteAttributeString ("time", DateTime
.Now
.ToString ("HH:mm:ss"));
191 writer
.WriteStartElement ("environment");
192 writer
.WriteAttributeString ("nunit-version", "2.4.8.0");
193 writer
.WriteAttributeString ("clr-version", Environment
.Version
.ToString ());
194 writer
.WriteAttributeString ("os-version", Environment
.OSVersion
.ToString ());
195 writer
.WriteAttributeString ("platform", Environment
.OSVersion
.Platform
.ToString ());
196 writer
.WriteAttributeString ("cwd", Environment
.CurrentDirectory
);
197 writer
.WriteAttributeString ("machine-name", Environment
.MachineName
);
198 writer
.WriteAttributeString ("user", Environment
.UserName
);
199 writer
.WriteAttributeString ("user-domain", Environment
.UserDomainName
);
200 writer
.WriteEndElement ();
202 writer
.WriteStartElement ("culture-info");
203 writer
.WriteAttributeString ("current-culture", CultureInfo
.CurrentCulture
.Name
);
204 writer
.WriteAttributeString ("current-uiculture", CultureInfo
.CurrentUICulture
.Name
);
205 writer
.WriteEndElement ();
207 writer
.WriteStartElement ("test-suite");
208 writer
.WriteAttributeString ("name", "profiler-stress-tests.dummy");
209 writer
.WriteAttributeString ("success", (failures
+ timeouts
== 0).ToString ());
210 writer
.WriteAttributeString ("time", ((int) sw
.Elapsed
.TotalSeconds
).ToString ());
211 writer
.WriteAttributeString ("asserts", (failures
+ timeouts
).ToString ());
212 writer
.WriteStartElement ("results");
214 writer
.WriteStartElement ("test-suite");
215 writer
.WriteAttributeString ("name", "MonoTests");
216 writer
.WriteAttributeString ("success", (failures
+ timeouts
== 0).ToString ());
217 writer
.WriteAttributeString ("time", ((int) sw
.Elapsed
.TotalSeconds
).ToString ());
218 writer
.WriteAttributeString ("asserts", (failures
+ timeouts
).ToString ());
219 writer
.WriteStartElement ("results");
221 writer
.WriteStartElement ("test-suite");
222 writer
.WriteAttributeString ("name", "profiler-stress");
223 writer
.WriteAttributeString ("success", (failures
+ timeouts
== 0).ToString ());
224 writer
.WriteAttributeString ("time", ((int) sw
.Elapsed
.TotalSeconds
).ToString ());
225 writer
.WriteAttributeString ("asserts", (failures
+ timeouts
).ToString ());
226 writer
.WriteStartElement ("results");
228 foreach (var result
in results
) {
229 var timeoutStr
= result
.ExitCode
== null ? "_timeout" : string.Empty
;
231 writer
.WriteStartElement ("test-case");
232 writer
.WriteAttributeString ("name", $"MonoTests.profiler-stress.{result.Benchmark.Name}{timeoutStr}");
233 writer
.WriteAttributeString ("executed", "True");
234 writer
.WriteAttributeString ("success", (result
.ExitCode
== 0).ToString ());
235 writer
.WriteAttributeString ("time", ((int) result
.Stopwatch
.Elapsed
.TotalSeconds
).ToString ());
236 writer
.WriteAttributeString ("asserts", result
.ExitCode
== 0 ? "0" : "1");
238 if (result
.ExitCode
!= 0) {
239 writer
.WriteStartElement ("failure");
241 writer
.WriteStartElement ("message");
242 writer
.WriteCData (FilterInvalidXmlChars (result
.StandardOutput
.ToString ()));
243 writer
.WriteEndElement ();
245 writer
.WriteStartElement ("stack-trace");
246 writer
.WriteCData (FilterInvalidXmlChars (result
.StandardError
.ToString ()));
247 writer
.WriteEndElement ();
249 writer
.WriteEndElement ();
252 writer
.WriteEndElement ();
255 writer
.WriteEndElement ();
256 writer
.WriteEndElement ();
258 writer
.WriteEndElement ();
259 writer
.WriteEndElement ();
261 writer
.WriteEndElement ();
262 writer
.WriteEndElement ();
264 writer
.WriteEndElement ();
266 writer
.WriteEndDocument ();
269 var failureStr
= failures
+ timeouts
!= 0 ? $" ({failures} failures, {timeouts} timeouts)" : string.Empty
;
271 Console
.ForegroundColor
= failures
+ timeouts
!= 0 ? ConsoleColor
.Red
: ConsoleColor
.Green
;
272 Console
.WriteLine ($"[{sw.Elapsed.ToString ("G")}] Finished with {successes}/{results.Count} passing tests{failureStr}");
273 Console
.ResetColor ();
275 return failures
+ timeouts
;