From f6b0056c819b07ae053d3112299ade0990144d00 Mon Sep 17 00:00:00 2001 From: Ivan Baravy Date: Tue, 25 Oct 2016 20:32:15 +0400 Subject: [PATCH] Add more lldbplugin/libsosplugin tests (#5606) * Add more libsosplugin tests, update framework + Introduce test.assert* functions in testutils.py + Print test results as a table with failed assertions highlighted + PEP8! * Add more lldbplugin/libsosplugin tests --- src/ToolBox/SOS/tests/OnCrash.do | 2 - src/ToolBox/SOS/tests/README.md | 43 ++- src/ToolBox/SOS/tests/Test.cs | 94 ++++++ src/ToolBox/SOS/tests/dumpil.py | 26 -- src/ToolBox/SOS/tests/dumpmodule.py | 26 -- src/ToolBox/SOS/tests/runprocess.py | 34 --- src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py | 62 ++++ src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py | 62 ++++ src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py | 45 +++ .../SOS/tests/t_cmd_bpmd_module_function.py | 43 +++ .../tests/t_cmd_bpmd_module_function_iloffset.py | 43 +++ .../t_cmd_bpmd_nofuturemodule_module_function.py | 48 +++ src/ToolBox/SOS/tests/t_cmd_clrstack.py | 36 +++ src/ToolBox/SOS/tests/t_cmd_clrthreads.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_clru.py | 46 +++ src/ToolBox/SOS/tests/t_cmd_dso.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_dumpclass.py | 68 +++++ src/ToolBox/SOS/tests/t_cmd_dumpheap.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_dumpil.py | 38 +++ src/ToolBox/SOS/tests/t_cmd_dumplog.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_dumpmd.py | 46 +++ src/ToolBox/SOS/tests/t_cmd_dumpmodule.py | 46 +++ src/ToolBox/SOS/tests/t_cmd_dumpmt.py | 68 +++++ src/ToolBox/SOS/tests/t_cmd_dumpobj.py | 69 +++++ src/ToolBox/SOS/tests/t_cmd_dumpstack.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_eeheap.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_eestack.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_gcroot.py | 61 ++++ src/ToolBox/SOS/tests/t_cmd_histclear.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_histinit.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_histobj.py | 61 ++++ src/ToolBox/SOS/tests/t_cmd_histobjfind.py | 61 ++++ src/ToolBox/SOS/tests/t_cmd_histroot.py | 61 ++++ src/ToolBox/SOS/tests/t_cmd_ip2md.py | 53 ++++ src/ToolBox/SOS/tests/t_cmd_name2ee.py | 46 +++ src/ToolBox/SOS/tests/t_cmd_pe.py | 28 ++ src/ToolBox/SOS/tests/t_cmd_sos.py | 31 ++ src/ToolBox/SOS/tests/t_cmd_soshelp.py | 31 ++ src/ToolBox/SOS/tests/test_libsosplugin.py | 338 ++++++++++++++++----- src/ToolBox/SOS/tests/testutils.py | 234 +++++++++++--- 40 files changed, 2022 insertions(+), 207 deletions(-) delete mode 100644 src/ToolBox/SOS/tests/OnCrash.do create mode 100644 src/ToolBox/SOS/tests/Test.cs delete mode 100644 src/ToolBox/SOS/tests/dumpil.py delete mode 100644 src/ToolBox/SOS/tests/dumpmodule.py delete mode 100644 src/ToolBox/SOS/tests/runprocess.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_clrstack.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_clrthreads.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_clru.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dso.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpclass.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpheap.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpil.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumplog.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpmd.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpmodule.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpmt.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpobj.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_dumpstack.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_eeheap.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_eestack.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_gcroot.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_histclear.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_histinit.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_histobj.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_histobjfind.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_histroot.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_ip2md.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_name2ee.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_pe.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_sos.py create mode 100644 src/ToolBox/SOS/tests/t_cmd_soshelp.py diff --git a/src/ToolBox/SOS/tests/OnCrash.do b/src/ToolBox/SOS/tests/OnCrash.do deleted file mode 100644 index b1da861..0000000 --- a/src/ToolBox/SOS/tests/OnCrash.do +++ /dev/null @@ -1,2 +0,0 @@ -script open("/tmp/flag_fail", "a").close() -q diff --git a/src/ToolBox/SOS/tests/README.md b/src/ToolBox/SOS/tests/README.md index ed4c8d0..1b9c1ea 100644 --- a/src/ToolBox/SOS/tests/README.md +++ b/src/ToolBox/SOS/tests/README.md @@ -1,18 +1,28 @@ Testing libsosplugin ===================================== -**Test assembly** -The test asembly file must follow two rules: -1. the test class must be named `Program` and -2. it must have a static `Main` method. - -**Running tests** -Make sure that python's lldb module is accessible. To run the tests, use the following command: -`python test_libsosplugin.py --clr-args="/path/to/corerun [corerun_options] /path/to/test_assembly.exe"` -`--clr-args` is a command that would normally be used to launch `corerun` -This will run the test suite on the specified assembly. - -**Writing tests** +**Test assembly** +Compile test assembly file using any C# compiler you have, for example: +- `gmcs test.cs` +- `corerun csc.exe /nologo /r:System.Private.CoreLib.dll test.cs` + + +**Running tests** +Make sure that python's lldb module is accessible. To run the tests, use the following command: +`python2 test_libsosplugin.py --corerun=corerun --sosplugin=sosplugin --assembly=assembly --timeout=timeout` +- `lldb` is a path to `lldb` to run +- `clrdir` is a directory with `corerun` and sosplugin +- `assembly` is a compiled test assembly (e.g. Test.exe) +- `timeout` is a deadline for a single test (in seconds) +- `regex` is a regular expression matching tests to run +- `repeat` is a number of passes for each test + + + +Log files for both failed and passed tests are `*.log` and `*.log.2` for standard output and error correspondingly. + + +**Writing tests** Tests start with the `TestSosCommands` class defined in `test_libsosplugin.py`. To add a test to the suite, start with implementing a new method inside this class whose name begins with `test_`. Most new commands will require only one line of code in this method: `self.do_test("scenarioname")`. This command will launch a new `lldb` instance, which in turn will call the `runScenario` method from `scenarioname` module. `scenarioname` is the name of the python module that will be running the scenario inside `lldb` (found in `tests` folder alongside with `test_libsosplugin.py` and named `scenarioname.py`). An example of a scenario looks like this: @@ -25,9 +35,10 @@ An example of a scenario looks like this: process.Continue() return True - `runScenario` method does all the work related to running the scenario: setting breakpoints, running SOS commands and examining their output. It should return a boolean value indicating a success or a failure. + `runScenario` method does all the work related to running the scenario: setting breakpoints, running SOS commands and examining their output. It should return a boolean value indicating a success or a failure. ***Note:*** `testutils.py` defines some useful commands that can be reused in many scenarios. -**Useful links** -[Python scripting in LLDB](http://lldb.llvm.org/python-reference.html) -[Python unittest framework](https://docs.python.org/2.7/library/unittest.html) \ No newline at end of file + +**Useful links** +[Python scripting in LLDB](http://lldb.llvm.org/python-reference.html) +[Python unittest framework](https://docs.python.org/2.7/library/unittest.html) diff --git a/src/ToolBox/SOS/tests/Test.cs b/src/ToolBox/SOS/tests/Test.cs new file mode 100644 index 0000000..e4ef76b --- /dev/null +++ b/src/ToolBox/SOS/tests/Test.cs @@ -0,0 +1,94 @@ +using System; + +class Test +{ + static void LikelyInlined() + { + Console.WriteLine("I would like to be inlined"); + } + + static void UnlikelyInlined() + { + Console.Write("I"); + Console.Write(" "); + Console.Write("w"); + Console.Write("o"); + Console.Write("u"); + Console.Write("l"); + Console.Write("d"); + Console.Write(" "); + Console.Write("n"); + Console.Write("o"); + Console.Write("t"); + Console.Write(" "); + Console.Write("l"); + Console.Write("i"); + Console.Write("k"); + Console.Write("e"); + Console.Write(" "); + Console.Write("t"); + Console.Write("o"); + Console.Write(" "); + Console.Write("b"); + Console.Write("e"); + Console.Write(" "); + Console.Write("i"); + Console.Write("n"); + Console.Write("l"); + Console.Write("i"); + Console.Write("n"); + Console.Write("e"); + Console.Write("d"); + Console.Write("\n"); + } + + static void ClrU() + { + Console.WriteLine("test dumpclass"); + } + + static void DumpClass() + { + Console.WriteLine("test dumpclass"); + } + + static void DumpIL() + { + Console.WriteLine("test dumpil"); + } + + static void DumpMD() + { + Console.WriteLine("test dumpmd"); + } + + static void DumpModule() + { + Console.WriteLine("test dumpmodule"); + } + + static void DumpObject() + { + Console.WriteLine("test dumpobject"); + } + + static void DumpStackObjects() + { + Console.WriteLine("test dso"); + } + + static void Name2EE() + { + Console.WriteLine("test name2ee"); + } + + + static int Main() + { + DumpIL(); + LikelyInlined(); + UnlikelyInlined(); + + return 0; + } +} diff --git a/src/ToolBox/SOS/tests/dumpil.py b/src/ToolBox/SOS/tests/dumpil.py deleted file mode 100644 index 9539b61..0000000 --- a/src/ToolBox/SOS/tests/dumpil.py +++ /dev/null @@ -1,26 +0,0 @@ -import lldb -import lldbutil -import re -import os -import testutils - -def runScenario(assemblyName, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - testutils.stop_in_main(ci, process, assemblyName) - addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "MethodDesc:\s+([0-9a-fA-F]+)") - - result = False - if addr is not None: - ci.HandleCommand("dumpil " + addr, res) - if res.Succeeded(): - result = True - else: - print("DumpIL failed:") - print(res.GetOutput()) - print(res.GetError()) - - process.Continue() - return result diff --git a/src/ToolBox/SOS/tests/dumpmodule.py b/src/ToolBox/SOS/tests/dumpmodule.py deleted file mode 100644 index 04a5764..0000000 --- a/src/ToolBox/SOS/tests/dumpmodule.py +++ /dev/null @@ -1,26 +0,0 @@ -import lldb -import lldbutil -import re -import os -import testutils - -def runScenario(assemblyName, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - testutils.stop_in_main(ci, process, assemblyName) - addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "Module:\s+([0-9a-fA-F]+)") - - result = False - if addr is not None: - ci.HandleCommand("dumpmodule " + addr, res) - if res.Succeeded(): - result = True - else: - print("DumpModule failed:") - print(res.GetOutput()) - print(res.GetError()) - - process.Continue() - return result \ No newline at end of file diff --git a/src/ToolBox/SOS/tests/runprocess.py b/src/ToolBox/SOS/tests/runprocess.py deleted file mode 100644 index d9367b3..0000000 --- a/src/ToolBox/SOS/tests/runprocess.py +++ /dev/null @@ -1,34 +0,0 @@ -import os -import lldb -import sys -import importlib -from test_libsosplugin import fail_flag - -def run(assemblyName, moduleName): - global fail_flag - - print(fail_flag) - # set the flag, if it is not set - if not os.access(fail_flag, os.R_OK): - open(fail_flag, "a").close() - - - debugger = lldb.debugger - - debugger.SetAsync(False) - target = lldb.target - - debugger.HandleCommand("process launch -s") - debugger.HandleCommand("breakpoint set -n LoadLibraryExW") - - target.GetProcess().Continue() - - debugger.HandleCommand("breakpoint delete 1") - #run the scenario - print("starting scenario...") - i = importlib.import_module(moduleName) - scenarioResult = i.runScenario(os.path.basename(assemblyName), debugger, target) - - # clear the failed flag if the exit status is OK - if scenarioResult is True and target.GetProcess().GetExitStatus() == 0: - os.unlink(fail_flag) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py new file mode 100644 index 0000000..814d114 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py @@ -0,0 +1,62 @@ +import lldb +import re +import testutils as test + +# bpmd -clearall + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + # Set breakpoint + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + # Delete the first breakpoint + + ci.HandleCommand("bpmd -clear 1", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + match = re.search('Cleared', out_msg) + # Check for specific output + test.assertTrue(match) + + # Error message is empty + test.assertEqual(err_msg, '') + + process.Continue() + # Process must be exited + test.assertEqual(process.GetState(), lldb.eStateExited) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonNone) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py new file mode 100644 index 0000000..8da9239 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py @@ -0,0 +1,62 @@ +import lldb +import re +import testutils as test + +# bpmd -clearall + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + # Set breakpoint + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + # Delete all breakpoints + + ci.HandleCommand("bpmd -clearall", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + match = re.search('All pending breakpoints cleared.', out_msg) + # Check for specific output + test.assertTrue(match) + + # Error message is empty + test.assertEqual(err_msg, '') + + process.Continue() + # Process must be exited + test.assertEqual(process.GetState(), lldb.eStateExited) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonNone) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py new file mode 100644 index 0000000..dfd7543 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py @@ -0,0 +1,45 @@ +import lldb +import re +import testutils as test + +# bpmd -md + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + md_addr = test.get_methoddesc(debugger, assembly, "Test.UnlikelyInlined") + + ci.HandleCommand("bpmd -md %s" % md_addr, res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped at UnlinkelyInlined + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + # + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py new file mode 100644 index 0000000..e407ab3 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py @@ -0,0 +1,43 @@ +import lldb +import re +import testutils as test + +# bpmd + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped at UnlinkelyInlined + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py new file mode 100644 index 0000000..91fb1cd --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py @@ -0,0 +1,43 @@ +import lldb +import re +import testutils as test + +# bpmd [] + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined 66", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped at UnlinkelyInlined + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py new file mode 100644 index 0000000..64efad7 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py @@ -0,0 +1,48 @@ +import lldb +import re +import testutils as test + +# bpmd -nofuturemodule + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Process must be stopped here while libcoreclr loading. + # This test usually fails on release version of coreclr + # since we depend on 'LoadLibraryExW' symbol present. + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + ci.HandleCommand("bpmd -nofuturemodule " + assembly + " Test.Main", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) == 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be exited because of -nofuturemodule flag + test.assertEqual(process.GetState(), lldb.eStateExited) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonNone) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_clrstack.py b/src/ToolBox/SOS/tests/t_cmd_clrstack.py new file mode 100644 index 0000000..28a1a8b --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_clrstack.py @@ -0,0 +1,36 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("clrstack", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('OS Thread Id', output) + # Specific string must be in the output + test.assertTrue(match) + + match = re.search('Failed to start', output) + # Check if a fail was reported + test.assertFalse(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_clrthreads.py b/src/ToolBox/SOS/tests/t_cmd_clrthreads.py new file mode 100644 index 0000000..ff731da --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_clrthreads.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("clrthreads", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("ThreadCount:"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_clru.py b/src/ToolBox/SOS/tests/t_cmd_clru.py new file mode 100644 index 0000000..81a583e --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_clru.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + jit_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(jit_addr)) + + ci.HandleCommand("clru " + jit_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dso.py b/src/ToolBox/SOS/tests/t_cmd_dso.py new file mode 100644 index 0000000..492204d --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dso.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("SP/REG"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpclass.py b/src/ToolBox/SOS/tests/t_cmd_dumpclass.py new file mode 100644 index 0000000..6a69070 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpclass.py @@ -0,0 +1,68 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmd " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Class:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + class_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(class_addr)) + + ci.HandleCommand("dumpmd " + class_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpheap.py b/src/ToolBox/SOS/tests/t_cmd_dumpheap.py new file mode 100644 index 0000000..8546de7 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpheap.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dumpheap", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Address"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpil.py b/src/ToolBox/SOS/tests/t_cmd_dumpil.py new file mode 100644 index 0000000..295cf19 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpil.py @@ -0,0 +1,38 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + md_addr = test.get_methoddesc(debugger, assembly, "Test.DumpIL") + + ci.HandleCommand("dumpil " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + insts = res.GetOutput() + print(insts) + # Function must have some instructions + test.assertTrue(len(insts) > 0) + + match = re.search(r'IL_\w{4}:\sldstr.*test\sdumpil.*' + + r'IL_\w{4}:\scall.*System\.Console::WriteLine.*' + + r'IL_\w{4}:\sret', + insts.replace('\n', ' ')) + # Must have ldstr, call and ret instructions + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumplog.py b/src/ToolBox/SOS/tests/t_cmd_dumplog.py new file mode 100644 index 0000000..ab33b66 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumplog.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dumplog", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find(" dump "), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmd.py b/src/ToolBox/SOS/tests/t_cmd_dumpmd.py new file mode 100644 index 0000000..0340eb5 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpmd.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmd " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py b/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py new file mode 100644 index 0000000..2dd0048 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Module:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmodule " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmt.py b/src/ToolBox/SOS/tests/t_cmd_dumpmt.py new file mode 100644 index 0000000..059060b --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpmt.py @@ -0,0 +1,68 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmd " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodTable:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + mt_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(mt_addr)) + + ci.HandleCommand("dumpmt " + mt_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpobj.py b/src/ToolBox/SOS/tests/t_cmd_dumpobj.py new file mode 100644 index 0000000..93f32cd --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpobj.py @@ -0,0 +1,69 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("dumpobj " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Name:\s+\S+', output) + test.assertTrue(match) + match = re.search('MethodTable:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('EEClass:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Size:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Fields:', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpstack.py b/src/ToolBox/SOS/tests/t_cmd_dumpstack.py new file mode 100644 index 0000000..3e15f8b --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpstack.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dumpstack", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Test.Main()"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_eeheap.py b/src/ToolBox/SOS/tests/t_cmd_eeheap.py new file mode 100644 index 0000000..50d8977 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_eeheap.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("eeheap", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Loader Heap"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_eestack.py b/src/ToolBox/SOS/tests/t_cmd_eestack.py new file mode 100644 index 0000000..bc36592 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_eestack.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("eestack", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Current frame"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_gcroot.py b/src/ToolBox/SOS/tests/t_cmd_gcroot.py new file mode 100644 index 0000000..e6b727c --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_gcroot.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("gcroot " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Found', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histclear.py b/src/ToolBox/SOS/tests/t_cmd_histclear.py new file mode 100644 index 0000000..db29bd8 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histclear.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("histclear", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Completed successfully."), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histinit.py b/src/ToolBox/SOS/tests/t_cmd_histinit.py new file mode 100644 index 0000000..5119128 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histinit.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("histinit", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("STRESS LOG:"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histobj.py b/src/ToolBox/SOS/tests/t_cmd_histobj.py new file mode 100644 index 0000000..c88bdac --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histobj.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("histobj " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('GCCount', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histobjfind.py b/src/ToolBox/SOS/tests/t_cmd_histobjfind.py new file mode 100644 index 0000000..ffe5dbf --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histobjfind.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("histobjfind " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('GCCount', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histroot.py b/src/ToolBox/SOS/tests/t_cmd_histroot.py new file mode 100644 index 0000000..7b73caa --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histroot.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("histroot " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('GCCount', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_ip2md.py b/src/ToolBox/SOS/tests/t_cmd_ip2md.py new file mode 100644 index 0000000..1384c38 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_ip2md.py @@ -0,0 +1,53 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + jit_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(jit_addr)) + + ci.HandleCommand("ip2md " + jit_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("MethodDesc:"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_name2ee.py b/src/ToolBox/SOS/tests/t_cmd_name2ee.py new file mode 100644 index 0000000..b617020 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_name2ee.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Module:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Assembly:\s+\S+', output) + test.assertTrue(match) + match = re.search('Token:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('MethodDesc:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Name:\s+\S+', output) + test.assertTrue(match) + + process.Continue() + # Process must exit + test.assertEqual(process.GetState(), lldb.eStateExited) + + # Process must exit with zero code + test.assertEqual(process.GetExitStatus(), 0) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_pe.py b/src/ToolBox/SOS/tests/t_cmd_pe.py new file mode 100644 index 0000000..0a87014 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_pe.py @@ -0,0 +1,28 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_sos.py b/src/ToolBox/SOS/tests/t_cmd_sos.py new file mode 100644 index 0000000..b407491 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_sos.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("sos", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("SOS"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_soshelp.py b/src/ToolBox/SOS/tests/t_cmd_soshelp.py new file mode 100644 index 0000000..8bb51da --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_soshelp.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("soshelp", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("soshelp "), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/test_libsosplugin.py b/src/ToolBox/SOS/tests/test_libsosplugin.py index e4f59eb..e5a5906 100644 --- a/src/ToolBox/SOS/tests/test_libsosplugin.py +++ b/src/ToolBox/SOS/tests/test_libsosplugin.py @@ -1,3 +1,4 @@ +from __future__ import print_function import unittest import argparse import re @@ -5,80 +6,279 @@ import tempfile import subprocess import threading import os -import os.path import sys +import inspect + +lldb = '' +clrdir = '' +workdir = '' +corerun = '' +sosplugin = '' +assembly = '' +fail_flag = '' +fail_flag_lldb = '' +summary_file = '' +timeout = 0 +regex = '' +repeat = 0 + + +def runWithTimeout(cmd): + p = None + + def run(): + global p + p = subprocess.Popen(cmd, shell=True) + p.communicate() + + thread = threading.Thread(target=run) + thread.start() + + thread.join(timeout) + if thread.is_alive(): + with open(summary_file, 'a+') as summary: + print('Timeout!', file=summary) + p.kill() + thread.join() + -assemblyName='' -clrArgs='' -fail_flag='/tmp/fail_flag' - -# helper functions - -def prepareScenarioFile(moduleName): - global assemblyName - #create a temporary scenario file - fd, scenarioFileName = tempfile.mkstemp() - scenarioFile = open(scenarioFileName, 'w') - scenarioFile.write('script from runprocess import run\n') - scenarioFile.write('script run("'+assemblyName+'", "'+moduleName+'")\n') - scenarioFile.write('quit\n') - scenarioFile.close() - os.close(fd) - return scenarioFileName - -def runWithTimeout(cmd, timeout): - d = {'process': None} - def run(): - d['process'] = subprocess.Popen(cmd, shell=True) - d['process'].communicate() - - thread = threading.Thread(target=run) - thread.start() - - thread.join(timeout) - if thread.is_alive(): - d['process'].terminate() - thread.join() - -# Test class class TestSosCommands(unittest.TestCase): - def do_test(self, command): - global clrArgs - global fail_flag - filename = prepareScenarioFile(command) - cmd = "lldb --source "+filename+" -b -K \"OnCrash.do\" -- "+clrArgs+" > "+command+".log 2>"+command+".log.2" - runWithTimeout(cmd, 120) - self.assertFalse(os.path.isfile(fail_flag)) - os.unlink(filename) + def do_test(self, command): + open(fail_flag, 'a').close() + try: + os.unlink(fail_flag_lldb) + except: + pass + + cmd = (('%s -b ' % lldb) + + ("-k \"script open('%s', 'a').close()\" " % fail_flag_lldb) + + ("-k 'quit' ") + + ("--no-lldbinit ") + + ("-O \"plugin load %s \" " % sosplugin) + + ("-o \"script import testutils as test\" ") + + ("-o \"script test.fail_flag = '%s'\" " % fail_flag) + + ("-o \"script test.summary_file = '%s'\" " % summary_file) + + ("-o \"script test.run('%s', '%s')\" " % (assembly, command)) + + ("-o \"quit\" ") + + (" -- %s %s > %s.log 2> %s.log.2" % (corerun, assembly, + command, command))) + + runWithTimeout(cmd) + self.assertFalse(os.path.isfile(fail_flag)) + self.assertFalse(os.path.isfile(fail_flag_lldb)) + + try: + os.unlink(fail_flag) + except: + pass + try: + os.unlink(fail_flag_lldb) + except: + pass + + def t_cmd_bpmd_nofuturemodule_module_function(self): + self.do_test('t_cmd_bpmd_nofuturemodule_module_function') + + def t_cmd_bpmd_module_function(self): + self.do_test('t_cmd_bpmd_module_function') + + def t_cmd_bpmd_module_function_iloffset(self): + self.do_test('t_cmd_bpmd_module_function_iloffset') + + def t_cmd_bpmd_methoddesc(self): + self.do_test('t_cmd_bpmd_methoddesc') + + def t_cmd_bpmd_clearall(self): + self.do_test('t_cmd_bpmd_clearall') + + def t_cmd_clrstack(self): + self.do_test('t_cmd_clrstack') + + def t_cmd_clrthreads(self): + self.do_test('t_cmd_clrthreads') + + def t_cmd_clru(self): + self.do_test('t_cmd_clru') + + def t_cmd_dumpclass(self): + self.do_test('t_cmd_dumpclass') + + def t_cmd_dumpheap(self): + self.do_test('t_cmd_dumpheap') + + def t_cmd_dumpil(self): + self.do_test('t_cmd_dumpil') + + def t_cmd_dumplog(self): + self.do_test('t_cmd_dumplog') + + def t_cmd_dumpmd(self): + self.do_test('t_cmd_dumpmd') + + def t_cmd_dumpmodule(self): + self.do_test('t_cmd_dumpmodule') + + def t_cmd_dumpmt(self): + self.do_test('t_cmd_dumpmt') + + def t_cmd_dumpobj(self): + self.do_test('t_cmd_dumpobj') + + def t_cmd_dumpstack(self): + self.do_test('t_cmd_dumpstack') + + def t_cmd_dso(self): + self.do_test('t_cmd_dso') + + def t_cmd_eeheap(self): + self.do_test('t_cmd_eeheap') - def test_dumpmodule(self): - self.do_test("dumpmodule") + def t_cmd_eestack(self): + self.do_test('t_cmd_eestack') + + def t_cmd_gcroot(self): + self.do_test('t_cmd_gcroot') + + def t_cmd_ip2md(self): + self.do_test('t_cmd_ip2md') + + def t_cmd_name2ee(self): + self.do_test('t_cmd_name2ee') + + def t_cmd_pe(self): + self.do_test('t_cmd_pe') + + def t_cmd_histclear(self): + self.do_test('t_cmd_histclear') + + def t_cmd_histinit(self): + self.do_test('t_cmd_histinit') + + def t_cmd_histobj(self): + self.do_test('t_cmd_histobj') + + def t_cmd_histobjfind(self): + self.do_test('t_cmd_histobjfind') + + def t_cmd_histroot(self): + self.do_test('t_cmd_histroot') + + def t_cmd_sos(self): + self.do_test('t_cmd_sos') + + def t_cmd_soshelp(self): + self.do_test('t_cmd_soshelp') + + +def generate_report(): + report = [{'name': 'TOTAL', True: 0, False: 0, 'completed': True}] + fail_messages = [] + + if not os.path.isfile(summary_file): + print('No summary file to process!') + return + + with open(summary_file, 'r') as summary: + for line in summary: + if line.startswith('new_suite: '): + report.append({'name': line.split()[-1], True: 0, False: 0, + 'completed': False, 'timeout': False}) + elif line.startswith('True'): + report[-1][True] += 1 + elif line.startswith('False'): + report[-1][False] += 1 + elif line.startswith('Completed!'): + report[-1]['completed'] = True + elif line.startswith('Timeout!'): + report[-1]['timeout'] = True + elif line.startswith('!!! '): + fail_messages.append(line.rstrip('\n')) + + for suite in report[1:]: + report[0][True] += suite[True] + report[0][False] += suite[False] + report[0]['completed'] &= suite['completed'] + + for line in fail_messages: + print(line) + + print() + print('=' * 79) + print('{:72} {:6}'.format('Test suite', 'Result')) + print('-' * 79) + for suite in report[1:]: + if suite['timeout']: + result = 'Timeout' + elif suite[False]: + result = 'Fail' + elif not suite['completed']: + result = 'Crash' + elif suite[True]: + result = 'Success' + else: + result = 'Please, report' + print('{:68} {:>10}'.format(suite['name'], result)) + print('=' * 79) - def test_dumpil(self): - self.do_test("dumpil") - if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--clr-args', default='') - parser.add_argument('unittest_args', nargs='*') - - args = parser.parse_args() - - clrArgs = args.clr_args - print("ClrArgs: " + clrArgs) - # find assembly name among lldb arguments - assembly_regexp = re.compile("([^\s]+\.exe)") - assemblyMatch = assembly_regexp.search(clrArgs) - if assemblyMatch is not None: - assemblyName = assemblyMatch.group(1) - else: - print("Assembly not recognized") - exit(1) - - print("Assembly name: "+assemblyName) - sys.argv[1:] = args.unittest_args - suite = unittest.TestLoader().loadTestsFromTestCase(TestSosCommands) - unittest.TextTestRunner(verbosity=2).run(suite) - os.unlink(fail_flag) \ No newline at end of file + parser = argparse.ArgumentParser() + parser.add_argument('--lldb', default='lldb') + parser.add_argument('--clrdir', default='.') + parser.add_argument('--workdir', default='.') + parser.add_argument('--assembly', default='Test.exe') + parser.add_argument('--timeout', default=90) + parser.add_argument('--regex', default='t_cmd_') + parser.add_argument('--repeat', default=1) + parser.add_argument('unittest_args', nargs='*') + + args = parser.parse_args() + + lldb = args.lldb + clrdir = args.clrdir + workdir = args.workdir + assembly = args.assembly + timeout = int(args.timeout) + regex = args.regex + repeat = int(args.repeat) + print("lldb: %s" % lldb) + print("clrdir: %s" % clrdir) + print("workdir: %s" % workdir) + print("assembly: %s" % assembly) + print("timeout: %i" % timeout) + print("regex: %s" % regex) + print("repeat: %i" % repeat) + + corerun = os.path.join(clrdir, 'corerun') + sosplugin = os.path.join(clrdir, 'libsosplugin.so') + if os.name != 'posix': + print('Not implemented: corerun.exe, sosplugin.dll?') + exit(1) + + print("corerun: %s" % corerun) + print("sosplugin: %s" % sosplugin) + + fail_flag = os.path.join(workdir, 'fail_flag') + fail_flag_lldb = os.path.join(workdir, 'fail_flag.lldb') + + print("fail_flag: %s" % fail_flag) + print("fail_flag_lldb: %s" % fail_flag_lldb) + + summary_file = os.path.join(workdir, 'summary') + print("summary_file: %s" % summary_file) + + try: + os.unlink(summary_file) + except: + pass + + sys.argv[1:] = args.unittest_args + suite = unittest.TestSuite() + all_tests = inspect.getmembers(TestSosCommands, predicate=inspect.ismethod) + for (test_name, test_func) in all_tests: + if re.match(regex, test_name): + suite.addTest(TestSosCommands(test_name)) + unittest.TextTestRunner(verbosity=1).run(suite) + + generate_report() diff --git a/src/ToolBox/SOS/tests/testutils.py b/src/ToolBox/SOS/tests/testutils.py index 1ddb656..1f784b4 100644 --- a/src/ToolBox/SOS/tests/testutils.py +++ b/src/ToolBox/SOS/tests/testutils.py @@ -1,40 +1,206 @@ +from __future__ import print_function import lldb import re +import inspect +import sys +import os +import importlib + +summary_file = '' +fail_flag = '' + +failed = False + + +def assertCommon(passed, fatal): + global failed + with open(summary_file, 'a+') as summary: + print(bool(passed), file=summary) + if (not passed): + failed = True + print('!!! test failed:', file=summary) + for s in inspect.stack()[2:]: + print("!!! %s:%i" % (s[1], s[2]), file=summary) + print("!!! %s" % s[4][0], file=summary) + if re.match('\W*t_\w+\.py$', s[1]): + break + print('!!! ', file=summary) + + if fatal: + exit(1) + + +def assertTrue(x, fatal=True): + passed = bool(x) + assertCommon(passed, fatal) + + +def assertFalse(x, fatal=True): + passed = not bool(x) + assertCommon(passed, fatal) + + +def assertEqual(x, y, fatal=True): + passed = (x == y) + if not passed: + print(str(x), ' != ', str(y)) + assertCommon(passed, fatal) + + +def assertNotEqual(x, y, fatal=True): + passed = (x != y) + if not passed: + print(str(x), ' == ', str(y)) + assertCommon(passed, fatal) + def checkResult(res): - if not res.Succeeded(): - print(res.GetOutput()) - print(res.GetError()) - exit(1) + if not res.Succeeded(): + print(res.GetOutput()) + print(res.GetError()) + exit(1) + + +def is_hexnum(s): + try: + int(s, 16) + return True + except ValueError: + return False + def exec_and_find(commandInterpreter, cmd, regexp): - res = lldb.SBCommandReturnObject() - commandInterpreter.HandleCommand(cmd, res) - checkResult(res) - - expr = re.compile(regexp) - addr = None - - print(res.GetOutput()) - lines = res.GetOutput().splitlines() - for line in lines: - match = expr.match(line) - if match is not None: - addr = match.group(1) - break - - print("Found addr: " + str(addr)) - return addr - -def stop_in_main(commandInterpreter, process, assemblyName): - res = lldb.SBCommandReturnObject() - commandInterpreter.HandleCommand("bpmd " + assemblyName + " Program.Main", res) - checkResult(res) - print(res.GetOutput()) - print(res.GetError()) - res.Clear() - - - # Use Python API to continue the process. The listening thread should be - # able to receive the state changed events. - process.Continue() \ No newline at end of file + res = lldb.SBCommandReturnObject() + commandInterpreter.HandleCommand(cmd, res) + checkResult(res) + + expr = re.compile(regexp) + addr = None + + print(res.GetOutput()) + lines = res.GetOutput().splitlines() + for line in lines: + match = expr.match(line) + if match: + addr = match.group(1) + break + + print("Found addr: " + str(addr)) + return addr + + +def stop_in_main(debugger, assembly): + ci = debugger.GetCommandInterpreter() + target = debugger.GetSelectedTarget() + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + + # Process must be stopped here while libcoreclr loading. + # This test usually fails on release version of coreclr + # since we depend on 'LoadLibraryExW' symbol present. + assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + ci.HandleCommand("bpmd " + assembly + " Test.Main", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + assertTrue(len(out_msg) > 0) + + # Error message is empty + assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped here if bpmd works at all + assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + +def exit_lldb(debugger, assembly): + ci = debugger.GetCommandInterpreter() + target = debugger.GetSelectedTarget() + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + + ci.HandleCommand("breakpoint delete --force", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + # assertTrue(res.Succeeded()) + + process.Continue() + # Process must exit + assertEqual(process.GetState(), lldb.eStateExited) + + # Process must exit with zero code + assertEqual(process.GetExitStatus(), 0) + + +def get_methoddesc(debugger, assembly, funcname): + ci = debugger.GetCommandInterpreter() + target = debugger.GetSelectedTarget() + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + + ci.HandleCommand("name2ee %s %s" % (assembly, funcname), res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + assertTrue(is_hexnum(md_addr)) + + return md_addr + + +def run(assembly, module): + with open(summary_file, 'a+') as summary: + print('new_suite: %s' % module, file=summary) + + debugger = lldb.debugger + + debugger.SetAsync(False) + target = lldb.target + + debugger.HandleCommand("breakpoint set --one-shot --name coreclr_execute_assembly") + debugger.HandleCommand("process launch") + + # run the scenario + print("starting scenario...") + i = importlib.import_module(module) + scenarioResult = i.runScenario(os.path.basename(assembly), debugger, + target) + + if (target.GetProcess().GetExitStatus() == 0) and not failed: + os.unlink(fail_flag) + + with open(summary_file, 'a+') as summary: + print('Completed!', file=summary) -- 2.7.4