From 6b2eafc39db5574bde9e7cf52914c888bb4a2578 Mon Sep 17 00:00:00 2001 From: Marius Ungureanu Date: Wed, 25 Jul 2018 12:16:17 +0300 Subject: Fix up behaviour of verbose method with arguments (#9087) * Fix up behaviour of verbose method with arguments Given a method spec which looks like: `MainClass:Do(int,int,int)` Via f024e820a025aacc1cf80eb82a8aee4552c3ac11 the code would match 3 methods: `MainClass:Do(int`, `int`, `int)`, crashing the runtime. Accept the multiple method matches via `;`, rather than `,`. Now, the behaviour is changed so: `MainClass:Do(int,int,int);MainClass:Do(int,int)` Get parsed as: `MainClass:Do(int,int,int)` `MainClass:Do(int,int)` * Add tests * add test file and make tests actually output something * Smarter unit test around disassembling methods * Try single quoting environment variable value * Try using non-throwing setter --- man/mono.1 | 2 +- mono/mini/mini.c | 2 +- mono/tests/Makefile.am | 3 +- mono/tests/verbose.cs | 157 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 mono/tests/verbose.cs diff --git a/man/mono.1 b/man/mono.1 index 8f9f6439f79..7b9ae4eddf9 100644 --- a/man/mono.1 +++ b/man/mono.1 @@ -1897,7 +1897,7 @@ for example, to see managed frame names on gdb backtraces. \fBMONO_VERBOSE_METHOD\fR Enables the maximum JIT verbosity for the specified method. This is very helpfull to diagnose a miscompilation problems of a specific -method. This can be a comma-separated list of method names to +method. This can be a semicolon-separated list of method names to match. If the name is simple, this applies to any method with that name, otherwise you can use a mono method description (see the section METHOD DESCRIPTIONS). diff --git a/mono/mini/mini.c b/mono/mini/mini.c index 31cd462dcf8..e2e52fc66d4 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -3361,7 +3361,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl if (!verbose_method_inited) { char *env = g_getenv ("MONO_VERBOSE_METHOD"); if (env != NULL) - verbose_method_names = g_strsplit (env, ",", -1); + verbose_method_names = g_strsplit (env, ";", -1); verbose_method_inited = TRUE; } diff --git a/mono/tests/Makefile.am b/mono/tests/Makefile.am index dbbaa669590..bc903e4e223 100755 --- a/mono/tests/Makefile.am +++ b/mono/tests/Makefile.am @@ -687,7 +687,8 @@ TESTS_CS_SRC= \ tailcall-interface.cs \ bug-60843.cs \ nested_type_visibility.cs \ - dynamic-method-churn.cs + dynamic-method-churn.cs \ + verbose.cs # some tests fail to compile on mcs if CSC_IS_ROSLYN diff --git a/mono/tests/verbose.cs b/mono/tests/verbose.cs new file mode 100644 index 00000000000..2c609567dbf --- /dev/null +++ b/mono/tests/verbose.cs @@ -0,0 +1,157 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; + +public class MainClass +{ + class TestCase + { + public string MethodSpec; + public string[] ExpectedMethods; + } + + public static int Main (string[] args) + { + var testCase = new TestCase { + MethodSpec = "*:Method", + ExpectedMethods = new[] { + "N1.Test:Method ()", + "N2.Test:Method ()", + "N2.Test:Method (int)", + "N2.Test:Method (int,string[])", + }, + }; + if (!RunTest (testCase)) + return 1; + + testCase = new TestCase { + MethodSpec = "*:Method (int)", + ExpectedMethods = new[] { + "N2.Test:Method (int)", + }, + }; + if (!RunTest (testCase)) + return 2; + + testCase = new TestCase + { + MethodSpec = "N1.Test:Method", + ExpectedMethods = new[] { + "N1.Test:Method ()", + }, + }; + if (!RunTest (testCase)) + return 3; + + testCase = new TestCase { + MethodSpec = "Test:Method", + ExpectedMethods = new[] { + "N1.Test:Method ()", + "N2.Test:Method ()", + "N2.Test:Method (int)", + "N2.Test:Method (int,string[])", + }, + }; + if (!RunTest (testCase)) + return 4; + + testCase = new TestCase { + MethodSpec = "*:Method(int,string[])", + ExpectedMethods = new[] { + "N2.Test:Method (int,string[])", + }, + }; + if (!RunTest (testCase)) + return 5; + + testCase = new TestCase { + MethodSpec = "*:Method();N2.*:Method(int,string[])", + ExpectedMethods = new[] { + "N1.Test:Method ()", + "N2.Test:Method ()", + "N2.Test:Method (int,string[])", + }, + }; + if (!RunTest (testCase)) + return 6; + + return 0; + } + + static bool RunTest (TestCase testCase) + { + var thisProcess = typeof (MainClass).Assembly.Location; + + var process = StartCompileAssemblyProcess (thisProcess, testCase.MethodSpec); + var output = process.StandardOutput.ReadToEnd (); + process.WaitForExit (); + + var lines = output.Split (new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + foreach (var expectedMethod in testCase.ExpectedMethods) { + var sortedExpectedMethods = testCase.ExpectedMethods.OrderBy (x => x).ToArray (); + + var regex = new Regex ("converting method void (?.*)"); + var matches = regex.Matches (output); + var sortedJittedMethods = matches.Cast ().Select (x => x.Groups["methodName"]) + .SelectMany (x => x.Captures.Cast ()) + .Select (x => x.Value) + .OrderBy (x => x) + .ToArray (); + + if (sortedJittedMethods.Length != sortedExpectedMethods.Length) + return false; + + for (int i = 0; i < sortedJittedMethods.Length; ++i) { + if (sortedJittedMethods[i] != sortedExpectedMethods[i]) + return false; + } + + } + + return true; + } + + static Process StartCompileAssemblyProcess (string process, string methodSpec) + { + var psi = new ProcessStartInfo (process) { + UseShellExecute = false, + RedirectStandardOutput = true, + }; + psi.EnvironmentVariables["MONO_ENV_OPTIONS"] = "--compile-all"; + psi.EnvironmentVariables["MONO_VERBOSE_METHOD"] = methodSpec; + + return Process.Start (psi); + } +} + +namespace N1 +{ + public class Test + { + public static void Method () + { + } + } +} + +namespace N2 +{ + public class Test + { + public static void Method (int n) + { + } + + public static void Method () + { + } + + public static void Method (int n, string[] args) + { + } + } +} + + -- cgit v1.2.3