Add "dotnet-dump analyze" tests using the existing SOS tests and SOS runner (#136)
authorMike McLaughlin <mikem@microsoft.com>
Thu, 14 Mar 2019 23:06:01 +0000 (16:06 -0700)
committerGitHub <noreply@github.com>
Thu, 14 Mar 2019 23:06:01 +0000 (16:06 -0700)
Add "dotnet-dump analyze" tests using the existing SOS tests and SOS runner.

Removed "dumpstack" and "eestack" from dotnet-dump. They don't work well at all in
the hosted environment. Added "gcwhere".

Changed the SOSHost interop code from using "out" parameters in most cases to
using IntPtr/Marshal.Write* so the pointer can be checked for null.

eng/build-native.sh
src/Microsoft.Diagnostic.Repl/Console/ConsoleProvider.cs
src/SOS/SOS.Hosting/LLDBServicesWrapper.cs
src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt
src/SOS/SOS.UnitTests/SOS.cs
src/SOS/SOS.UnitTests/SOSRunner.cs
src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script
src/SOS/SOS.UnitTests/Scripts/StackTests.script
src/SOS/Strike/sosdocsunix.txt
src/SOS/lldbplugin/soscommand.cpp
src/Tools/dotnet-dump/Commands/SOSCommand.cs

index 0ece72f29d18d685e29879e6c4dccea3d7f5bdd0..e2c623474e9745cce94488d5c996ed088c9f48a2 100755 (executable)
@@ -415,6 +415,8 @@ initHostDistroRid
 # Init the target distro name
 initTargetDistroRid
 
+echo "RID: $__DistroRid"
+
 if [ "$__HostOS" == "OSX" ]; then
     export LLDB_H=$__ProjectRoot/src/SOS/lldbplugin/swift-4.0
     export LLDB_LIB=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/LLDB
@@ -442,6 +444,7 @@ if [ "$__HostOS" == "OSX" ]; then
     python --version
 fi
 
+
 # Build native components
 if [ $__Build == true ]; then
     if [[ $__CI == true ]]; then
@@ -458,14 +461,21 @@ if [ $__Build == true ]; then
     fi
 
     build_native "$__BuildArch" "$__IntermediatesDir" "$__ExtraCmakeArgs"
+fi
 
+if [[ $__Build == true || $__Test == true ]]; then
     # Copy the native SOS binaries to where these tools expect for testing
     __dotnet_sos=$__RootBinDir/bin/dotnet-sos/$__BuildType/netcoreapp2.1/publish/$__DistroRid
     __dotnet_dump=$__RootBinDir/bin/dotnet-dump/$__BuildType/netcoreapp2.1/publish/$__DistroRid
+
     mkdir -p "$__dotnet_sos"
     mkdir -p "$__dotnet_dump"
+
     cp "$__BinDir"/* "$__dotnet_sos"
+    echo "Copied SOS to $__dotnet_sos"
+
     cp "$__BinDir"/* "$__dotnet_dump"
+    echo "Copied SOS to $__dotnet_dump"
 fi
 
 # Run SOS/lldbplugin tests
index d4f9bbd2b296be2d57cc32afede145c42bd76d88..35554e60e032a9748b26d2e5a478e2e0b910b446 100644 (file)
@@ -6,6 +6,7 @@
 using System;
 using System.Collections.Generic;
 using System.CommandLine;
+using System.IO;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
@@ -26,6 +27,7 @@ namespace Microsoft.Diagnostic.Repl
         private CancellationTokenSource m_interruptExecutingCommand;
 
         private string m_clearLine;
+        private bool m_interactiveConsole;
         private bool m_refreshingLine;
         private StringBuilder m_activeLine;
 
@@ -76,12 +78,26 @@ namespace Microsoft.Diagnostic.Repl
         {
             m_lastCommandLine = null;
             m_shutdown = false;
+            m_interactiveConsole = !Console.IsInputRedirected;
             RefreshLine();
 
             // Start keyboard processing
             while (!m_shutdown) {
-                ConsoleKeyInfo keyInfo = Console.ReadKey(true);
-                await ProcessKeyInfo(keyInfo, dispatchCommand);
+                if (m_interactiveConsole)
+                {
+                    ConsoleKeyInfo keyInfo = Console.ReadKey(true);
+                    await ProcessKeyInfo(keyInfo, dispatchCommand);
+                }
+                else
+                {
+                    // The input has been redirected (i.e. testing or in script)
+                    WriteLine(OutputType.Normal, "<END_COMMAND_OUTPUT>");
+                    string line = Console.ReadLine();
+                    if (string.IsNullOrEmpty(line)) {
+                        continue;
+                    }
+                    await Dispatch(line, dispatchCommand);
+                }
             }
         }
 
@@ -173,7 +189,7 @@ namespace Microsoft.Diagnostic.Repl
         /// </summary>
         private void OnCtrlBreakKeyPress(object sender, ConsoleCancelEventArgs e)
         {
-            if (!m_shutdown) {
+            if (!m_shutdown && m_interactiveConsole) {
                 if (m_interruptExecutingCommand != null) {
                     m_interruptExecutingCommand.Cancel();
                 }
@@ -198,6 +214,10 @@ namespace Microsoft.Diagnostic.Repl
 
         private void ClearLine()
         {
+            if (!m_interactiveConsole) {
+                return;
+            }
+
             if (m_commandExecuting != 0) {
                 return;
             }
@@ -212,6 +232,10 @@ namespace Microsoft.Diagnostic.Repl
 
         private void PrintActiveLine()
         {
+            if (!m_interactiveConsole) {
+                return;
+            }
+
             if (m_shutdown) {
                 return;
             }
@@ -399,7 +423,7 @@ namespace Microsoft.Diagnostic.Repl
                     // ctrl-c interrupted the command
                     m_lastCommandLine = null;
                 }
-                catch (Exception ex) when (!(ex is NullReferenceException || ex is ArgumentNullException))
+                catch (Exception ex) when (!(ex is NullReferenceException || ex is ArgumentNullException || ex is ArgumentException))
                 {
                     WriteLine(OutputType.Error, "ERROR: {0}", ex.Message);
                     m_lastCommandLine = null;
index 74670f59cb021ae0913a6db9964a2468c9022621..a733335d58da92a651cff03e3ff24f1527f40268 100644 (file)
@@ -314,13 +314,8 @@ namespace SOS
             uint descriptionSize,
             out uint descriptionUsed)
         {
-            type = 0;
-            processId = 0;
-            threadId = 0;
-            extraInformationSize = 0;
-            extraInformationUsed = 0;
-            descriptionUsed = 0;
-            return E_NOTIMPL;
+            // Should never be called. This exception will take down the program.
+            throw new NotImplementedException("GetLastEventInformation");
         }
 
         int Disassemble(
@@ -329,11 +324,12 @@ namespace SOS
             DEBUG_DISASM flags,
             StringBuilder buffer,
             uint bufferSize,
-            out uint disassemblySize,
-            out ulong endOffset)
+            IntPtr pdisassemblySize,            // uint
+            IntPtr pendOffset)                  // ulong
         {
-            disassemblySize = 0;
-            endOffset = 0;
+            buffer.Clear();
+            WriteUInt32(pdisassemblySize, 0);
+            WriteUInt64(pendOffset, offset);
             return E_NOTIMPL;
         }
 
@@ -346,9 +342,11 @@ namespace SOS
             IntPtr frameContexts,
             uint frameContextsSize,
             uint frameContextsEntrySize,
-            IntPtr pframesFilled)
+            IntPtr pframesFilled)               // uint
         {
-            return E_NOTIMPL;
+            // Don't fail, but always return 0 native frames so "clrstack -f" still prints the managed frames
+            WriteUInt32(pframesFilled, 0);
+            return S_OK;
         }
 
         int ReadVirtual(
@@ -356,14 +354,11 @@ namespace SOS
             ulong address,
             IntPtr buffer,
             int bytesRequested,
-            IntPtr pbytesRead)
+            IntPtr pbytesRead)                  // uint
         {
             if (_dataReader.ReadMemory(address, buffer, bytesRequested, out int bytesRead))
             {
-                if (pbytesRead != IntPtr.Zero)
-                {
-                    Marshal.WriteInt32(pbytesRead, bytesRead);
-                }
+                WriteUInt32(pbytesRead, (uint)bytesRead);
                 return S_OK;
             }
             return E_FAIL;
@@ -377,9 +372,7 @@ namespace SOS
             IntPtr pbytesWritten)
         {
             // This gets used by MemoryBarrier() calls in the dac, which really shouldn't matter what we do here.
-            if (pbytesWritten != IntPtr.Zero) {
-                Marshal.WriteInt32(pbytesWritten, (int)bytesRequested);
-            }
+            WriteUInt32(pbytesWritten, bytesRequested);
             return S_OK;
         }
 
@@ -396,11 +389,12 @@ namespace SOS
             ulong offset,
             StringBuilder nameBuffer,
             uint nameBufferSize,
-            out uint nameSize,
-            out ulong displacement)
+            IntPtr pnameSize,                       // uint
+            IntPtr pdisplacement)                   // ulong
         {
-            nameSize = 0;
-            displacement = 0;
+            nameBuffer.Clear();
+            WriteUInt32(pnameSize, 0);
+            WriteUInt64(pdisplacement, 0);
             return E_NOTIMPL;
         }
 
@@ -420,7 +414,6 @@ namespace SOS
             out ulong baseAddress)
         {
             baseAddress = 0;
-
             try
             {
                 ModuleInfo module = _dataReader.EnumerateModules().ElementAt((int)index);
@@ -434,7 +427,6 @@ namespace SOS
             {
                 return E_FAIL;
             }
-
             return S_OK;
         }
 
@@ -450,7 +442,6 @@ namespace SOS
             Debug.Assert(startIndex == 0);
 
             baseAddress = 0;
-
             foreach (ModuleInfo module in _dataReader.EnumerateModules())
             {
                 if (string.Equals(Path.GetFileName(module.FileName), name))
@@ -466,11 +457,11 @@ namespace SOS
             IntPtr self,
             ulong offset,
             uint startIndex,
-            out uint index,
-            out ulong baseAddress)
+            IntPtr pindex,                          // uint
+            IntPtr pbaseAddress)                    // ulong
         {
-            index = 0;
-            baseAddress = 0;
+            WriteUInt32(pindex, 0);
+            WriteUInt64(pbaseAddress, 0);
             return E_NOTIMPL;
         }
 
@@ -480,32 +471,32 @@ namespace SOS
             ulong baseAddress,
             StringBuilder imageNameBuffer,
             uint imageNameBufferSize,
-            out uint imageNameSize,
+            IntPtr pimageNameSize,                  // uint
             StringBuilder moduleNameBuffer,
             uint ModuleNameBufferSize,
-            out uint moduleNameSize,
+            IntPtr pmoduleNameSize,                 // uint
             StringBuilder loadedImageNameBuffer,
             uint loadedImageNameBufferSize,
-            out uint loadedImageNameSize)
+            IntPtr ploadedImageNameSize)            // uint
         {
-            imageNameSize = 0;
-            moduleNameSize = 0;
-            loadedImageNameSize = 0;
+            WriteUInt32(pimageNameSize, 0);
+            WriteUInt32(pmoduleNameSize, 0);
+            WriteUInt32(ploadedImageNameSize, 0);
             return E_NOTIMPL;
         }
 
         int GetLineByOffset(
             IntPtr self,
             ulong offset,
-            out uint line,
+            IntPtr pline,                            // uint
             StringBuilder fileBuffer,
             uint fileBufferSize,
-            out uint fileSize,
-            out ulong displacement)
+            IntPtr pfileSize,                        // uint
+            IntPtr pdisplacement)                    // ulong 
         {
-            line = 0;
-            fileSize = 0;
-            displacement = 0;
+            WriteUInt32(pline, 0);
+            WriteUInt32(pfileSize, 0);
+            WriteUInt64(pdisplacement, 0);
             return E_NOTIMPL;
         }
 
@@ -514,9 +505,9 @@ namespace SOS
             string file,
             ulong[] buffer,
             uint bufferLines,
-            out uint fileLines)
+            IntPtr pfileLines)                      // uint
         {
-            fileLines = 0;
+            WriteUInt32(pfileLines, 0);
             return E_NOTIMPL;
         }
 
@@ -525,13 +516,13 @@ namespace SOS
             uint startElement,
             string file,
             uint flags,
-            out uint foundElement,
+            IntPtr pfoundElement,                   // uint
             StringBuilder buffer,
             uint bufferSize,
-            out uint foundSize)
+            IntPtr pfoundSize)                      // uint
         {
-            foundElement = 0;
-            foundSize = 0;
+            WriteUInt32(pfoundElement, 0);
+            WriteUInt32(pfoundSize, 0);
             return E_NOTIMPL;
         }
 
@@ -644,6 +635,20 @@ namespace SOS
 
         #endregion 
 
+        void WriteUInt32(IntPtr pointer, uint value)
+        {
+            if (pointer != IntPtr.Zero) {
+                Marshal.WriteInt32(pointer, unchecked((int)value));
+            }
+        }
+
+        void WriteUInt64(IntPtr pointer, ulong value)
+        {
+            if (pointer != IntPtr.Zero) {
+                Marshal.WriteInt64(pointer, unchecked((long)value));
+            }
+        }
+
         // TODO: Support other architectures
         int GetRegister(string register, out ulong value)
         {
@@ -853,8 +858,8 @@ namespace SOS
             DEBUG_DISASM flags,
             [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer,
             uint bufferSize,
-            out uint disassemblySize,
-            out ulong endOffset);
+            IntPtr pdisassemblySize,
+            IntPtr pendOffset);
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate int GetContextStackTraceDelegate(
@@ -895,8 +900,8 @@ namespace SOS
             ulong offset,
             [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder nameBuffer,
             uint nameBufferSize,
-            out uint nameSize,
-            out ulong displacement);
+            IntPtr pnameSize,
+            IntPtr pdisplacement);
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate int GetNumberModulesDelegate(
@@ -923,8 +928,8 @@ namespace SOS
             IntPtr self,
             ulong offset,
             uint startIndex,
-            out uint index,
-            out ulong baseAddress);
+            IntPtr pindex,
+            IntPtr pbaseAddress);
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate int GetModuleNamesDelegate(
@@ -933,23 +938,23 @@ namespace SOS
             ulong baseAddress,
             [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder imageNameBuffer,
             uint imageNameBufferSize,
-            out uint imageNameSize,
+            IntPtr pimageNameSize,
             [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder moduleNameBuffer,
             uint ModuleNameBufferSize,
-            out uint moduleNameSize,
+            IntPtr pmoduleNameSize,
             [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder loadedImageNameBuffer,
             uint loadedImageNameBufferSize,
-            out uint loadedImageNameSize);
+            IntPtr ploadedImageNameSize);
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate int GetLineByOffsetDelegate(
             IntPtr self,
             ulong offset,
-            out uint line,
+            IntPtr line,
             [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder fileBuffer,
             uint fileBufferSize,
-            out uint fileSize,
-            out ulong displacement);
+            IntPtr fileSize,
+            IntPtr displacement);
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate int GetSourceFileLineOffsetsDelegate(
@@ -957,7 +962,7 @@ namespace SOS
             [In, MarshalAs(UnmanagedType.LPStr)] string file,
             [Out, MarshalAs(UnmanagedType.LPArray)] ulong[] buffer,
             uint bufferLines,
-            out uint fileLines);
+            IntPtr fileLines);
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate int FindSourceFileDelegate(
@@ -965,10 +970,10 @@ namespace SOS
             uint startElement,
             [In, MarshalAs(UnmanagedType.LPStr)] string file,
             uint flags,
-            out uint foundElement,
+            IntPtr foundElement,
             [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer,
             uint bufferSize,
-            out uint foundSize);
+            IntPtr foundSize);
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate int GetCurrentProcessIdDelegate(
index a5c35194a2e762eae749502b5e97dfdf5b2832ca..d845fabbe417b276b75237d35e126a4f6a3bee1d 100644 (file)
@@ -67,6 +67,7 @@
   <Options>
     <Option Condition="$(OS) == Linux">
       <SOSPath>$(InstallDir)/libsosplugin.so</SOSPath>
+      <DotNetDumpPath>$(RootBinDir)/bin/dotnet-dump/$(TargetConfiguration)/netcoreapp2.1/publish/dotnet-dump.dll</DotNetDumpPath>
       <DebuggeeDumpOutputRootDir>$(DumpDir)/$(TestProduct)/$(RuntimeFrameworkVersion)/$(BuildProjectFramework)</DebuggeeDumpOutputRootDir>
       <DebuggeeDumpInputRootDir>$(DebuggeeDumpOutputRootDir)</DebuggeeDumpInputRootDir>
     </Option>
index 70d222f630a45c9d032ac5bedce8ff00012370a8..01cf1456277a23ca249b52bedbb5a5d7c1739a19 100644 (file)
@@ -54,18 +54,29 @@ public class SOS
             await runner.RunScript(scriptName);
         }
 
-        // Against a crash dump.
+        // Generate a crash dump.
         if (IsCreateDumpConfig(config))
         {
             await SOSRunner.CreateDump(config, Output, testName, debuggeeName, debuggeeArguments, useCreateDump);
         }
 
+        // Test against a crash dump.
         if (IsOpenDumpConfig(config))
         {
-            using (SOSRunner runner = await SOSRunner.StartDebugger(config, Output, testName, debuggeeName, debuggeeArguments, loadDump: true))
+            // With cdb (Windows) or lldb (Linux or OSX)
+            using (SOSRunner runner = await SOSRunner.StartDebugger(config, Output, testName, debuggeeName, debuggeeArguments, SOSRunner.Options.LoadDump))
             {
                 await runner.RunScript(scriptName);
             }
+
+            // With the dotnet-dump analyze tool
+            if (OS.Kind == OSKind.Linux)
+            {
+                using (SOSRunner runner = await SOSRunner.StartDebugger(config, Output, testName, debuggeeName, debuggeeArguments, SOSRunner.Options.LoadDumpWithDotNetDump))
+                {
+                    await runner.RunScript(scriptName);
+                }
+            }
         }
     }
 
index fc51b5bca0e456ca90c37bbbf83e16145791c234..ac83e210d1cbbb4d9d2622ce72331fdee51e4b24 100644 (file)
@@ -25,12 +25,21 @@ public class SOSRunner : IDisposable
     string _lastCommandOutput;
     string _previousCommandCapture;
 
+    public enum Options
+    {
+        None,
+        GenerateDump,
+        LoadDump,
+        LoadDumpWithDotNetDump,
+    }
+
     public enum NativeDebugger
     {
         Unknown,
         Cdb,
         Lldb,
-        Gdb
+        Gdb,
+        DotNetDump,
     }
 
     public const string HexValueRegEx = "[A-Fa-f0-9]+(`[A-Fa-f0-9]+)?";
@@ -71,7 +80,7 @@ public class SOSRunner : IDisposable
 
         if (!config.CreateDumpExists || !useCreateDump || config.GenerateDumpWithLLDB() || config.GenerateDumpWithGDB())
         {
-            using (SOSRunner runner = await SOSRunner.StartDebugger(config, output, testName, debuggeeName, debuggeeArguments, loadDump: false, generateDump: true))
+            using (SOSRunner runner = await SOSRunner.StartDebugger(config, output, testName, debuggeeName, debuggeeArguments, Options.GenerateDump))
             {
                 try
                 {
@@ -142,7 +151,7 @@ public class SOSRunner : IDisposable
 
                 // Run the debuggee with the createdump environment variables set to generate a coredump on unhandled exception
                 var testLogger = new TestRunner.TestLogger(outputHelper.IndentedOutput);
-                var variables = GenerateVariables(config, debuggeeConfig, generateDump: true);
+                var variables = GenerateVariables(config, debuggeeConfig, Options.GenerateDump);
                 ProcessRunner processRunner = new ProcessRunner(exePath, ReplaceVariables(variables, arguments.ToString())).
                     WithLog(testLogger).
                     WithTimeout(TimeSpan.FromMinutes(5)).
@@ -176,17 +185,16 @@ public class SOSRunner : IDisposable
     /// <param name="testName">name of test</param>
     /// <param name="debuggeeName">debuggee name</param>
     /// <param name="debuggeeArguments">optional args to pass to debuggee</param>
-    /// <param name="generateDump">if true, generate dump with native debugger</param>
-    /// <param name="loadDump">if true, load dump with native debugger</param>
+    /// <param name="options">dump options</param>
     /// <returns>sos runner instance</returns>
     public static async Task<SOSRunner> StartDebugger(TestConfiguration config, ITestOutputHelper output, string testName, string debuggeeName, 
-        string debuggeeArguments = null, bool loadDump = false, bool generateDump = false)
+        string debuggeeArguments = null, Options options = Options.None)
     {
         TestRunner.OutputHelper outputHelper = null;
         SOSRunner sosRunner = null;
 
         // Figure out which native debugger to use
-        NativeDebugger debugger = GetNativeDebuggerToUse(config, generateDump);
+        NativeDebugger debugger = GetNativeDebuggerToUse(config, options);
 
         try
         {
@@ -199,14 +207,14 @@ public class SOSRunner : IDisposable
             outputHelper.WriteLine("SOSRunner processing {0}", testName);
             outputHelper.WriteLine("{");
 
-            var variables = GenerateVariables(config, debuggeeConfig, generateDump);
+            var variables = GenerateVariables(config, debuggeeConfig, options);
             var scriptLogger = new ScriptLogger(debugger, outputHelper.IndentedOutput);
 
-            if (loadDump)
+            if (options == Options.LoadDump || options == Options.LoadDumpWithDotNetDump)
             {
                 if (!variables.TryGetValue("%DUMP_NAME%", out string dumpName) || !File.Exists(dumpName))
                 {
-                    throw new Exception($"Dump file does not exist: {dumpName ?? ""}");
+                    throw new FileNotFoundException($"Dump file does not exist: {dumpName ?? ""}");
                 }
             }
 
@@ -233,7 +241,7 @@ public class SOSRunner : IDisposable
             string debuggerPath = GetNativeDebuggerPath(debugger, config);
             if (string.IsNullOrWhiteSpace(debuggerPath) || !File.Exists(debuggerPath))
             {
-                throw new Exception($"Native debugger path not set or does not exist: {debuggerPath}");
+                throw new FileNotFoundException($"Native debugger path not set or does not exist: {debuggerPath}");
             }
 
             // Get the debugger arguments and commands to run initially
@@ -246,11 +254,11 @@ public class SOSRunner : IDisposable
                     string helperExtension = config.CDBHelperExtension();
                     if (string.IsNullOrWhiteSpace(helperExtension) || !File.Exists(helperExtension))
                     {
-                        throw new Exception($"CDB helper script path not set or does not exist: {helperExtension}");
+                        throw new ArgumentException($"CDB helper script path not set or does not exist: {helperExtension}");
                     }
                     arguments.AppendFormat(@"-c "".load {0}""", helperExtension);
 
-                    if (loadDump)
+                    if (options == Options.LoadDump)
                     {
                         arguments.Append(" -z %DUMP_NAME%");
                     }
@@ -280,12 +288,12 @@ public class SOSRunner : IDisposable
                     string lldbHelperScript = config.LLDBHelperScript();
                     if (string.IsNullOrWhiteSpace(lldbHelperScript) || !File.Exists(lldbHelperScript))
                     {
-                        throw new Exception("LLDB helper script path not set or does not exist: " + lldbHelperScript);
+                        throw new ArgumentException("LLDB helper script path not set or does not exist: " + lldbHelperScript);
                     }
                     arguments.AppendFormat(@"--no-lldbinit -o ""settings set interpreter.prompt-on-quit false"" -o ""command script import {0}"" -o ""version""", lldbHelperScript);
 
                     // Load the dump or launch the debuggee process
-                    if (loadDump)
+                    if (options == Options.LoadDump)
                     {
                         initialCommands.Add($@"target create --core ""%DUMP_NAME%"" ""{config.HostExe}""");
                     }
@@ -327,9 +335,9 @@ public class SOSRunner : IDisposable
                     }
                     break;
                 case NativeDebugger.Gdb:
-                    if (loadDump)
+                    if (options == Options.LoadDump || options == Options.LoadDumpWithDotNetDump)
                     {
-                        throw new Exception("GDB not meant for loading core dumps");
+                        throw new ArgumentException("GDB not meant for loading core dumps");
                     }
                     arguments.AppendFormat("--args {0}", debuggeeCommandLine);
 
@@ -348,6 +356,20 @@ public class SOSRunner : IDisposable
                     initialCommands.Add("set use-coredump-filter on");
                     initialCommands.Add("run");
                     break;
+
+                case NativeDebugger.DotNetDump:
+                    if (options != Options.LoadDumpWithDotNetDump)
+                    {
+                        throw new ArgumentException($"{options} not supported for dotnet-dump testing");
+                    }
+                    if (string.IsNullOrWhiteSpace(config.HostExe))
+                    {
+                        throw new ArgumentException("No HostExe in configuration");
+                    }
+                    arguments.Append(debuggerPath);
+                    arguments.Append(@" analyze %DUMP_NAME%");
+                    debuggerPath = config.HostExe;
+                    break;
             }
 
             // Create the native debugger process running
@@ -356,7 +378,7 @@ public class SOSRunner : IDisposable
                 WithTimeout(TimeSpan.FromMinutes(10));
 
             // Create the sos runner instance
-            sosRunner = new SOSRunner(debugger, config, outputHelper, variables, scriptLogger, processRunner, loadDump);
+            sosRunner = new SOSRunner(debugger, config, outputHelper, variables, scriptLogger, processRunner, options == Options.LoadDump || options == Options.LoadDumpWithDotNetDump);
 
             // Start the native debugger
             processRunner.Start();
@@ -392,7 +414,7 @@ public class SOSRunner : IDisposable
         string scriptFile = Path.Combine(_config.ScriptRootDir, scriptRelativePath);
         if (!File.Exists(scriptFile))
         {
-            throw new Exception("Script file does not exist: " + scriptFile);
+            throw new FileNotFoundException("Script file does not exist: " + scriptFile);
         }
         HashSet<string> enabledDefines = GetEnabledDefines();
         LogProcessingReproInfo(scriptFile, enabledDefines);
@@ -496,6 +518,7 @@ public class SOSRunner : IDisposable
         string sosHostRuntime = _config.SOSHostRuntime();
         string sosPath = _config.SOSPath();
         List<string> commands = new List<string>();
+
         switch (Debugger)
         {
             case NativeDebugger.Cdb:
@@ -513,20 +536,29 @@ public class SOSRunner : IDisposable
                 {
                     commands.Add($"sos SetHostRuntime {sosHostRuntime}");
                 }
-                if (_isDump)
-                {
-                    // lldb doesn't load dump with the initial thread set to one with
-                    // the exception. This SOS command looks for a thread with a managed
-                    // exception and set the current thread to it.
-                    commands.Add("clrthreads -managedexception");
-                }
+                SwitchToExceptionThread();
                 break;
             case NativeDebugger.Gdb:
                 break;
+            case NativeDebugger.DotNetDump:
+                SwitchToExceptionThread();
+                break;
             default:
                 throw new Exception($"{DebuggerToString} cannot load sos extension");
         }
         await RunCommands(commands);
+
+        // Helper function to switch to the thread with an exception
+        void SwitchToExceptionThread()
+        {
+            if (_isDump)
+            {
+                // lldb/dotnet-dump don't load dump with the initial thread set to one
+                // with the exception. This SOS command looks for a thread with a managed
+                // exception and set the current thread to it.
+                commands.Add("clrthreads -managedexception");
+            }
+        }
     }
 
     public async Task ContinueExecution()
@@ -547,10 +579,15 @@ public class SOSRunner : IDisposable
             case NativeDebugger.Gdb:
                 command = "continue";
                 break;
+            case NativeDebugger.DotNetDump:
+                break;
         }
-        if (!await RunCommand(command, addPrefix))
+        if (command != null)
         {
-            throw new Exception($"'{command}' FAILED");
+            if (!await RunCommand(command, addPrefix))
+            {
+                throw new Exception($"'{command}' FAILED");
+            }
         }
     }
 
@@ -564,8 +601,19 @@ public class SOSRunner : IDisposable
             case NativeDebugger.Lldb:
                 command = "sos " + command;
                 break;
+            case NativeDebugger.DotNetDump:
+                int index = command.IndexOf(' ');
+                if (index != -1) {
+                    // lowercase just the command name not the rest of the command line
+                    command = command.Substring(0, index).ToLowerInvariant() + command.Substring(index);
+                }
+                else {
+                    // it is only the command name
+                    command = command.ToLowerInvariant();
+                }
+                break;
             default:
-                throw new Exception(DebuggerToString + " cannot execute sos command");
+                throw new ArgumentException(DebuggerToString + " cannot execute sos command");
         }
         return await RunCommand(command);
     }
@@ -585,7 +633,7 @@ public class SOSRunner : IDisposable
     {
         if (string.IsNullOrWhiteSpace(command))
         {
-            throw new Exception("Debugger command empty or null");
+            throw new ArgumentException("Debugger command empty or null");
         }
         return await HandleCommand(command, addPrefix);
     }
@@ -602,6 +650,7 @@ public class SOSRunner : IDisposable
                     command = "q";
                     break;
                 case NativeDebugger.Lldb:
+                case NativeDebugger.DotNetDump:
                     command = "quit";
                     break;
             }
@@ -628,9 +677,9 @@ public class SOSRunner : IDisposable
         }
     }
 
-    public static string GenerateDumpFileName(TestConfiguration config, string debuggeeName, bool generateDump)
+    public static string GenerateDumpFileName(TestConfiguration config, string debuggeeName, Options options)
     {
-        string dumpRoot = generateDump ? config.DebuggeeDumpOutputRootDir() : config.DebuggeeDumpInputRootDir();
+        string dumpRoot = options == Options.GenerateDump ? config.DebuggeeDumpOutputRootDir() : config.DebuggeeDumpInputRootDir();
         if (dumpRoot != null)
         {
             return Path.Combine(dumpRoot, Path.GetFileNameWithoutExtension(debuggeeName) + ".dmp");
@@ -660,7 +709,7 @@ public class SOSRunner : IDisposable
         _outputHelper.Dispose();
     }
 
-    private static NativeDebugger GetNativeDebuggerToUse(TestConfiguration config, bool generateDump)
+    private static NativeDebugger GetNativeDebuggerToUse(TestConfiguration config, Options options)
     {
         switch (OS.Kind)
         {
@@ -669,7 +718,14 @@ public class SOSRunner : IDisposable
 
             case OSKind.Linux:
             case OSKind.OSX:
-                return generateDump ? (config.GenerateDumpWithLLDB() ? NativeDebugger.Lldb : NativeDebugger.Gdb) : NativeDebugger.Lldb;
+                switch (options) {
+                    case Options.GenerateDump: 
+                        return config.GenerateDumpWithLLDB() ? NativeDebugger.Lldb : NativeDebugger.Gdb;
+                    case Options.LoadDumpWithDotNetDump:
+                        return NativeDebugger.DotNetDump;
+                    default:
+                        return NativeDebugger.Lldb;
+                }
 
             default:
                 throw new Exception(OS.Kind.ToString() + " not supported");
@@ -688,6 +744,9 @@ public class SOSRunner : IDisposable
 
             case NativeDebugger.Gdb:
                 return config.GDBPath();
+
+            case NativeDebugger.DotNetDump:
+                return config.DotNetDumpPath();
         }
 
         return null;
@@ -820,11 +879,11 @@ public class SOSRunner : IDisposable
         return true;
     }
 
-    private static Dictionary<string, string> GenerateVariables(TestConfiguration config, DebuggeeConfiguration debuggeeConfig, bool generateDump)
+    private static Dictionary<string, string> GenerateVariables(TestConfiguration config, DebuggeeConfiguration debuggeeConfig, Options options)
     {
-        Dictionary<string, string> vars = new Dictionary<string, string>();
+        var vars = new Dictionary<string, string>();
         string debuggeeExe = debuggeeConfig.BinaryExePath;
-        string dumpFileName = GenerateDumpFileName(config, Path.GetFileNameWithoutExtension(debuggeeExe), generateDump);
+        string dumpFileName = GenerateDumpFileName(config, Path.GetFileNameWithoutExtension(debuggeeExe), options);
 
         vars.Add("%DEBUGGEE_EXE%", debuggeeExe);
         if (dumpFileName != null)
@@ -953,6 +1012,7 @@ public class SOSRunner : IDisposable
                     {
                         case NativeDebugger.Cdb:
                         case NativeDebugger.Lldb:
+                        case NativeDebugger.DotNetDump:
                             commandError = lastCommandOutput.EndsWith("<END_COMMAND_ERROR>");
                             commandEnd = commandError || lastCommandOutput.EndsWith("<END_COMMAND_OUTPUT>");
                             break;
@@ -1036,6 +1096,12 @@ public static class TestConfigurationExtensions
         return TestConfiguration.MakeCanonicalPath(gdbPath);
     }
 
+    public static string DotNetDumpPath(this TestConfiguration config)
+    {
+        string dotnetDumpPath = config.GetValue("DotNetDumpPath");
+        return TestConfiguration.MakeCanonicalPath(dotnetDumpPath);
+    }
+
     public static string SOSPath(this TestConfiguration config)
     {
         return TestConfiguration.MakeCanonicalPath(config.GetValue("SOSPath"));
index 5382109ba0fe37f4741f3ff7ef4a3f8512c8693d..becb47a119b026faf03bbd8970098090c89502de 100644 (file)
@@ -11,7 +11,9 @@ ENDIF:LIVE
 
 # Verify that ClrStack with no options works
 SOSCOMMAND:SetSymbolServer -ms
+!IFDEF:DOTNETDUMP
 SOSCOMMAND:SetHostRuntime
+ENDIF:DOTNETDUMP
 SOSCOMMAND:ClrStack
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
 VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
@@ -110,28 +112,28 @@ VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String\[\].*
 ENDIF:PROJECTK
 
-# Verify DumpStack works
+!IFDEF:DOTNETDUMP
 IFDEF:PROJECTK
+
+# Verify DumpStack works
 SOSCOMMAND:DumpStack
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
 VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
 VERIFY:(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\),\s+calling.*\s+)|(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\),\s+calling.*\s+)
-ENDIF:PROJECTK
 
 # Verify DumpStack -EE works
-IFDEF:PROJECTK
 SOSCOMMAND:DumpStack -EE
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
 VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
 VERIFY:(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\)\s+)|(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\)\s+)
-ENDIF:PROJECTK
 
 # Verify EEStack works
-IFDEF:PROJECTK
 SOSCOMMAND:EEStack
 VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
 VERIFY:(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\),\s+calling.*\s+)|(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\),\s+calling.*\s+)
+
 ENDIF:PROJECTK
+ENDIF:DOTNETDUMP
 
 # Verify that IP2MD works (uses IP from ClrStack)
 SOSCOMMAND:ClrStack
@@ -139,31 +141,31 @@ SOSCOMMAND:IP2MD <POUT>.*\s+(<HEXVAL>)\s+SymbolTestApp\.Program\.Foo4.*\s+<POUT>
 VERIFY:.*\s+Method Name:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
 VERIFY:.*\s+Source file:\s+(?i:.*[\\|/]SymbolTestApp\.cs) @ 54\s+
 
-# Verify that "u" works (depends on the IP2MD right above)
+!IFDEF:DOTNETDUMP
 IFDEF:PROJECTK
+
+# Verify that "u" works (depends on the IP2MD right above)
 SOSCOMMAND:u <POUT>\s*MethodDesc:\s+(<HEXVAL>)\s*<POUT>
 VERIFY:\s*Normal JIT generated code\s+
 VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
 VERIFY:\s+Begin\s+<HEXVAL>,\s+size\s+<HEXVAL>\s+
 VERIFY:\s+(?i:.*[\\|/]SymbolTestApp\.cs) @ 54:\s+
-ENDIF:PROJECTK
 
 # Verify that "u" with no line info works
-IFDEF:PROJECTK
 SOSCOMMAND:u -n <PREVPOUT>
 VERIFY:\s*Normal JIT generated code\s+
 VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
 VERIFY:\s+Begin\s+<HEXVAL>,\s+size\s+<HEXVAL>\s+
-ENDIF:PROJECTK
 
 # Verify that "u" with offsets info works
-IFDEF:PROJECTK
 SOSCOMMAND:u -o <PREVPOUT>
 VERIFY:\s*Normal JIT generated code\s+
 VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
 VERIFY:\s+Begin\s+<HEXVAL>,\s+size\s+<HEXVAL>\s+
 VERIFY:\s+(?i:.*[\\|/]SymbolTestApp\.cs) @ 54:\s+
+
 ENDIF:PROJECTK
+ENDIF:DOTNETDUMP
 
 # Verify that Name2EE works
 IFDEF:PROJECTK
@@ -175,7 +177,12 @@ VERIFY:\s+JITTED Code Address:\s+<HEXVAL>\s+
 ENDIF:PROJECTK
 
 # Verify that Threads (clrthreads) works
+IFDEF:DOTNETDUMP
+SOSCOMMAND:clrthreads
+ENDIF:DOTNETDUMP
+!IFDEF:DOTNETDUMP
 SOSCOMMAND:Threads
+ENDIF:DOTNETDUMP
 VERIFY:\s*ThreadCount:\s+<DECVAL>\s+
 VERIFY:\s+UnstartedThread:\s+<DECVAL>\s+
 VERIFY:\s+BackgroundThread:\s+<DECVAL>\s+
index f79d76514bcca4249f3510e6a6bff92419c65483..4805c84f058f708e24c64ba08aca56d24cef2dd5 100644 (file)
@@ -19,7 +19,9 @@ ENDIF:64BIT
 # 2) Verifying that ClrStack with managed/native mixed works
 IFDEF:PROJECTK
 SOSCOMMAND:SetSymbolServer -ms -loadsymbols
+!IFDEF:DOTNETDUMP
 SOSCOMMAND:SetHostRuntime
+ENDIF:DOTNETDUMP
 SOSCOMMAND:ClrStack -f
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
 VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
@@ -108,6 +110,8 @@ VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.InvalidOperationException\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
 ENDIF:PROJECTK
 
+!IFDEF:DOTNETDUMP
+
 # 9) Verify DumpStack works
 SOSCOMMAND:DumpStack
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
@@ -124,3 +128,5 @@ VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+\+\s*0x<HEXVAL>\s+Ne
 SOSCOMMAND:EEStack
 VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+\+\s*0x<HEXVAL>\s+NestedExceptionTest\.Program\.Main\(System\.String\[\]\)\),\s+calling.*
+
+ENDIF:DOTNETDUMP
index 3015584362c0a0889e4630c417301cd2a9b36118..e37142379e93ee55b321f99cc9ed7eb7ba7ab7ba 100644 (file)
@@ -1595,6 +1595,23 @@ possibility is eliminated, consider contacting Microsoft Product Support for
 help.
 \\
 
+COMMAND: gcwhere.
+GCWhere <object address>
+
+!GCWhere displays the location in the GC heap of the argument passed in.
+
+       0:002> !GCWhere 02800038  
+       Address  Gen Heap segment  begin    allocated size
+       02800038 2    0   02800000 02800038 0282b740  12
+
+When the argument lies in the managed heap, but is not a valid *object* address 
+the "size" is displayed as 0:
+
+       0:002> !GCWhere 0280003c
+       Address  Gen Heap segment  begin    allocated size
+       0280003c 2    0   02800000 02800038 0282b740  0
+\\
+
 COMMAND: dumplog.
 DumpLog [-addr <addressOfStressLog>] [<Filename>]
 
index cea564f8df204c28cc7acdeec510009c680552f7..a65b7ce05cf25872a1cbda2261d307ba167a069c 100644 (file)
@@ -151,6 +151,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     interpreter.AddCommand("eestack", new sosCommand("EEStack"), "Runs dumpstack on all threads in the process.");
     interpreter.AddCommand("finalizequeue", new sosCommand("FinalizeQueue"), "Displays all objects registered for finalization.");
     interpreter.AddCommand("gcroot", new sosCommand("GCRoot"), "Displays info about references (or roots) to an object at the specified address.");
+    interpreter.AddCommand("gcwhere", new sosCommand("GCWhere"), "Displays the location in the GC heap of the argument passed in.");
     interpreter.AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.");
     interpreter.AddCommand("loadsymbols", new sosCommand("SetSymbolServer", "-loadsymbols"), "Load the .NET Core native module symbols.");
     interpreter.AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.");
index 36e53b7c7c4e744eee94e6cccfd236072724289f..0c8954d14d1667a56e80dd0ba9bb336167c3fd88 100644 (file)
@@ -23,15 +23,16 @@ namespace Microsoft.Diagnostic.Tools.Dump
     [Command(Name = "dumpmodule",       AliasExpansion = "DumpModule",          Help = "Displays information about a EE module structure at the specified address.")]
     [Command(Name = "dumpmt",           AliasExpansion = "DumpMT",              Help = "Displays information about a method table at the specified address.")]
     [Command(Name = "dumpobj",          AliasExpansion = "DumpObj",             Help = "Displays info about an object at the specified address.")]
-    [Command(Name = "dumpstack",        AliasExpansion = "DumpStack",           Help = "Displays a native and managed stack trace.")]
-    [Command(Name = "dso",              AliasExpansion = "DumpStackObjects",    Help = "Displays all managed objects found within the bounds of the current stack.")]
+    [Command(Name = "dumpstackobjects", AliasExpansion = "DumpStackObjects",    Help = "Displays all managed objects found within the bounds of the current stack.")]
+    [Command(Name = "dso")]
     [Command(Name = "eeheap",           AliasExpansion = "EEHeap",              Help = "Displays info about process memory consumed by internal runtime data structures.")]
-    [Command(Name = "eestack",          AliasExpansion = "EEStack",             Help = "Runs dumpstack on all threads in the process.")]
     [Command(Name = "finalizequeue",    AliasExpansion = "FinalizeQueue",       Help = "Displays all objects registered for finalization.")]
     [Command(Name = "gcroot",           AliasExpansion = "GCRoot",              Help = "Displays info about references (or roots) to an object at the specified address.")]
+    [Command(Name = "gcwhere",          AliasExpansion = "GCWhere",             Help = "Displays the location in the GC heap of the argument passed in.")]
     [Command(Name = "ip2md",            AliasExpansion = "IP2MD",               Help = "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.")]
     [Command(Name = "name2ee",          AliasExpansion = "Name2EE",             Help = "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.")]
-    [Command(Name = "pe",               AliasExpansion = "PrintException",      Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")]
+    [Command(Name = "printexception",   AliasExpansion = "PrintException",      Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")]
+    [Command(Name = "pe")]
     [Command(Name = "syncblk",          AliasExpansion = "SyncBlk",             Help = "Displays the SyncBlock holder info.")]
     [Command(Name = "histclear",        AliasExpansion = "HistClear",           Help = "Releases any resources used by the family of Hist commands.")]
     [Command(Name = "histinit",         AliasExpansion = "HistInit",            Help = "Initializes the SOS structures from the stress log saved in the debuggee.")]