Intrinsicify SpanHelpers.IndexOf(char) (dotnet/coreclr#22505)
[mono-project.git] / acceptance-tests / profiler-stress / runner.cs
bloba0815af5088ac13ae6c5cc96d688ed45fa36f0f6
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Globalization;
5 using System.IO;
6 using System.Linq;
7 using System.Text;
8 using System.Text.RegularExpressions;
9 using System.Threading;
10 using System.Xml;
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 string StandardOutput { get; set; }
42 public string StandardError { get; set; }
45 static class Program {
47 static readonly string[] _options = new [] {
48 "exception",
49 "monitor",
50 "gc",
51 "gcalloc",
52 "gcmove",
53 "gcroot",
54 "gchandle",
55 "finalization",
56 "counter",
57 "jit",
60 static readonly TimeSpan _timeout = TimeSpan.FromHours (6);
62 const RegexOptions _regexOptions = RegexOptions.Compiled | RegexOptions.Singleline;
64 static readonly Dictionary<string, Predicate<Benchmark>> _filters = new Dictionary<string, Predicate<Benchmark>> {
65 { "ironjs-v8", FilterNotOnArm },
66 { "msbiology", FilterNever },
69 static bool FilterNever (Benchmark benchmark)
71 return false;
74 static bool FilterNotOnArm (Benchmark benchmark)
76 #if ARCH_arm || ARCH_arm64
77 return false;
78 #else
79 return true;
80 #endif
83 static bool IsSupported (Benchmark benchmark)
85 return _filters.TryGetValue (benchmark.Name, out var filter) ? filter (benchmark) : true;
88 static string ReplaceInvalidXmlChars (string text) {
89 return Regex.Replace (text, @"[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]", string.Empty);
92 static int Main ()
94 var regex = new Regex (".*", _regexOptions);
96 if (Environment.GetEnvironmentVariable ("MONO_PROFILER_STRESS_REGEX") is string envRegex)
97 regex = new Regex (envRegex, _regexOptions);
99 var depDir = Path.Combine ("..", "external", "benchmarker");
100 var benchDir = Path.Combine (depDir, "benchmarks");
101 var testDir = Path.Combine (depDir, "tests");
103 var benchmarks = Directory.EnumerateFiles (benchDir, "*.benchmark")
104 .Select (Benchmark.Load)
105 .Where (b => !b.OnlyExplicit && b.ClientCommandLine == null && IsSupported (b) && regex.IsMatch (b.Name))
106 .OrderBy (b => b.Name)
107 .ToArray ();
109 var monoPath = Path.GetFullPath (Path.Combine ("..", "..", "runtime", "mono-wrapper"));
110 var classDir = Path.GetFullPath (Path.Combine ("..", "..", "mcs", "class", "lib", "net_4_x"));
112 var seed = Environment.TickCount;
114 if (Environment.GetEnvironmentVariable ("MONO_PROFILER_STRESS_SEED") is string envSeed)
115 seed = int.Parse (envSeed);
117 var timeout = (int) _timeout.TotalMilliseconds;
119 if (Environment.GetEnvironmentVariable ("MONO_PROFILER_STRESS_TIMEOUT") is string envTimeout)
120 timeout = (int) TimeSpan.Parse (envTimeout).TotalMilliseconds;
122 string options = null;
124 if (Environment.GetEnvironmentVariable ("MONO_PROFILER_STRESS_OPTIONS") is string envOptions)
125 options = envOptions;
127 var suspend = false;
129 if (Environment.GetEnvironmentVariable ("MONO_PROFILER_STRESS_SUSPEND") is string envSuspend)
130 suspend = bool.Parse (envSuspend);
132 var rand = new Random (seed);
133 var cpus = Environment.ProcessorCount;
135 var sw = Stopwatch.StartNew ();
137 Console.ForegroundColor = ConsoleColor.Magenta;
138 Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] Starting test session with seed: {seed}");
139 Console.ResetColor ();
141 var results = new List<TestResult> (benchmarks.Length);
143 for (var i = 0; i < benchmarks.Length; i++) {
144 var bench = benchmarks [i];
146 var sampleFreq = rand.Next (-1000, 1001);
147 var sampleMode = rand.Next (0, 2) == 1 ? "-real" : string.Empty;
148 var maxSamples = rand.Next (0, cpus * 2000 + 1);
149 var heapShotFreq = rand.Next (-10, 11);
150 var maxFrames = rand.Next (0, 33);
151 var flags = _options.ToDictionary (x => x, _ => rand.Next (0, 2) == 1)
152 .Select (x => (x.Value ? string.Empty : "no") + x.Key)
153 .ToArray ();
155 var profOptions = "nodefaults,output=/dev/null,";
157 if (options == null) {
158 profOptions += $"maxframes={maxFrames},";
160 if (sampleFreq > 0)
161 profOptions += $"sample{sampleMode}={sampleFreq},maxsamples={maxSamples},";
163 if (heapShotFreq > 0)
164 profOptions += $"heapshot={heapShotFreq}gc,";
166 profOptions += string.Join (",", flags);
167 } else
168 profOptions += options;
170 var info = new ProcessStartInfo {
171 UseShellExecute = false,
172 WorkingDirectory = Path.Combine (testDir, bench.TestDirectory),
173 FileName = monoPath,
174 Arguments = $"--debug --profile=log:{profOptions} " + string.Join (" ", bench.CommandLine),
175 RedirectStandardOutput = true,
176 RedirectStandardError = true,
179 info.EnvironmentVariables.Clear ();
180 info.EnvironmentVariables.Add ("MONO_PATH", classDir);
182 if (suspend)
183 info.EnvironmentVariables.Add ("MONO_DEBUG", "suspend-on-native-crash,suspend-on-unhandled");
185 var progress = $"({i + 1}/{benchmarks.Length})";
187 Console.ForegroundColor = ConsoleColor.Blue;
188 Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] {progress} Running {bench.Name} with profiler options: {profOptions}");
189 Console.ResetColor ();
191 var result = new TestResult {
192 Benchmark = bench,
193 StartInfo = info,
196 using (var proc = new Process ()) {
197 proc.StartInfo = info;
199 var stdout = new StringBuilder ();
200 var stderr = new StringBuilder ();
202 proc.OutputDataReceived += (sender, args) => {
203 if (args.Data != null)
204 lock (result)
205 stdout.AppendLine (args.Data);
208 proc.ErrorDataReceived += (sender, args) => {
209 if (args.Data != null)
210 lock (result)
211 stderr.AppendLine (args.Data);
214 result.Stopwatch.Start ();
216 proc.Start ();
218 proc.BeginOutputReadLine ();
219 proc.BeginErrorReadLine ();
221 if (!proc.WaitForExit (timeout)) {
222 // Force a thread dump.
223 Syscall.kill (proc.Id, Signum.SIGQUIT);
224 Thread.Sleep (1000);
226 try {
227 proc.Kill ();
228 } catch (Exception) {
230 } else
231 result.ExitCode = proc.ExitCode;
233 result.Stopwatch.Stop ();
235 lock (result) {
236 result.StandardOutput = stdout.ToString ();
237 result.StandardError = stderr.ToString ();
241 var resultStr = result.ExitCode == null ? "timed out" : $"exited with code: {result.ExitCode}";
243 Console.ForegroundColor = result.ExitCode != 0 ? ConsoleColor.Red : ConsoleColor.Green;
244 Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] {progress} {bench.Name} took {result.Stopwatch.Elapsed.ToString ("G")} and {resultStr}");
245 Console.ResetColor ();
247 if (result.ExitCode != 0) {
248 Console.ForegroundColor = ConsoleColor.Red;
249 Console.WriteLine ("===== stdout =====");
250 Console.ResetColor ();
252 Console.WriteLine (result.StandardOutput);
254 Console.ForegroundColor = ConsoleColor.Red;
255 Console.WriteLine ("===== stderr =====");
256 Console.ResetColor ();
258 Console.WriteLine (result.StandardError);
261 results.Add (result);
264 sw.Stop ();
266 var successes = results.Count (r => r.ExitCode == 0);
267 var failures = results.Count (r => r.ExitCode != null && r.ExitCode != 0);
268 var timeouts = results.Count (r => r.ExitCode == null);
270 var settings = new XmlWriterSettings {
271 NewLineOnAttributes = true,
272 Indent = true,
275 using (var writer = XmlWriter.Create ("TestResult-profiler-stress.xml", settings)) {
276 writer.WriteStartDocument ();
277 writer.WriteComment ($"This file represents the results of running a test suite (seed: {seed})");
279 writer.WriteStartElement ("test-results");
280 writer.WriteAttributeString ("name", "profiler-stress-tests.dummy");
281 writer.WriteAttributeString ("total", results.Count.ToString ());
282 writer.WriteAttributeString ("failures", failures.ToString ());
283 writer.WriteAttributeString ("not-run", "0");
284 writer.WriteAttributeString ("date", DateTime.Now.ToString ("yyyy-MM-dd"));
285 writer.WriteAttributeString ("time", DateTime.Now.ToString ("HH:mm:ss"));
287 writer.WriteStartElement ("environment");
288 writer.WriteAttributeString ("nunit-version", "2.4.8.0");
289 writer.WriteAttributeString ("clr-version", Environment.Version.ToString ());
290 writer.WriteAttributeString ("os-version", Environment.OSVersion.ToString ());
291 writer.WriteAttributeString ("platform", Environment.OSVersion.Platform.ToString ());
292 writer.WriteAttributeString ("cwd", Environment.CurrentDirectory);
293 writer.WriteAttributeString ("machine-name", Environment.MachineName);
294 writer.WriteAttributeString ("user", Environment.UserName);
295 writer.WriteAttributeString ("user-domain", Environment.UserDomainName);
296 writer.WriteEndElement ();
298 writer.WriteStartElement ("culture-info");
299 writer.WriteAttributeString ("current-culture", CultureInfo.CurrentCulture.Name);
300 writer.WriteAttributeString ("current-uiculture", CultureInfo.CurrentUICulture.Name);
301 writer.WriteEndElement ();
303 writer.WriteStartElement ("test-suite");
304 writer.WriteAttributeString ("name", "profiler-stress-tests.dummy");
305 writer.WriteAttributeString ("success", (failures + timeouts == 0).ToString ());
306 writer.WriteAttributeString ("time", ((int) sw.Elapsed.TotalSeconds).ToString ());
307 writer.WriteAttributeString ("asserts", (failures + timeouts).ToString ());
308 writer.WriteStartElement ("results");
310 writer.WriteStartElement ("test-suite");
311 writer.WriteAttributeString ("name", "MonoTests");
312 writer.WriteAttributeString ("success", (failures + timeouts == 0).ToString ());
313 writer.WriteAttributeString ("time", ((int) sw.Elapsed.TotalSeconds).ToString ());
314 writer.WriteAttributeString ("asserts", (failures + timeouts).ToString ());
315 writer.WriteStartElement ("results");
317 writer.WriteStartElement ("test-suite");
318 writer.WriteAttributeString ("name", "profiler-stress");
319 writer.WriteAttributeString ("success", (failures + timeouts == 0).ToString ());
320 writer.WriteAttributeString ("time", ((int) sw.Elapsed.TotalSeconds).ToString ());
321 writer.WriteAttributeString ("asserts", (failures + timeouts).ToString ());
322 writer.WriteStartElement ("results");
324 foreach (var result in results) {
325 var timeoutStr = result.ExitCode == null ? "_timeout" : string.Empty;
327 writer.WriteStartElement ("test-case");
328 writer.WriteAttributeString ("name", $"MonoTests.profiler-stress.{result.Benchmark.Name}{timeoutStr}");
329 writer.WriteAttributeString ("executed", "True");
330 writer.WriteAttributeString ("success", (result.ExitCode == 0).ToString ());
331 writer.WriteAttributeString ("time", ((int) result.Stopwatch.Elapsed.TotalSeconds).ToString ());
332 writer.WriteAttributeString ("asserts", result.ExitCode == 0 ? "0" : "1");
334 if (result.ExitCode != 0) {
335 writer.WriteStartElement ("failure");
337 writer.WriteStartElement ("message");
338 writer.WriteCData (ReplaceInvalidXmlChars (result.StandardOutput));
339 writer.WriteEndElement ();
341 writer.WriteStartElement ("stack-trace");
342 writer.WriteCData (ReplaceInvalidXmlChars (result.StandardError));
343 writer.WriteEndElement ();
345 writer.WriteEndElement ();
348 writer.WriteEndElement ();
351 writer.WriteEndElement ();
352 writer.WriteEndElement ();
354 writer.WriteEndElement ();
355 writer.WriteEndElement ();
357 writer.WriteEndElement ();
358 writer.WriteEndElement ();
360 writer.WriteEndElement ();
362 writer.WriteEndDocument ();
365 var failureStr = failures + timeouts != 0 ? $" ({failures} failures, {timeouts} timeouts)" : string.Empty;
367 Console.ForegroundColor = failures + timeouts != 0 ? ConsoleColor.Red : ConsoleColor.Green;
368 Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] Finished with {successes}/{results.Count} passing tests{failureStr}");
369 Console.ResetColor ();
371 return failures + timeouts;