Update from coreclr repo and other misc changes. (#80)
authorMike McLaughlin <mikem@microsoft.com>
Mon, 26 Nov 2018 23:57:14 +0000 (15:57 -0800)
committerGitHub <noreply@github.com>
Mon, 26 Nov 2018 23:57:14 +0000 (15:57 -0800)
DumpDelegate, SyncBlk and DumpAsync SOS command updates.

Add embedded PDB to test helper assembly.

Change how the prebuilt compiler finds the debuggees.

Add lldbplugin project to solution.

18 files changed:
diagnostics.sln
eng/Build-Native.cmd
eng/Versions.props
eng/build-native.sh
src/Microsoft.Diagnostic.TestHelpers/BaseDebuggeeCompiler.cs
src/Microsoft.Diagnostic.TestHelpers/Microsoft.Diagnostic.TestHelpers.csproj
src/Microsoft.Diagnostic.TestHelpers/TestConfiguration.cs
src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt
src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt
src/SOS/Strike/sos.def
src/SOS/Strike/sos_unixexports.src
src/SOS/Strike/sosdocs.txt
src/SOS/Strike/sosdocsunix.txt
src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/SOS/Strike/util.h
src/SOS/lldbplugin/lldbplugin.vcxproj [new file with mode: 0644]
src/SOS/lldbplugin/soscommand.cpp

index 486b96bf6647fa2062e0ca4d2c2d0388110a0459..36da45b467c7eeb12dc8cdb0b21351d49224ece4 100644 (file)
@@ -15,76 +15,140 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SOS.UnitTests", "src\SOS\SO
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVersionFile", "eng\CreateVersionFile.csproj", "{365381BB-2A89-4F52-BC66-1BB4A9D514BB}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lldbplugin", "src\SOS\lldbplugin\lldbplugin.vcxproj", "{D52C65C4-2C7D-45E6-9F5C-6F3A96796018}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
+               Debug|ARM = Debug|ARM
+               Debug|ARM64 = Debug|ARM64
                Debug|x64 = Debug|x64
                Debug|x86 = Debug|x86
                Release|Any CPU = Release|Any CPU
+               Release|ARM = Release|ARM
+               Release|ARM64 = Release|ARM64
                Release|x64 = Release|x64
                Release|x86 = Release|x86
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|ARM.Build.0 = Debug|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|ARM64.Build.0 = Debug|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|x64.ActiveCfg = Debug|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|x64.Build.0 = Debug|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|x86.ActiveCfg = Debug|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|x86.Build.0 = Debug|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|Any CPU.Build.0 = Release|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|ARM.ActiveCfg = Release|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|ARM.Build.0 = Release|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|ARM64.Build.0 = Release|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|x64.ActiveCfg = Release|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|x64.Build.0 = Release|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|x86.ActiveCfg = Release|Any CPU
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|x86.Build.0 = Release|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|ARM.Build.0 = Debug|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|ARM64.Build.0 = Debug|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|x64.ActiveCfg = Debug|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|x64.Build.0 = Debug|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|x86.ActiveCfg = Debug|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Debug|x86.Build.0 = Debug|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|Any CPU.Build.0 = Release|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|ARM.ActiveCfg = Release|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|ARM.Build.0 = Release|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|ARM64.Build.0 = Release|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|x64.ActiveCfg = Release|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|x64.Build.0 = Release|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|x86.ActiveCfg = Release|Any CPU
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD}.Release|x86.Build.0 = Release|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|ARM.Build.0 = Debug|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|ARM64.Build.0 = Debug|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|x64.ActiveCfg = Debug|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|x64.Build.0 = Debug|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|x86.ActiveCfg = Debug|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Debug|x86.Build.0 = Debug|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|Any CPU.Build.0 = Release|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|ARM.ActiveCfg = Release|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|ARM.Build.0 = Release|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|ARM64.Build.0 = Release|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|x64.ActiveCfg = Release|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|x64.Build.0 = Release|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|x86.ActiveCfg = Release|Any CPU
                {730C1201-1848-4F1E-8C1F-6316FB886C35}.Release|x86.Build.0 = Release|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|ARM.Build.0 = Debug|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|ARM64.Build.0 = Debug|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|x64.ActiveCfg = Debug|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|x64.Build.0 = Debug|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|x86.ActiveCfg = Debug|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Debug|x86.Build.0 = Debug|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|Any CPU.Build.0 = Release|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|ARM.ActiveCfg = Release|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|ARM.Build.0 = Release|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|ARM64.Build.0 = Release|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|x64.ActiveCfg = Release|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|x64.Build.0 = Release|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|x86.ActiveCfg = Release|Any CPU
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2}.Release|x86.Build.0 = Release|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|ARM.Build.0 = Debug|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|ARM64.Build.0 = Debug|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|x64.ActiveCfg = Debug|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|x64.Build.0 = Debug|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|x86.ActiveCfg = Debug|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Debug|x86.Build.0 = Debug|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|Any CPU.Build.0 = Release|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|ARM.ActiveCfg = Release|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|ARM.Build.0 = Release|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|ARM64.Build.0 = Release|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|x64.ActiveCfg = Release|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|x64.Build.0 = Release|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|x86.ActiveCfg = Release|Any CPU
                {365381BB-2A89-4F52-BC66-1BB4A9D514BB}.Release|x86.Build.0 = Release|Any CPU
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|Any CPU.ActiveCfg = Debug|x64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|ARM.ActiveCfg = Debug|ARM
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|ARM.Build.0 = Debug|ARM
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|ARM64.ActiveCfg = Debug|ARM64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|ARM64.Build.0 = Debug|ARM64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|x64.ActiveCfg = Debug|x64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|x64.Build.0 = Debug|x64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|x86.ActiveCfg = Debug|x86
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Debug|x86.Build.0 = Debug|x86
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|Any CPU.ActiveCfg = Release|x86
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|ARM.ActiveCfg = Release|ARM
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|ARM.Build.0 = Release|ARM
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|ARM64.ActiveCfg = Release|ARM64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|ARM64.Build.0 = Release|ARM64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|x64.ActiveCfg = Release|x64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|x64.Build.0 = Release|x64
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|x86.ActiveCfg = Release|x86
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018}.Release|x86.Build.0 = Release|x86
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -93,6 +157,7 @@ Global
                {20513BA2-A156-4A17-4C70-5AC2DBD4F833} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
                {6C43BE85-F8C3-4D76-8050-F25CE953A7FD} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
                {1532DB3C-7DCD-45C6-B697-62B8378A16A2} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
+               {D52C65C4-2C7D-45E6-9F5C-6F3A96796018} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0}
index a8f8a53b4895d0b9084cf664282d7ee698853657..885202c5907b6fdc6ccebacdfe1b964b88922a58 100644 (file)
@@ -91,6 +91,7 @@ if /i "%1" == "-restore"             (set processedArgs=!processedArgs! %1&shift
 if /i "%1" == "-pack"                (set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
 if /i "%1" == "-publish"             (set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
 if /i "%1" == "-preparemachine"      (set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
+if /i "%1" == "-projects"            (set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
 
 if [!processedArgs!] == [] (
   set __UnprocessedBuildArgs=%__args%
index 587a7c52f478281c1b1a025b434de3b103548da2..79bbe444f871d2ed8f8fcc6359f7dd4496728e29 100644 (file)
@@ -12,7 +12,6 @@
     <UsingToolNetFrameworkReferenceAssemblies>true</UsingToolNetFrameworkReferenceAssemblies>
 
     <!-- Build tools -->
-    <MicrosoftDotNetBuildToolsVersion>2.2.0-preview1-03108-01</MicrosoftDotNetBuildToolsVersion>
     <MicrosoftNetCompilersVersion>2.8.0-beta6-62830-08</MicrosoftNetCompilersVersion>
 
     <!-- CoreFX -->
index 1918c23a646a3082783409b954b6779a0da8c19c..dca44b57b854fd937a7405797cb6c5ac7d2cef7b 100755 (executable)
@@ -230,7 +230,7 @@ while :; do
             ;;
 
         # Ignored for a native build
-        --build|--rebuild|--sign|--restore|--pack|--publish|--preparemachine)
+        --build|--rebuild|--sign|--restore|--pack|--publish|--preparemachine|--projects|--solution)
             ;;
 
         *)
index 99f70236ba0546531503e1ef5b28a5c2011c64e7..af2fa21016f0cd0b99ae4885a4e7b589a8fefd4b 100644 (file)
@@ -212,18 +212,6 @@ namespace Microsoft.Diagnostic.TestHelpers
         //DebuggeeBinaryExe:    <DebuggeeBuildRoot>/<DebuggeeName>[/<DebuggeeName>]/bin/Debug/<framework>/[<runtime>]/<DebuggeeName>.exe
         //LogPath:              <DebuggeeBuildRoot>/<DebuggeeName>.txt
 
-        // As seen above the project directory has two forms. In most cases it is identical with the solution
-        // directory for solutions that only build one managed binary. For a few cases where we need to build
-        // multiple binaries the project directory is nested one additional level. Siblings of the project directory
-        // are used for the referenced assemblies' project directories. For example:
-        //<DebuggeeBuildRoot>/MyApp/global.json
-        //<DebuggeeBuildRoot>/MyApp/MyApp/project.json
-        //<DebuggeeBuildRoot>/MyApp/MyHelperLib/project.json
-
-        // some combinations of dotnet + project.json seem to produce a runtime directory after the framework and some don't.
-        // I don't yet understand what exact factors drive this choice though I assume it has to do with shared runtime support.
-        // The logic works for the current default configuration but may not correctly handle others.
-        //
         // When the runtime directory is present it will have a native host exe in it that has been renamed to the debugee
         // name. It also has a managed dll in it which functions as a managed exe when renamed.
         // When the runtime directory is missing, the framework directory will have a managed dll in it that functions if it
index 847fd8fffa6260588daf9e7a3ea4ae6ff7dfe540..f7d8f9e48eb7629bf369e24704619889d5635f58 100644 (file)
@@ -8,7 +8,8 @@
     <Description>Diagnostic test support</Description>
     <PackageReleaseNotes>$(Description)</PackageReleaseNotes>
     <PackageTags>tests</PackageTags>
-    <DefineConstants>$(DefineConstants);CORE_CLR</DefineConstants>
+    <DebugType>embedded</DebugType>
+    <DebugSymbols>true</DebugSymbols>
   </PropertyGroup>
   
   <ItemGroup>
index 37aa51ab422ca7ea078f1532e658b7dcd2308f18..8f6d09241386e63d2748f4a23abc87a5c881949d 100644 (file)
@@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
 using System.Text;
 using System.Xml.Linq;
 using Xunit;
+using Xunit.Extensions;
 
 namespace Microsoft.Diagnostic.TestHelpers
 {
@@ -308,6 +309,14 @@ namespace Microsoft.Diagnostic.TestHelpers
             get { return GetValue("TargetArchitecture").ToLowerInvariant(); }
         }
 
+        /// <summary>
+        /// Built for "Debug" or "Release". Can be null.
+        /// </summary>
+        public string TargetConfiguration
+        {
+            get { return GetValue("TargetConfiguration"); }
+        }
+
         /// <summary>
         /// The product "projectk" (.NET Core) or "desktop".
         /// </summary>
@@ -401,14 +410,6 @@ namespace Microsoft.Diagnostic.TestHelpers
             get { return MakeCanonicalPath(GetValue("RuntimeSymbolsPath")); }
         }
 
-        /// <summary>
-        /// The root of the debuggees (managed and native)
-        /// </summary>
-        public string DebuggeeRootDir
-        {
-            get { return MakeCanonicalPath(GetValue("DebuggeeRootDir")); }
-        }
-
         /// <summary>
         /// How the debuggees are built: "prebuilt" or "cli" (builds the debuggee during the test run with build and cli configuration).
         /// </summary>
@@ -491,7 +492,7 @@ namespace Microsoft.Diagnostic.TestHelpers
                         }
                     }
                 }
-                return 0;
+                throw new SkipTestException("RuntimeFrameworkVersion (major) is not valid");
             }
         }
 
@@ -669,7 +670,6 @@ namespace Microsoft.Diagnostic.TestHelpers
     {
         static OS()
         {
-#if CORE_CLR // Only core build can run on different OSes
             if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
             {
                 Kind = OSKind.Linux;
@@ -687,10 +687,6 @@ namespace Microsoft.Diagnostic.TestHelpers
                 // Default to Unknown
                 Kind = OSKind.Unknown;
             }
-
-#else   // For everything else there's Windows 
-            _kind = OSKind.Windows; 
-#endif
         }
 
         /// <summary>
index e0590daf54cf422bf8ba425b245580ad8079144c..d86aefd107118085a6b616ca60095700ff64b735 100644 (file)
@@ -20,8 +20,7 @@
   <DumpDir>$(RootBinDir)/tmp/$(TargetConfiguration)\dumps</DumpDir>
   
   <DebuggeeSourceRoot>$(RepoRootDir)/src/SOS/SOS.UnitTests/Debuggees</DebuggeeSourceRoot>
-  <DebuggeeRootDir>$(RootBinDir)/Debuggees</DebuggeeRootDir>
-  <DebuggeeBuildRoot>$(DebuggeeRootDir)</DebuggeeBuildRoot>
+  <DebuggeeBuildRoot>$(RootBinDir)/Debuggees</DebuggeeBuildRoot>
   <DebuggeeNativeLibRoot>$(DebuggeeBuildRoot)/native</DebuggeeNativeLibRoot>
   <DebuggeeBuildProcess>cli</DebuggeeBuildProcess>
   <CliPath>$(RepoRootDir)/.dotnet/dotnet</CliPath>
index 981337a624fa8f3e19fdbe0b93647a998bc8ea3c..2eaf15b2b141caaf13d1f68e25f3bb480eef9a2e 100644 (file)
   <DumpDir>$(RootBinDir)\tmp\$(TargetConfiguration)\dumps</DumpDir>
   <CDBPath>$(NuGetPackageCacheDir)\cdb-sos\1.1.0\runtimes\win-$(TargetArchitecture)\native\cdb.exe</CDBPath>
   <CDBHelperExtension>$(InstallDir)\runcommand.dll</CDBHelperExtension>
-  
+
   <DebuggeeSourceRoot>$(RepoRootDir)\src\SOS\SOS.UnitTests\Debuggees</DebuggeeSourceRoot>
-  <DebuggeeRootDir>$(RootBinDir)\Debuggees</DebuggeeRootDir>
-  <DebuggeeBuildRoot>$(DebuggeeRootDir)</DebuggeeBuildRoot>
+  <DebuggeeBuildRoot>$(RootBinDir)\Debuggees</DebuggeeBuildRoot>
   <DebuggeeNativeLibRoot>$(DebuggeeBuildRoot)\native</DebuggeeNativeLibRoot>
   <DebuggeeBuildProcess>cli</DebuggeeBuildProcess>
   <CliPath>$(RepoRootDir)\.dotnet\dotnet.exe</CliPath>
index ff295128e20986fcb3bcdc5e2f3c86cc0c16d02c..93aaa518fd7d0d8f7dbfa3aba5cdffa50c4ffda5 100644 (file)
@@ -19,6 +19,8 @@ EXPORTS
     dumpasync=DumpAsync
     DumpClass
     dumpclass=DumpClass
+    DumpDelegate
+    dumpdelegate=DumpDelegate
     DumpDomain
     dumpdomain=DumpDomain
 #ifdef TRACE_GC
@@ -136,6 +138,7 @@ EXPORTS
     threadpool=ThreadPool
     tp=ThreadPool
     Threads
+    t=Threads
     threads=Threads
     clrthreads=Threads
     ThreadState
index 5e2ab8bf3cbed42676d96f28ce65802192f969f8..1ecb207439cc2ceeb7728c1ac87734b97a1860e5 100644 (file)
@@ -9,6 +9,7 @@ DumpArray
 DumpAssembly
 DumpAsync
 DumpClass
+DumpDelegate
 DumpDomain
 DumpGCData
 DumpHeap
@@ -43,6 +44,8 @@ Name2EE
 PrintException
 StopOnCatch
 SetHostRuntime
+SOSFlush
+SyncBlk
 Threads
 ThreadState
 Token2EE
index abd432e49a1d4b138e0b110c09c440ffd050f86f..fd3e9573e146ee4b6f32782942b2cc958681cdd8 100644 (file)
@@ -26,14 +26,15 @@ Object Inspection                  Examining code and stacks
 DumpObj (do)                       Threads (clrthreads)
 DumpArray (da)                     ThreadState
 DumpAsync                          IP2MD
-DumpStackObjects (dso)             U
-DumpHeap                           DumpStack
-DumpVC                             EEStack
-GCRoot                             CLRStack
-ObjSize                            GCInfo
-FinalizeQueue                      EHInfo
-PrintException (pe)                BPMD
-TraverseHeap                       COMState
+DumpDelegate                       U
+DumpStackObjects (dso)             DumpStack
+DumpHeap                           EEStack
+DumpVC                             CLRStack
+GCRoot                             GCInfo
+ObjSize                            EHInfo
+FinalizeQueue                      BPMD
+PrintException (pe)                COMState
+TraverseHeap                       
 
 Examining CLR data structures      Diagnostic Utilities
 -----------------------------      -----------------------------
@@ -339,64 +340,20 @@ The arguments in detail:
 \\
 
 COMMAND: dumpasync.
-!DumpAsync [-mt <MethodTable address>] 
+!DumpAsync [-addr <Object Address>]
+           [-mt <MethodTable address>]
            [-type <partial type name>]
-           [-waiting]
-           [-roots]]
+           [-tasks]
+           [-completed]
+           [-fields]
+           [-stacks]
+           [-roots]
 
 !DumpAsync traverses the garbage collected heap, looking for objects representing
 async state machines as created when an async method's state is transferred to the
 heap.  This command recognizes async state machines defined as "async void", "async Task",
-"async Task<T>", "async ValueTask", and "async ValueTask<T>".
-
-The output includes a block of details for each async state machine object found.
-These details include:
-    - a line for the type of the async state machine object, including its MethodTable address,
-      its object address, its size, and its type name.
-    - a line for the state machine type name as contained in the object.
-    - a listing of each field on the state machine.
-    - a line for a continuation from this state machine object, if one or more has been registered.
-    - discovered GC roots for this async state machine object.
-  
-For example:
-
-    0:011> !DumpAsync -roots
-    #0
-    000001989f413de0 00007ff88c506ba8      112  System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
-    StateMachine: Program+<MethodD>d__4 (struct)
-                  MT    Field   Offset                 Type VT     Attr            Value Name
-    00007ff8d3df4b80  400000d        0         System.Int32  1 instance                0 <>1__state
-    00007ff8d3e082c0  400000e        8 ...TaskMethodBuilder  1 instance 000001989f413e38 <>t__builder
-    00007ff8d3dfea90  400000f       10 ...vices.TaskAwaiter  1 instance 000001989f413e40 <>u__1
-    Continuation: 000001989f413e50 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodC>d__3, test]])
-    GC roots:
-        Thread 2936c:
-            000000071a37e050 00007ff8d3ac1657 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken) [d:\repos\coreclr\src\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs @ 2977]
-                rbp+10: 000000071a37e0c0
-                    ->  000001989f413fa0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<Main>d__0, test]]
-                    ->  000001989f413f30 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodA>d__1, test]]
-                    ->  000001989f413ec0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodB>d__2, test]]
-                    ->  000001989f413e50 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodC>d__3, test]]
-                    ->  000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
-        HandleTable:
-            000001989d8415f8 (pinned handle)
-            -> 00000198af3e1038 System.Object[]
-            -> 000001989f413410 System.Threading.TimerQueue[]
-            -> 000001989f413468 System.Threading.TimerQueue
-            -> 000001989f413330 System.Threading.TimerQueueTimer
-            -> 000001989f412e40 System.Threading.Tasks.Task+DelayPromise
-            -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
-    ...
-
-
-The arguments in detail:
-
--mt       List only those state machine objects with the MethodTable given.
--type     List only those state machine objects whose type name is a
-          substring match of the string provided.
--waiting  List only those state machines that are currently at an await point.
--roots    Include GC root information for each state machine object.
-
+"async Task<T>", "async ValueTask", and "async ValueTask<T>".  It also optionally supports
+any other tasks.
 \\
 
 COMMAND: dso.
@@ -416,6 +373,19 @@ interested in objects with invalid fields.
 The abbreviation !dso can be used for brevity.
 \\
 
+COMMAND: dumpdelegate.
+!DumpDelegate <delegate address>
+
+!DumpDelegate finds and outputs the one or more method descriptors associated with a delegate object.
+
+For example:
+
+       0:000> !dumpdelegate
+    Target           Method           Name
+    000001461bacb0d8 00007ffc5c894b80 ConsoleApp16.Program.InstanceMethod()
+    000001461bacb098 00007ffc5c894b68 ConsoleApp16.Program.StaticMethod()
+\\
+
 COMMAND: dumpheap.
 !DumpHeap [-stat] 
           [-strings] 
index 3c6d512f58b193cb8421ab3c3a084393f2db8857..df82d08fbbd3312a3b5ad5a1ee3a82a403d8ccbd 100644 (file)
@@ -26,19 +26,20 @@ Object Inspection                  Examining code and stacks
 DumpObj (dumpobj)                  Threads (clrthreads)
 DumpArray                          ThreadState
 DumpAsync (dumpasync)              IP2MD (ip2md)
-DumpStackObjects (dso)             u (clru)
-DumpHeap (dumpheap)                DumpStack (dumpstack)
-DumpVC                             EEStack (eestack)
-GCRoot (gcroot)                    CLRStack (clrstack)
-PrintException (pe)                GCInfo
-                                   EHInfo
+DumpDelegate (dumpdelegate)        u (clru)
+DumpStackObjects (dso)             DumpStack (dumpstack)
+DumpHeap (dumpheap)                EEStack (eestack)
+DumpVC                             CLRStack (clrstack)
+GCRoot (gcroot)                    GCInfo
+PrintException (pe)                EHInfo
                                    bpmd (bpmd)
-
+                                   
 Examining CLR data structures      Diagnostic Utilities
 -----------------------------      -----------------------------
 DumpDomain (dumpdomain)            VerifyHeap
 EEHeap (eeheap)                    FindAppDomain          
 Name2EE (name2ee)                  DumpLog (dumplog)
+SyncBlk (syncblk)
 DumpMT (dumpmt)
 DumpClass (dumpclass)
 DumpMD (dumpmd)                    
@@ -202,64 +203,20 @@ The arguments in detail:
 \\
 
 COMMAND: dumpasync.
-DumpAsync [-mt <MethodTable address>] 
+DumpAsync [-addr <Object Address>]
+          [-mt <MethodTable address>]
           [-type <partial type name>]
-          [-waiting]
+          [-tasks]
+          [-completed]
+          [-fields]
+          [-stacks]
           [-roots]
 
 DumpAsync traverses the garbage collected heap, looking for objects representing
 async state machines as created when an async method's state is transferred to the
 heap.  This command recognizes async state machines defined as "async void", "async Task",
-"async Task<T>", "async ValueTask", and "async ValueTask<T>".
-
-The output includes a block of details for each async state machine object found.
-These details include:
-    - a line for the type of the async state machine object, including its MethodTable address,
-      its object address, its size, and its type name.
-    - a line for the state machine type name as contained in the object.
-    - a listing of each field on the state machine.
-    - a line for a continuation from this state machine object, if one or more has been registered.
-    - discovered GC roots for this async state machine object.
-  
-For example:
-
-    (lldb) dumpasync -roots
-    #0
-    000001989f413de0 00007ff88c506ba8      112  System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
-    StateMachine: Program+<MethodD>d__4 (struct)
-                  MT    Field   Offset                 Type VT     Attr            Value Name
-    00007ff8d3df4b80  400000d        0         System.Int32  1 instance                0 <>1__state
-    00007ff8d3e082c0  400000e        8 ...TaskMethodBuilder  1 instance 000001989f413e38 <>t__builder
-    00007ff8d3dfea90  400000f       10 ...vices.TaskAwaiter  1 instance 000001989f413e40 <>u__1
-    Continuation: 000001989f413e50 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodC>d__3, test]])
-    GC roots:
-        Thread 2936c:
-            000000071a37e050 00007ff8d3ac1657 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken) [d:\repos\coreclr\src\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs @ 2977]
-                rbp+10: 000000071a37e0c0
-                    ->  000001989f413fa0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<Main>d__0, test]]
-                    ->  000001989f413f30 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodA>d__1, test]]
-                    ->  000001989f413ec0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodB>d__2, test]]
-                    ->  000001989f413e50 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodC>d__3, test]]
-                    ->  000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
-        HandleTable:
-            000001989d8415f8 (pinned handle)
-            -> 00000198af3e1038 System.Object[]
-            -> 000001989f413410 System.Threading.TimerQueue[]
-            -> 000001989f413468 System.Threading.TimerQueue
-            -> 000001989f413330 System.Threading.TimerQueueTimer
-            -> 000001989f412e40 System.Threading.Tasks.Task+DelayPromise
-            -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
-    ...
-
-
-The arguments in detail:
-
--mt       List only those state machine objects with the MethodTable given.
--type     List only those state machine objects whose type name is a
-          substring match of the string provided.
--waiting  List only those state machines that are currently at an await point.
--roots    Include GC root information for each state machine object.
-
+"async Task<T>", "async ValueTask", and "async ValueTask<T>".  It also optionally supports
+any other tasks.
 \\
 
 COMMAND: dumpstackobjects.
@@ -278,6 +235,19 @@ interested in objects with invalid fields.
 The abbreviation dso can be used for brevity.
 \\
 
+COMMAND: dumpdelegate.
+DumpDelegate <delegate address>
+
+DumpDelegate finds and outputs the one or more method descriptors associated with a delegate object.
+
+For example:
+
+       0:000> !dumpdelegate
+    Target           Method           Name
+    000001461bacb0d8 00007ffc5c894b80 ConsoleApp16.Program.InstanceMethod()
+    000001461bacb098 00007ffc5c894b68 ConsoleApp16.Program.StaticMethod()
+\\
+
 COMMAND: dumpheap.
 DumpHeap [-stat] 
          [-strings] 
@@ -294,7 +264,7 @@ DumpHeap [-stat]
 
 DumpHeap is a powerful command that traverses the garbage collected heap, 
 collection statistics about objects. With it's various options, it can look for
-particular types, restrict to a range, or look for ThinLocks (see SyncBlk 
+particular types, restrict to a range, or look for ThinLocks (see syncblk 
 documentation). Finally, it will provide a warning if it detects excessive 
 fragmentation in the GC heap. 
 
@@ -347,7 +317,7 @@ The arguments in detail:
 -live     Only print live objects
 -dead     Only print dead objects (objects which will be collected in the
           next full GC)
--thinlock Report on any ThinLocks (an efficient locking scheme, see SyncBlk 
+-thinlock Report on any ThinLocks (an efficient locking scheme, see syncblk 
           documentation for more info)
 -startAtLowerBound 
           Force heap walk to begin at lower bound of a supplied address range.
@@ -1186,6 +1156,84 @@ all loaded modules in all domains. And remember that you can browse all the
 types in a module with DumpModule -mt <module pointer>.
 \\
 
+COMMAND: syncblk.
+SyncBlk [-all | <syncblk number>]
+
+A SyncBlock is a holder for extra information that doesn't need to be created 
+for every object. It can hold COM Interop data, HashCodes, and locking 
+information for thread-safe operations.
+
+When called without arguments, syncblk will print the list of SyncBlocks 
+corresponding to objects that are owned by a thread. For example, a
+
+    lock(MyObject)
+    {
+        ....
+    }
+
+statement will set MyObject to be owned by the current thread. A SyncBlock will
+be created for MyObject, and the thread ownership information stored there 
+(this is an oversimplification, see NOTE below). If another thread tries to 
+execute the same code, they won't be able to enter the block until the first 
+thread exits.
+
+This makes syncblk useful for detecting managed deadlocks. Consider that the
+following code is executed by Threads A & B:
+
+    Resource r1 = new Resource();
+    Resource r2 = new Resource();
+
+    ...
+    
+    lock(r1)                             lock(r2)
+    {                                    {
+        lock(r2)                             lock(r1)
+        {                                    {
+            ...                                  ...
+        }                                    }
+    }                                    }
+
+This is a deadlock situation, as Thread A could take r1, and Thread B r2, 
+leaving both threads with no option but to wait forever in the second lock 
+statement. syncblk will detect this with the following output:
+
+        0:003> syncblk
+        Index SyncBlock MonitorHeld Recursion Owning Thread Info   SyncBlock Owner
+          238 001e40ec            3         1 001e4e60   e04   3   00a7a194 Resource
+          239 001e4124            3         1 001e5980   ab8   4   00a7a1a4 Resource
+
+It means that Thread e04 owns object 00a7a194, and Thread ab8 owns object 
+00a7a1a4. Combine that information with the call stacks of the deadlock:
+
+(threads 3 and 4 have similar output)  
+        0:003> bt
+        ChildEBP RetAddr
+        0404ea04 77f5c524 SharedUserData!SystemCallStub+0x4
+        0404ea08 77e75ee0 ntdll!NtWaitForMultipleObjects+0xc
+        0404eaa4 5d9de9d6 KERNEL32!WaitForMultipleObjectsEx+0x12c
+        0404eb38 5d9def80 clr!Thread::DoAppropriateAptStateWait+0x156
+        0404ecc4 5d9dd8bb clr!Thread::DoAppropriateWaitWorker+0x360
+        0404ed20 5da628dd clr!Thread::DoAppropriateWait+0xbb
+        0404ede4 5da4e2e2 clr!CLREvent::Wait+0x29d
+        0404ee70 5da4dd41 clr!AwareLock::EnterEpilog+0x132
+        0404ef34 5da4efa3 clr!AwareLock::Enter+0x2c1
+        0404f09c 5d767880 clr!AwareLock::Contention+0x483
+        0404f1c4 03f00229 clr!JITutil_MonContention+0x2c0
+        0404f1f4 5b6ef077 image00400000!Worker.Work()+0x79
+        ...
+
+By looking at the code corresponding to Worker.Work()+0x79 (run "clru 03f00229"),
+you can see that thread 3 is attempting to acquire the Resource 00a7a1a4, which
+is owned by thread 4.
+  
+NOTE:
+It is not always the case that a SyncBlock will be created for every object 
+that is locked by a thread. In version 2.0 of the CLR and above, a mechanism 
+called a ThinLock will be used if there is not already a SyncBlock for the 
+object in question. ThinLocks will not be reported by the syncblk command. 
+You can use "dumpheap -thinlock" to list objects locked in this way.
+\\
+
 COMMAND: dumpmt.
 DumpMT [-MD] <MethodTable address>
 
index d5b1b8dfe81e3c4f2373c65eab58dc48a2403598..a08443ab52f21318d7c02e3073493ffba12a04e6 100644 (file)
 #include "ExpressionNode.h"
 #include "WatchCmd.h"
 
-#include <set>
 #include <algorithm>
-#include <vector>
 
 #include "tls.h"
 
@@ -149,6 +147,10 @@ const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
 
 #endif // !FEATURE_PAL
 
+#include <set>
+#include <vector>
+#include <map>
+
 BOOL CallStatus;
 BOOL ControlC = FALSE;
 
@@ -157,7 +159,6 @@ WCHAR g_mdName[mdNameLen];
 
 #ifndef FEATURE_PAL
 HMODULE g_hInstance = NULL;
-#include <vector>
 #include <algorithm>
 #endif // !FEATURE_PAL
 
@@ -2054,6 +2055,134 @@ DECLARE_API(DumpObj)
     return Status;
 }
 
+/**********************************************************************\
+* Routine Description:                                                 *
+*                                                                      *
+*    This function is called to dump the contents of a delegate from a *
+*    given address.                                                    *
+*                                                                      *
+\**********************************************************************/
+
+DECLARE_API(DumpDelegate)
+{
+    INIT_API();
+    MINIDUMP_NOT_SUPPORTED();
+
+    try
+    {
+        BOOL dml = FALSE;
+        DWORD_PTR dwAddr = 0;
+
+        CMDOption option[] =
+        {   // name, vptr, type, hasValue
+            {"/d", &dml, COBOOL, FALSE}
+        };
+        CMDValue arg[] =
+        {   // vptr, type
+            {&dwAddr, COHEX}
+        };
+        size_t nArg;
+        if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+        {
+            return Status;
+        }
+        if (nArg != 1)
+        {
+            ExtOut("Usage: !DumpDelegate <delegate object address>\n");
+            return Status;
+        }
+
+        EnableDMLHolder dmlHolder(dml);
+        CLRDATA_ADDRESS delegateAddr = TO_CDADDR(dwAddr);
+
+        if (!sos::IsObject(delegateAddr))
+        {
+            ExtOut("Invalid object.\n");
+        }
+        else
+        {
+            sos::Object delegateObj = TO_TADDR(delegateAddr);
+            if (!IsDerivedFrom(TO_CDADDR(delegateObj.GetMT()), W("System.Delegate")))
+            {
+                ExtOut("Object of type '%S' is not a delegate.", delegateObj.GetTypeName());
+            }
+            else
+            {
+                ExtOut("Target           Method           Name\n");
+
+                std::vector<CLRDATA_ADDRESS> delegatesRemaining;
+                delegatesRemaining.push_back(delegateAddr);
+                while (delegatesRemaining.size() > 0)
+                {
+                    delegateAddr = delegatesRemaining.back();
+                    delegatesRemaining.pop_back();
+                    delegateObj = TO_TADDR(delegateAddr);
+
+                    int offset;
+                    if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_target"))) != 0)
+                    {
+                        CLRDATA_ADDRESS target;
+                        MOVE(target, delegateObj.GetAddress() + offset);
+
+                        if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationList"))) != 0)
+                        {
+                            CLRDATA_ADDRESS invocationList;
+                            MOVE(invocationList, delegateObj.GetAddress() + offset);
+
+                            if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationCount"))) != 0)
+                            {
+                                int invocationCount;
+                                MOVE(invocationCount, delegateObj.GetAddress() + offset);
+
+                                if (invocationList == NULL)
+                                {
+                                    CLRDATA_ADDRESS md;
+                                    DMLOut("%s ", DMLObject(target));
+                                    if (TryGetMethodDescriptorForDelegate(delegateAddr, &md))
+                                    {
+                                        DMLOut("%s ", DMLMethodDesc(md));
+                                        NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
+                                        ExtOut("%S\n", g_mdName);
+                                    }
+                                    else
+                                    {
+                                        ExtOut("(unknown)\n");
+                                    }
+                                }
+                                else if (sos::IsObject(invocationList, false))
+                                {
+                                    DacpObjectData objData;
+                                    if (objData.Request(g_sos, invocationList) == S_OK &&
+                                        objData.ObjectType == OBJ_ARRAY &&
+                                        invocationCount <= objData.dwNumComponents)
+                                    {
+                                        for (int i = 0; i < invocationCount; i++)
+                                        {
+                                            CLRDATA_ADDRESS elementPtr;
+                                            MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize)));
+                                            if (elementPtr != NULL && sos::IsObject(elementPtr, false))
+                                            {
+                                                delegatesRemaining.push_back(elementPtr);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return S_OK;
+    }
+    catch (const sos::Exception &e)
+    {
+        ExtOut("%s\n", e.what());
+        return E_FAIL;
+    }
+}
+
 CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
 {
     // We want to follow back until we get the mt for System.Exception
@@ -4102,11 +4231,169 @@ private:
 /**********************************************************************\
 * Routine Description:                                                 *
 *                                                                      *
-*    This function dumps async state machines on GC heap,          *
+*    This function dumps async state machines on GC heap,              *
 *    displaying details about each async operation found.              *
 *    (May not work if GC is in progress.)                              *
 *                                                                      *
 \**********************************************************************/
+
+void ResolveContinuation(CLRDATA_ADDRESS* contAddr)
+{
+    // Ideally this continuation is itself an async method box.
+    sos::Object contObj = TO_TADDR(*contAddr);
+    if (GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("StateMachine")) == 0)
+    {
+        // It was something else.
+
+        // If it's a standard task continuation, get its task field.
+        int offset;
+        if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_task"))) != 0)
+        {
+            MOVE(*contAddr, contObj.GetAddress() + offset);
+            if (sos::IsObject(*contAddr, false))
+            {
+                contObj = TO_TADDR(*contAddr);
+            }
+        }
+        else
+        {
+            // If it's storing an action wrapper, try to follow to that action's target.
+            if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_action"))) != 0)
+            {
+                MOVE(*contAddr, contObj.GetAddress() + offset);
+                if (sos::IsObject(*contAddr, false))
+                {
+                    contObj = TO_TADDR(*contAddr);
+                }
+            }
+
+            // If it was, or if it's storing an action, try to follow through to the action's target.
+            if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0)
+            {
+                MOVE(*contAddr, contObj.GetAddress() + offset);
+                if (sos::IsObject(*contAddr, false))
+                {
+                    contObj = TO_TADDR(*contAddr);
+                }
+            }
+        }
+
+        // Use whatever object we ended with.
+        *contAddr = contObj.GetAddress();
+    }
+}
+
+bool TryGetContinuation(CLRDATA_ADDRESS addr, CLRDATA_ADDRESS mt, CLRDATA_ADDRESS* contAddr)
+{
+    // Get the continuation field from the task.
+    int offset = GetObjFieldOffset(addr, mt, W("m_continuationObject"));
+    if (offset != 0)
+    {
+        DWORD_PTR contObjPtr;
+        MOVE(contObjPtr, addr + offset);
+        if (sos::IsObject(contObjPtr, false))
+        {
+            *contAddr = TO_CDADDR(contObjPtr);
+            ResolveContinuation(contAddr);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+struct AsyncRecord
+{
+    CLRDATA_ADDRESS Address;
+    CLRDATA_ADDRESS MT;
+    DWORD Size;
+    CLRDATA_ADDRESS StateMachineAddr;
+    CLRDATA_ADDRESS StateMachineMT;
+    BOOL FilteredByOptions;
+    BOOL IsStateMachine;
+    BOOL IsValueType;
+    BOOL IsTopLevel;
+    int TaskStateFlags;
+    int StateValue;
+    std::vector<CLRDATA_ADDRESS> Continuations;
+};
+
+bool AsyncRecordIsCompleted(AsyncRecord& ar)
+{
+    const int TASK_STATE_COMPLETED_MASK = 0x1600000;
+    return (ar.TaskStateFlags & TASK_STATE_COMPLETED_MASK) != 0;
+}
+
+const char* GetAsyncRecordStatusDescription(AsyncRecord& ar)
+{
+    const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000;
+    const int TASK_STATE_FAULTED = 0x200000;
+    const int TASK_STATE_CANCELED = 0x400000;
+
+    if ((ar.TaskStateFlags & TASK_STATE_RAN_TO_COMPLETION) != 0) return "Success";
+    if ((ar.TaskStateFlags & TASK_STATE_FAULTED) != 0) return "Failed";
+    if ((ar.TaskStateFlags & TASK_STATE_CANCELED) != 0) return "Canceled";
+    return "Pending";
+}
+
+void ExtOutTaskDelegateMethod(sos::Object& obj)
+{
+    DacpFieldDescData actionField;
+    int offset = GetObjFieldOffset(obj.GetAddress(), obj.GetMT(), W("m_action"), TRUE, &actionField);
+    if (offset != 0)
+    {
+        CLRDATA_ADDRESS actionAddr;
+        MOVE(actionAddr, obj.GetAddress() + offset);
+        CLRDATA_ADDRESS actionMD;
+        if (actionAddr != NULL && TryGetMethodDescriptorForDelegate(actionAddr, &actionMD))
+        {
+            NameForMD_s((DWORD_PTR)actionMD, g_mdName, mdNameLen);
+            ExtOut("(%S) ", g_mdName);
+        }
+    }
+}
+
+void ExtOutTaskStateFlagsDescription(int stateFlags)
+{
+    if (stateFlags == 0) return;
+
+    ExtOut("State Flags: ");
+
+    // TaskCreationOptions.*
+    if ((stateFlags & 0x01) != 0) ExtOut("PreferFairness ");
+    if ((stateFlags & 0x02) != 0) ExtOut("LongRunning ");
+    if ((stateFlags & 0x04) != 0) ExtOut("AttachedToParent ");
+    if ((stateFlags & 0x08) != 0) ExtOut("DenyChildAttach ");
+    if ((stateFlags & 0x10) != 0) ExtOut("HideScheduler ");
+    if ((stateFlags & 0x40) != 0) ExtOut("RunContinuationsAsynchronously ");
+
+    // InternalTaskOptions.*
+    if ((stateFlags & 0x0200) != 0) ExtOut("ContinuationTask ");
+    if ((stateFlags & 0x0400) != 0) ExtOut("PromiseTask ");
+    if ((stateFlags & 0x1000) != 0) ExtOut("LazyCancellation ");
+    if ((stateFlags & 0x2000) != 0) ExtOut("QueuedByRuntime ");
+    if ((stateFlags & 0x4000) != 0) ExtOut("DoNotDispose ");
+
+    // TASK_STATE_*
+    if ((stateFlags & 0x10000) != 0) ExtOut("STARTED ");
+    if ((stateFlags & 0x20000) != 0) ExtOut("DELEGATE_INVOKED ");
+    if ((stateFlags & 0x40000) != 0) ExtOut("DISPOSED ");
+    if ((stateFlags & 0x80000) != 0) ExtOut("EXCEPTIONOBSERVEDBYPARENT ");
+    if ((stateFlags & 0x100000) != 0) ExtOut("CANCELLATIONACKNOWLEDGED ");
+    if ((stateFlags & 0x200000) != 0) ExtOut("FAULTED ");
+    if ((stateFlags & 0x400000) != 0) ExtOut("CANCELED ");
+    if ((stateFlags & 0x800000) != 0) ExtOut("WAITING_ON_CHILDREN ");
+    if ((stateFlags & 0x1000000) != 0) ExtOut("RAN_TO_COMPLETION ");
+    if ((stateFlags & 0x2000000) != 0) ExtOut("WAITINGFORACTIVATION ");
+    if ((stateFlags & 0x4000000) != 0) ExtOut("COMPLETION_RESERVED ");
+    if ((stateFlags & 0x8000000) != 0) ExtOut("THREAD_WAS_ABORTED ");
+    if ((stateFlags & 0x10000000) != 0) ExtOut("WAIT_COMPLETION_NOTIFICATION ");
+    if ((stateFlags & 0x20000000) != 0) ExtOut("EXECUTIONCONTEXT_IS_NULL ");
+    if ((stateFlags & 0x40000000) != 0) ExtOut("TASKSCHEDULED_WAS_FIRED ");
+
+    ExtOut("\n");
+}
+
 DECLARE_API(DumpAsync)
 {
     INIT_API();
@@ -4121,40 +4408,47 @@ DECLARE_API(DumpAsync)
     {
         // Process command-line arguments.
         size_t nArg = 0;
-        TADDR mt = NULL;
+        TADDR mt = NULL, addr = NULL;
         ArrayHolder<char> ansiType = NULL;
         ArrayHolder<WCHAR> type = NULL;
-        BOOL dml = FALSE, waiting = FALSE, roots = FALSE;
+        BOOL dml = FALSE, includeCompleted = FALSE, includeStacks = FALSE, includeRoots = FALSE, includeAllTasks = FALSE, dumpFields = FALSE;
         CMDOption option[] =
         {   // name, vptr, type, hasValue
-            { "-mt", &mt, COHEX, TRUE },             // dump state machines only with a given MethodTable
-            { "-type", &ansiType, COSTRING, TRUE },  // dump state machines only that contain the specified type substring
-            { "-waiting", &waiting, COBOOL, FALSE }, // dump state machines only when they're in a waiting state
-            { "-roots", &roots, COBOOL, FALSE },     // gather GC root information
+            { "-addr", &addr, COHEX, TRUE },                // dump only the async object at the specified address
+            { "-mt", &mt, COHEX, TRUE },                        // dump only async objects with a given MethodTable
+            { "-type", &ansiType, COSTRING, TRUE },             // dump only async objects that contain the specified type substring
+            { "-tasks", &includeAllTasks, COBOOL, FALSE },      // include all tasks that can be found on the heap, not just async methods
+            { "-completed", &includeCompleted, COBOOL, FALSE }, // include async objects that are in a completed state
+            { "-fields", &dumpFields, COBOOL, FALSE },          // show relevant fields of found async objects
+            { "-stacks", &includeStacks, COBOOL, FALSE },       // gather and output continuation/stack information
+            { "-roots", &includeRoots, COBOOL, FALSE },         // gather and output GC root information
 #ifndef FEATURE_PAL
-            { "/d", &dml, COBOOL, FALSE },            // Debugger Markup Language
+            { "/d", &dml, COBOOL, FALSE },                      // Debugger Markup Language
 #endif
         };
-        if (!GetCMDOption(args, option, _countof(option), NULL, 0, &nArg))
-        {
-            sos::Throw<sos::Exception>("Usage: DumpAsync [-mt MethodTableAddr] [-type TypeName] [-waiting] [-roots]");
-        }
-        if (nArg != 0)
-        {
-            sos::Throw<sos::Exception>("Unexpected command-line arguments.");
+        if (!GetCMDOption(args, option, _countof(option), NULL, 0, &nArg) || nArg != 0)
+        {
+            sos::Throw<sos::Exception>(
+                "Usage: DumpAsync [-addr ObjectAddr] [-mt MethodTableAddr] [-type TypeName] [-tasks] [-completed] [-fields] [-stacks] [-roots]\n"
+                "[-addr ObjectAddr]     => Only display the async object at the specified address.\n"
+                "[-mt MethodTableAddr]  => Only display top-level async objects with the specified method table address.\n"
+                "[-type TypeName]       => Only display top-level async objects whose type name includes the specified substring.\n"
+                "[-tasks]               => Include Task and Task-derived objects, in addition to any state machine objects found.\n"
+                "[-completed]           => Include async objects that represent completed operations but that are still on the heap.\n"
+                "[-fields]              => Show the fields of state machines.\n"
+                "[-stacks]              => Gather, output, and consolidate based on continuation chains / async stacks for discovered async objects.\n"
+                "[-roots]               => Perform a gcroot on each rendered async object.\n"
+                );
         }
         if (ansiType != NULL)
         {
-            if (mt != NULL)
-            {
-                sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
-            }
-
             size_t ansiTypeLen = strlen(ansiType) + 1;
             type = new WCHAR[ansiTypeLen];
             MultiByteToWideChar(CP_ACP, 0, ansiType, -1, type, (int)ansiTypeLen);
         }
+        
         EnableDMLHolder dmlHolder(dml);
+        BOOL hasTypeFilter = mt != NULL || ansiType != NULL || addr != NULL;
 
         // Display a message if the heap isn't verified.
         sos::GCHeap gcheap;
@@ -4163,160 +4457,320 @@ DECLARE_API(DumpAsync)
             DisplayInvalidStructuresMessage();
         }
 
-        // Print out header for the main line of each async state machine object.
-        ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s %s\n", "Address", "MT", "Size", "Name");
-
-        // Walk each heap object looking for async state machine objects.
-        BOOL missingStateFieldWarning = FALSE;
-        int numStateMachines = 0;
+        // Walk each heap object looking for async state machine objects.  As we're targeting .NET Core 2.1+, all such objects
+        // will be Task or Task-derived types.
+        std::map<CLRDATA_ADDRESS, AsyncRecord> asyncRecords;
         for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr)
         {
-            // Skip objects we know to be too small to possibly be a state machine.
-            // This helps filter out some caching data structures generated by the compiler.
-            if (itr->GetSize() <= 24)
+            // Skip objects too small to be state machines or tasks, avoiding some compiler-generated caching data structures.
+            if (itr->GetSize() <= 24) 
             {
                 continue;
             }
 
-            // Match only MTs the user requested.
-            if (mt != NULL && mt != itr->GetMT())
+            // Match only async objects.
+            if (includeAllTasks)
             {
-                continue;
-            }
-
-            // Match only type name substrings the user requested.
-            if (type != NULL && _wcsstr(itr->GetTypeName(), type) == NULL)
-            {
-                continue;
+                // If the user has selected to include all tasks and not just async state machine boxes, we simply need to validate
+                // that this is Task or Task-derived, and if it's not, skip it.
+                if (!IsDerivedFrom(itr->GetMT(), W("System.Threading.Tasks.Task")))
+                {
+                    continue;
+                }
             }
-
-            // Match only the two known state machine class name prefixes.
-            if (_wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1"), 79) != 0 &&               // Normal box.
-                _wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1"), 95) != 0) // Used when certain ETW events enabled.
+            else
             {
-                continue;
+                // Otherwise, we only care about AsyncStateMachineBox`1 as well as the DebugFinalizableAsyncStateMachineBox`1
+                // that's used when certain ETW events are set.
+                if (_wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1"), 79) != 0 &&
+                    _wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1"), 95) != 0)
+                {
+                    continue;
+                }
             }
 
-            // Get the async state machine object's StateMachine field.  If we can't, it's not
-            // an async state machine we can handle.
+            // Create an AsyncRecord to store the state for this instance.  We're likely going to keep the object at this point,
+            // though we may still discard/skip it with a few checks later; to do that, though, we'll need some of the info
+            // gathered here, so we construct the record to store the data.
+            AsyncRecord ar;
+            ar.Address = itr->GetAddress();
+            ar.MT = itr->GetMT();
+            ar.Size = (DWORD)itr->GetSize();
+            ar.StateMachineAddr = itr->GetAddress();
+            ar.StateMachineMT = itr->GetMT();
+            ar.IsValueType = false;
+            ar.IsTopLevel = true;
+            ar.IsStateMachine = false;
+            ar.TaskStateFlags = 0;
+            ar.StateValue = 0;
+            ar.FilteredByOptions = // we process all objects to support forming proper chains, but then only display ones that match the user's request
+                (mt == NULL || mt == itr->GetMT()) && // Match only MTs the user requested.
+                (type == NULL || _wcsstr(itr->GetTypeName(), type) != NULL) && // Match only type name substrings the user requested.
+                (addr == NULL || addr == itr->GetAddress()); // Match only the object at the specified address.
+
+            // Get the state flags for the task.  This is used to determine whether async objects are completed (and thus should
+            // be culled by default).  It avoids our needing to depend on interpreting the compiler's "<>1__state" field, and also lets
+            // us display state information for non-async state machine objects.
+            DacpFieldDescData stateFlagsField;
+            int offset = GetObjFieldOffset(ar.Address, ar.MT, W("m_stateFlags"), TRUE, &stateFlagsField);
+            if (offset != 0)
+            {
+                MOVE(ar.TaskStateFlags, ar.Address + offset);
+            }
+
+            // Get the async state machine object's StateMachine field.
             DacpFieldDescData stateMachineField;
             int stateMachineFieldOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("StateMachine"), TRUE, &stateMachineField);
-            if (stateMachineFieldOffset <= 0)
+            if (stateMachineFieldOffset != 0)
             {
-                continue;
+                ar.IsStateMachine = true;
+                ar.IsValueType = stateMachineField.Type == ELEMENT_TYPE_VALUETYPE;
+
+                // Get the address and method table of the state machine.  While it'll generally be a struct, it is valid for it to be a
+                // class (the C# compiler generates a class in debug builds to better support Edit-And-Continue), so we accommodate both.
+                DacpFieldDescData stateField;
+                int stateFieldOffset = -1;
+                if (ar.IsValueType)
+                {
+                    ar.StateMachineAddr = itr->GetAddress() + stateMachineFieldOffset;
+                    ar.StateMachineMT = stateMachineField.MTOfType;
+                    stateFieldOffset = GetValueFieldOffset(ar.StateMachineMT, W("<>1__state"), &stateField);
+                }
+                else
+                {
+                    MOVE(ar.StateMachineAddr, itr->GetAddress() + stateMachineFieldOffset);
+                    DacpObjectData objData;
+                    if (objData.Request(g_sos, ar.StateMachineAddr) == S_OK)
+                    {
+                        ar.StateMachineMT = objData.MethodTable; // update from Canon to actual type
+                        stateFieldOffset = GetObjFieldOffset(ar.StateMachineAddr, ar.StateMachineMT, W("<>1__state"), TRUE, &stateField);
+                    }
+                }
+
+                if (stateFieldOffset >= 0 && (ar.IsValueType || stateFieldOffset != 0))
+                {
+                    MOVE(ar.StateValue, ar.StateMachineAddr + stateFieldOffset);
+                }
             }
 
-            // Get the address and method table of the state machine.  While it'll generally be a struct,
-            // it is valid for it to be a class, so we accommodate both.
-            BOOL bStateMachineIsValueType = stateMachineField.Type == ELEMENT_TYPE_VALUETYPE;
-            CLRDATA_ADDRESS stateMachineAddr;
-            CLRDATA_ADDRESS stateMachineMT;
-            if (bStateMachineIsValueType)
+            // If we only want to include incomplete async objects, skip this one if it's completed.
+            if (!includeCompleted && AsyncRecordIsCompleted(ar))
             {
-                stateMachineAddr = itr->GetAddress() + stateMachineFieldOffset;
-                stateMachineMT = stateMachineField.MTOfType;
+                continue;
             }
-            else
+
+            // If the user has asked to include "async stacks" information, resolve any continuation
+            // that might be registered with it.  This could be a single continuation, or it could
+            // be a list of continuations in the case of the same task being awaited multiple times.
+            CLRDATA_ADDRESS nextAddr;
+            if (includeStacks && TryGetContinuation(itr->GetAddress(), itr->GetMT(), &nextAddr))
             {
-                MOVE(stateMachineAddr, itr->GetAddress() + stateMachineFieldOffset);
-                DacpObjectData objData;
-                if (objData.Request(g_sos, stateMachineAddr) != S_OK)
+                sos::Object contObj = TO_TADDR(nextAddr);
+                if (_wcsncmp(contObj.GetTypeName(), W("System.Collections.Generic.List`1"), 33) == 0)
                 {
-                    // Couldn't get the class-based object; just skip this state machine.
-                    continue;
+                    // The continuation is a List<object>.  Iterate through its internal object[]
+                    // looking for non-null objects, and adding each one as a continuation.
+                    int itemsOffset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_items"));
+                    if (itemsOffset != 0)
+                    {
+                        DWORD_PTR listItemsPtr;
+                        MOVE(listItemsPtr, contObj.GetAddress() + itemsOffset);
+                        if (sos::IsObject(listItemsPtr, false))
+                        {
+                            DacpObjectData objData;
+                            if (objData.Request(g_sos, TO_CDADDR(listItemsPtr)) == S_OK && objData.ObjectType == OBJ_ARRAY)
+                            {
+                                for (int i = 0; i < objData.dwNumComponents; i++)
+                                {
+                                    CLRDATA_ADDRESS elementPtr;
+                                    MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize)));
+                                    if (elementPtr != NULL && sos::IsObject(elementPtr, false))
+                                    {
+                                        ResolveContinuation(&elementPtr);
+                                        ar.Continuations.push_back(elementPtr);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    ar.Continuations.push_back(contObj.GetAddress());
                 }
-                stateMachineMT = objData.MethodTable; // update from Canon to actual type
             }
 
-            // Get the current state value of the state machine. If the user has requested to filter down
-            // to only those state machines that are currently at an await, compare it against the expected
-            // waiting values.  This value can also be used in later analysis.
-            int stateValue = -2;
-            DacpFieldDescData stateField;
-            int stateFieldOffset = bStateMachineIsValueType ?
-                GetValueFieldOffset(stateMachineMT, W("<>1__state"), &stateField) :
-                GetObjFieldOffset(stateMachineAddr, stateMachineMT, W("<>1__state"), TRUE, &stateField);
-            if (stateFieldOffset < 0 || (!bStateMachineIsValueType && stateFieldOffset == 0))
+            // We've gathered all of the needed information for this heap object.  Add it to our list of async records.
+            asyncRecords.insert(std::pair<CLRDATA_ADDRESS, AsyncRecord>(ar.Address, ar));
+        }
+
+        // As with DumpHeap, output a summary table about all of the objects we found.  In contrast, though, his is based on the filtered
+        // list of async records we gathered rather than everything on the heap.
+        if (addr == NULL) // no point in stats if we're only targeting a single object
+        {
+            HeapStat stats;
+            for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt)
             {
-                missingStateFieldWarning = TRUE;
-                if (waiting)
+                if (!hasTypeFilter || arIt->second.FilteredByOptions)
                 {
-                    // waiting was specified and we couldn't find the field to satisfy the query,
-                    // so skip this object.
-                    continue;
+                    stats.Add((DWORD_PTR)arIt->second.MT, (DWORD)arIt->second.Size);
                 }
             }
-            else
+            stats.Sort();
+            stats.Print();
+        }
+
+        // If the user has asked for "async stacks" and if there's not MT/type name filter, look through all of our async records
+        // to find the "top-level" nodes that start rather than that are a part of a continuation chain.  When we then iterate through
+        // async records, we only print ones out that are still classified as top-level.  We don't do this if there's a type filter
+        // because in that case we consider those and only those objects to be top-level.
+        if (includeStacks && !hasTypeFilter)
+        {
+            size_t uniqueChains = asyncRecords.size();
+            for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt)
             {
-                MOVE(stateValue, stateMachineAddr + stateFieldOffset);
-                if (waiting && stateValue < 0)
+                for (std::vector<CLRDATA_ADDRESS>::iterator contIt = arIt->second.Continuations.begin(); contIt != arIt->second.Continuations.end(); ++contIt)
                 {
-                    // 0+ values correspond to the await in linear sequence in the method, so a non-negative
-                    // value indicates the state machine is at an await.  Since we're filtering for waiting,
-                    // anything else should be skipped.
-                    continue;
+                    std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator found = asyncRecords.find(*contIt);
+                    if (found != asyncRecords.end())
+                    {
+                        if (found->second.IsTopLevel)
+                        {
+                            found->second.IsTopLevel = false;
+                            uniqueChains--;
+                        }
+                    }
                 }
             }
 
-            // We now have a state machine that's passed all of our criteria.  Print out its details.
+            ExtOut("In %d chains.\n", uniqueChains);
+        }
 
-            // Print out top level description of the state machine object.
-            ExtOut("#%d\n", numStateMachines);
-            numStateMachines++;
-            DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
-            ExtOut("  %S\n", itr->GetTypeName());
+        // Print out header for the main line of each result.
+        ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s ", "Address", "MT", "Size");
+        if (includeCompleted) ExtOut("%8s ", "Status");
+        ExtOut("%10s %s\n", "State", "Description");
+
+        // Output each top-level async record.
+        int counter = 0;
+        for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt)
+        {
+            if (!arIt->second.IsTopLevel || (hasTypeFilter && !arIt->second.FilteredByOptions))
+            {
+                continue;
+            }
 
-            // Output the state machine's name and fields.
+            // Output the state machine's details as a single line.
+            sos::Object obj = TO_TADDR(arIt->second.Address);
             DacpMethodTableData mtabledata;
             DacpMethodTableFieldData vMethodTableFields;
-            if (mtabledata.Request(g_sos, stateMachineMT) == S_OK &&
-                vMethodTableFields.Request(g_sos, stateMachineMT) == S_OK &&
+            if (arIt->second.IsStateMachine &&
+                mtabledata.Request(g_sos, arIt->second.StateMachineMT) == S_OK &&
+                vMethodTableFields.Request(g_sos, arIt->second.StateMachineMT) == S_OK &&
                 vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
             {
-                sos::MethodTable mt = (TADDR)stateMachineMT;
-                ExtOut("StateMachine: %S (%s)\n", mt.GetName(), bStateMachineIsValueType ? "struct" : "class");
-                DisplayFields(stateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)stateMachineAddr, TRUE, bStateMachineIsValueType);
+                // This has a StateMachine.  Output its details.
+                sos::MethodTable mt = TO_TADDR(arIt->second.StateMachineMT);
+                DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize());
+                if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second));
+                ExtOut("%10d %S\n", arIt->second.StateValue, mt.GetName());
+                if (dumpFields) DisplayFields(arIt->second.StateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)arIt->second.StateMachineAddr, TRUE, arIt->second.IsValueType);
+            }
+            else
+            {
+                // This does not have a StateMachine.  Output the details of the Task itself.
+                DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize());
+                if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second));
+                ExtOut("[%08x] %S ", arIt->second.TaskStateFlags, obj.GetTypeName());
+                ExtOutTaskDelegateMethod(obj);
+                ExtOut("\n");
+                if (dumpFields) ExtOutTaskStateFlagsDescription(arIt->second.TaskStateFlags);
             }
 
-            // If the object already has a registered continuation, output it.
-            int iContOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("m_continuationObject"));
-            if (iContOffset > 0)
+            // If we gathered any continuations for this record, output the chains now.
+            if (includeStacks && arIt->second.Continuations.size() > 0)
             {
-                DWORD_PTR ContObjPtr;
-                MOVE(ContObjPtr, itr->GetAddress() + iContOffset);
-                DMLOut("Continuation: %s", DMLObject(ContObjPtr));
-                if (sos::IsObject(ContObjPtr, false))
+                ExtOut(includeAllTasks ? "Continuation chains:\n" : "Async \"stack\":\n");
+                std::vector<std::pair<int, CLRDATA_ADDRESS>> continuationChainToExplore;
+                continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(1, obj.GetAddress()));
+
+                // Do a depth-first traversal of continuations, outputting each continuation found and then
+                // looking in our gathered objects list for its continuations.
+                std::set<CLRDATA_ADDRESS> seen;
+                while (continuationChainToExplore.size() > 0)
                 {
-                    sos::Object contObj = ContObjPtr;
-                    ExtOut(" (%S)", contObj.GetTypeName());
+                    // Pop the next continuation from the stack.
+                    std::pair<int, CLRDATA_ADDRESS> cur = continuationChainToExplore.back();
+                    continuationChainToExplore.pop_back();
+
+                    // Get the async record for this continuation.  It should be one we already know about.
+                    std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator curAsyncRecord = asyncRecords.find(cur.second);
+                    if (curAsyncRecord == asyncRecords.end())
+                    {
+                        continue;
+                    }
+
+                    // Make sure to avoid cycles in the rare case where async records may refer to each other.
+                    if (seen.find(cur.second) != seen.end())
+                    {
+                        continue;
+                    }
+                    seen.insert(cur.second);
+
+                    // Iterate through all continuations from this object.
+                    for (std::vector<CLRDATA_ADDRESS>::iterator contIt = curAsyncRecord->second.Continuations.begin(); contIt != curAsyncRecord->second.Continuations.end(); ++contIt)
+                    {
+                        sos::Object cont = TO_TADDR(*contIt);
+
+                        // Print out the depth of the continuation with dots, then its address.
+                        for (int i = 0; i < cur.first; i++) ExtOut(".");
+                        DMLOut("%s ", DMLObject(cont.GetAddress()));
+
+                        // Print out the name of the method for this task's delegate if it has one (state machines won't, but others tasks may).
+                        ExtOutTaskDelegateMethod(cont);
+
+                        // Find the async record for this continuation, and output its name.  If it's a state machine,
+                        // also output its current state value so that a user can see at a glance its status.
+                        std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator contAsyncRecord = asyncRecords.find(cont.GetAddress());
+                        if (contAsyncRecord != asyncRecords.end())
+                        {
+                            sos::MethodTable contMT = TO_TADDR(contAsyncRecord->second.StateMachineMT);
+                            if (contAsyncRecord->second.IsStateMachine) ExtOut("(%d) ", contAsyncRecord->second.StateValue);
+                            ExtOut("%S\n", contMT.GetName());
+                        }
+                        else
+                        {
+                            ExtOut("%S\n", cont.GetTypeName());
+                        }
+
+                        // Add this continuation to the stack to explore.
+                        continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(cur.first + 1, *contIt));
+                    }
                 }
-                ExtOut("\n");
             }
 
-            // Finally, output gcroots, as they can serve as call stacks, and also help to highlight
-            // state machines that aren't being kept alive.
-            if (roots)
+            // Finally, output gcroots, as they can serve as alternative/more detailed "async stacks", and also help to highlight
+            // state machines that aren't being kept alive.  However, they're more expensive to compute, so they're opt-in.
+            if (includeRoots)
             {
                 ExtOut("GC roots:\n");
                 IncrementIndent();
                 GCRootImpl gcroot;
-                int numRoots = gcroot.PrintRootsForObject(*itr, FALSE, FALSE);
+                int numRoots = gcroot.PrintRootsForObject(obj.GetAddress(), FALSE, FALSE);
                 DecrementIndent();
-
-                if (stateValue >= 0 && numRoots == 0)
+                if (numRoots == 0 && !AsyncRecordIsCompleted(arIt->second))
                 {
-                    ExtOut("Incomplete state machine (<>1__state == %d) with 0 roots.\n", stateValue);
+                    ExtOut("Incomplete state machine or task with 0 roots.\n");
                 }
             }
 
-            ExtOut("\n");
+            // If we're rendering more than one line per entry, output a separator to help distinguish the entries.
+            if (dumpFields || includeStacks || includeRoots)
+            {
+                ExtOut("--------------------------------------------------------------------------------\n");
+            }
         }
 
-        ExtOut("\nFound %d state machines.\n", numStateMachines);
-        if (missingStateFieldWarning)
-        {
-            ExtOut("Warning: Could not find a state machine's <>1__state field.\n");
-        }
         return S_OK;
     }
     catch (const sos::Exception &e)
@@ -5009,6 +5463,8 @@ DECLARE_API(GCHeapStat)
 #endif // FEATURE_PAL
 }
 
+#endif // FEATURE_PAL
+
 /**********************************************************************\
 * Routine Description:                                                 *
 *                                                                      *
@@ -5191,14 +5647,18 @@ DECLARE_API(SyncBlk)
     
     ExtOut("-----------------------------\n");
     ExtOut("Total           %d\n", dwCount);
+#ifdef FEATURE_COMINTEROP
     ExtOut("CCW             %d\n", CCWCount);
     ExtOut("RCW             %d\n", RCWCount);
     ExtOut("ComClassFactory %d\n", CFCount);
+#endif
     ExtOut("Free            %d\n", freeCount);
    
     return Status;
 }
 
+#ifndef FEATURE_PAL
+
 #ifdef FEATURE_COMINTEROP
 struct VisitRcwArgs
 {
@@ -7697,11 +8157,15 @@ DECLARE_API(ThreadPool)
 
     if ((Status = threadpool.Request(g_sos)) == S_OK)
     {
-        BOOL doHCDump = FALSE;
+        BOOL doHCDump = FALSE, doWorkItemDump = FALSE, dml = FALSE;
 
         CMDOption option[] = 
         {   // name, vptr, type, hasValue
-            {"-ti", &doHCDump, COBOOL, FALSE}
+            {"-ti", &doHCDump, COBOOL, FALSE},
+            {"-wi", &doWorkItemDump, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+            {"/d", &dml, COBOOL, FALSE},
+#endif
         };    
 
         if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
@@ -7709,6 +8173,8 @@ DECLARE_API(ThreadPool)
             return Status;
         }
 
+        EnableDMLHolder dmlHolder(dml);
+
         ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);            
         ExtOut ("Worker Thread:");
         ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
@@ -7751,6 +8217,152 @@ DECLARE_API(ThreadPool)
             workRequestPtr = workRequestData.NextWorkRequest;
         }
 
+        if (doWorkItemDump && g_snapshot.Build())
+        {
+            // Display a message if the heap isn't verified.
+            sos::GCHeap gcheap;
+            if (!gcheap.AreGCStructuresValid())
+            {
+                DisplayInvalidStructuresMessage();
+            }
+
+            // Walk every heap item looking for the global queue and local queues.
+            ExtOut("\nQueued work items:\n%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "Queue", "Address", "Work Item");
+            HeapStat stats;
+            for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr)
+            {
+                if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue")) == 0)
+                {
+                    // We found a global queue (there should be only one, given one AppDomain).
+                    // Get its workItems ConcurrentQueue<IThreadPoolWorkItem>.
+                    int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("workItems"));
+                    if (offset > 0)
+                    {
+                        DWORD_PTR workItemsConcurrentQueuePtr;
+                        MOVE(workItemsConcurrentQueuePtr, itr->GetAddress() + offset);
+                        if (sos::IsObject(workItemsConcurrentQueuePtr, false))
+                        {
+                            // We got the ConcurrentQueue.  Get its head segment.
+                            sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr);
+                            offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head"));
+                            if (offset > 0)
+                            {
+                                // Now, walk from segment to segment, each of which contains an array of work items.
+                                DWORD_PTR segmentPtr;
+                                MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset);
+                                while (sos::IsObject(segmentPtr, false))
+                                {
+                                    sos::Object segment = TO_TADDR(segmentPtr);
+
+                                    // Get the work items array.  It's an array of Slot structs, which starts with the T.
+                                    offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots"));
+                                    if (offset <= 0)
+                                    {
+                                        break;
+                                    }
+
+                                    DWORD_PTR slotsPtr;
+                                    MOVE(slotsPtr, segment.GetAddress() + offset);
+                                    if (!sos::IsObject(slotsPtr, false))
+                                    {
+                                        break;
+                                    }
+
+                                    // Walk every element in the array, outputting details on non-null work items.
+                                    DacpObjectData slotsArray;
+                                    if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY)
+                                    {
+                                        for (int i = 0; i < slotsArray.dwNumComponents; i++)
+                                        {
+                                            CLRDATA_ADDRESS workItemPtr;
+                                            MOVE(workItemPtr, TO_CDADDR(slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize))); // the item object reference is at the beginning of the Slot
+                                            if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
+                                            {
+                                                sos::Object workItem = TO_TADDR(workItemPtr);
+                                                stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
+                                                DMLOut("%" POINTERSIZE "s %s %S", "[Global]", DMLObject(workItem.GetAddress()), workItem.GetTypeName());
+                                                if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
+                                                    (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
+                                                {
+                                                    CLRDATA_ADDRESS delegatePtr;
+                                                    MOVE(delegatePtr, workItem.GetAddress() + offset);
+                                                    CLRDATA_ADDRESS md;
+                                                    if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
+                                                    {
+                                                        NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
+                                                        ExtOut(" => %S", g_mdName);
+                                                    }
+                                                }
+                                                ExtOut("\n");
+                                            }
+                                        }
+                                    }
+
+                                    // Move to the next segment.
+                                    DacpFieldDescData segmentField;
+                                    offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField);
+                                    if (offset <= 0)
+                                    {
+                                        break;
+                                    }
+
+                                    MOVE(segmentPtr, segment.GetAddress() + offset);
+                                    if (segmentPtr == NULL)
+                                    {
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                else if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue+WorkStealingQueue")) == 0)
+                {
+                    // We found a local queue.  Get its work items array.
+                    int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("m_array"));
+                    if (offset > 0)
+                    {
+                        // Walk every element in the array, outputting details on non-null work items.
+                        DWORD_PTR workItemArrayPtr;
+                        MOVE(workItemArrayPtr, itr->GetAddress() + offset);
+                        DacpObjectData workItemArray;
+                        if (workItemArray.Request(g_sos, TO_CDADDR(workItemArrayPtr)) == S_OK && workItemArray.ObjectType == OBJ_ARRAY)
+                        {
+                            for (int i = 0; i < workItemArray.dwNumComponents; i++)
+                            {
+                                CLRDATA_ADDRESS workItemPtr;
+                                MOVE(workItemPtr, TO_CDADDR(workItemArray.ArrayDataPtr + (i * workItemArray.dwComponentSize)));
+                                if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
+                                {
+                                    sos::Object workItem = TO_TADDR(workItemPtr);
+                                    stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
+                                    DMLOut("%s %s %S", DMLObject(itr->GetAddress()), DMLObject(workItem.GetAddress()), workItem.GetTypeName());
+                                    if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
+                                        (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
+                                    {
+                                        CLRDATA_ADDRESS delegatePtr;
+                                        MOVE(delegatePtr, workItem.GetAddress() + offset);
+                                        CLRDATA_ADDRESS md;
+                                        if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
+                                        {
+                                            NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
+                                            ExtOut(" => %S", g_mdName);
+                                        }
+                                    }
+                                    ExtOut("\n");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Output a summary.
+            stats.Sort();
+            stats.Print();
+            ExtOut("\n");
+        }
+
         if (doHCDump)
         {
             ExtOut ("--------------------------------------\n");
@@ -10508,27 +11120,6 @@ DECLARE_API(GCHandles)
     return Status;
 }
 
-BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
-{
-    // We want to follow back until we get the mt for System.Exception
-    DacpMethodTableData dmtd;
-    CLRDATA_ADDRESS walkMT = mtObj;
-    while(walkMT != NULL)
-    {
-        if (dmtd.Request(g_sos, walkMT) != S_OK)
-        {
-            break;            
-        }
-        NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);                
-        if (_wcscmp (baseString, g_mdName) == 0)
-        {
-            return TRUE;
-        }
-        walkMT = dmtd.ParentMethodTable;
-    }
-    return FALSE;
-}
-
 // This is an experimental and undocumented SOS API that attempts to step through code
 // stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
 // to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
@@ -10846,7 +11437,7 @@ DECLARE_API(StopOnException)
         {            
             NameForMT_s (taMT, g_mdName, mdNameLen);
             if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
-                (fDerived && derivedFrom(taMT, typeNameWide)))
+                (fDerived && IsDerivedFrom(taMT, typeNameWide)))
             {
                 sprintf_s(buffer,_countof (buffer),
                     "r$t%d=1",
index b9dbd628ef04c005d6815fd8819fd6773ad71f3d..1ae4e3f1545212e3660d049687b123440a012bff 100644 (file)
@@ -2205,6 +2205,65 @@ BOOL IsStringObject (size_t obj)
     return FALSE;
 }
 
+BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString)
+{
+    DacpMethodTableData dmtd;
+    CLRDATA_ADDRESS walkMT = mtObj;
+    while (walkMT != NULL)
+    {
+        if (dmtd.Request(g_sos, walkMT) != S_OK)
+        {
+            break;
+        }
+
+        NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
+        if (_wcscmp(baseString, g_mdName) == 0)
+        {
+            return TRUE;
+        }
+
+        walkMT = dmtd.ParentMethodTable;
+    }
+
+    return FALSE;
+}
+
+BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD)
+{
+    if (!sos::IsObject(delegateAddr, false))
+    {
+        return FALSE;
+    }
+
+    sos::Object delegateObj = TO_TADDR(delegateAddr);
+
+    for (int i = 0; i < 2; i++)
+    {
+        int offset;
+        if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), i == 0 ? W("_methodPtrAux") : W("_methodPtr"))) != 0)
+        {
+            CLRDATA_ADDRESS methodPtr;
+            MOVE(methodPtr, delegateObj.GetAddress() + offset);
+            if (methodPtr != NULL)
+            {
+                if (g_sos->GetMethodDescPtrFromIP(methodPtr, pMD) == S_OK)
+                {
+                    return TRUE;
+                }
+
+                DacpCodeHeaderData codeHeaderData;
+                if (codeHeaderData.Request(g_sos, methodPtr) == S_OK)
+                {
+                    *pMD = codeHeaderData.MethodDescPtr;
+                    return TRUE;
+                }
+            }
+        }
+    }
+
+    return FALSE;
+}
+
 void DumpStackObjectsOutput(const char *location, DWORD_PTR objAddr, BOOL verifyFields)
 {
     // rule out pointers that are outside of the gc heap.
@@ -5235,6 +5294,7 @@ const char * const DMLFormats[] =
     "<exec cmd=\"!DumpRCW /d %s\">%s</exec>",       // DML_RCWrapper
     "<exec cmd=\"!DumpCCW /d %s\">%s</exec>",       // DML_CCWrapper
     "<exec cmd=\"!ClrStack -i %S %d\">%S</exec>",   // DML_ManagedVar
+    "<exec cmd=\"!DumpAsync -addr %s -tasks -completed -fields -stacks -roots\">%s</exec>", // DML_Async
 };
 
 void ConvertToLower(__out_ecount(len) char *buffer, size_t len)
index e0286a21605e74a9b105e5b5c671c6738d2f66bc..94ee46d74013f10fb04923a8f669fef26a7125db 100644 (file)
@@ -356,6 +356,7 @@ namespace Output
         DML_RCWrapper,
         DML_CCWrapper,
         DML_ManagedVar,
+        DML_Async,
     };
 
     /**********************************************************************\
@@ -457,6 +458,7 @@ inline void ExtOutIndent()  { WhitespaceOut(Output::g_Indent << 2); }
 #define DMLRCWrapper(addr) Output::BuildHexValue(addr, Output::DML_RCWrapper).GetPtr()
 #define DMLCCWrapper(addr) Output::BuildHexValue(addr, Output::DML_CCWrapper).GetPtr()
 #define DMLManagedVar(expansionName,frame,simpleName) Output::BuildManagedVarValue(expansionName, frame, simpleName, Output::DML_ManagedVar).GetPtr()
+#define DMLAsync(addr) Output::BuildHexValue(addr, Output::DML_Async).GetPtr()
 
 bool IsDMLEnabled();
 
@@ -1846,6 +1848,8 @@ BOOL IsMethodTable (DWORD_PTR value);
 BOOL IsStringObject (size_t obj);
 BOOL IsObjectArray (DWORD_PTR objPointer);
 BOOL IsObjectArray (DacpObjectData *pData);
+BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString);
+BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD);
 
 /* Returns a list of all modules in the process.
  * Params:
diff --git a/src/SOS/lldbplugin/lldbplugin.vcxproj b/src/SOS/lldbplugin/lldbplugin.vcxproj
new file mode 100644 (file)
index 0000000..6150527
--- /dev/null
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|ARM">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM">
+      <Configuration>Release</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|ARM64">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM64">
+      <Configuration>Release</Configuration>
+      <Platform>ARM64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x86">
+      <Configuration>Debug</Configuration>
+      <Platform>x86</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x86">
+      <Configuration>Release</Configuration>
+      <Platform>x86</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{d52c65c4-2c7d-45e6-9f5c-6f3a96796018}</ProjectGuid>
+    <Keyword>Linux</Keyword>
+    <RootNamespace>lldbplugin</RootNamespace>
+    <MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
+    <ApplicationType>Linux</ApplicationType>
+    <ApplicationTypeRevision>1.0</ApplicationTypeRevision>
+    <TargetLinuxPlatform>Generic</TargetLinuxPlatform>
+    <LinuxProjectType>{D51BCBC9-82E9-4017-911E-C93873C4EA2B}</LinuxProjectType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets" />
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <OutDir>$(ArtifactsBinDir)Linux.$(Platform).$(Configuration)\</OutDir>
+    <IntDir>$(ArtifactsObjDir)Linux.$(Platform).$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemGroup>
+    <ClCompile Include="services.cpp" />
+    <ClCompile Include="setclrpathcommand.cpp" />
+    <ClCompile Include="setsostidcommand.cpp" />
+    <ClCompile Include="soscommand.cpp" />
+    <ClCompile Include="sosplugin.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="mstypes.h" />
+    <ClInclude Include="services.h" />
+    <ClInclude Include="sosplugin.h" />
+  </ItemGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)src\pal\prebuilt\inc;$(SolutionDir)src\inc;$(SolutionDir)src\pal\inc;$(SolutionDir)src\pal\inc\rt;$(SolutionDir)src\SOS\lldbplugin\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets" />
+</Project>
\ No newline at end of file
index 5c86dfa4c01b507c248c537fd44f689c028994c0..b2a97853f328756ce08a7541a5ad75136583815d 100644 (file)
@@ -131,6 +131,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method.");
     interpreter.AddCommand("dumpasync", new sosCommand("DumpAsync"), "Displays info about async state machines on the garbage-collected heap.");
     interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address.");
+    interpreter.AddCommand("dumpdelegate", new sosCommand("DumpDelegate"), "Displays information about a delegate.");
     interpreter.AddCommand("dumpdomain", new sosCommand("DumpDomain"), "Displays information all the AppDomains and all assemblies within the domains.");
     interpreter.AddCommand("dumpheap", new sosCommand("DumpHeap"), "Displays info about the garbage-collected heap and collection statistics about objects.");
     interpreter.AddCommand("dumpil", new sosCommand("DumpIL"), "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.");
@@ -147,6 +148,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     interpreter.AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.");
     interpreter.AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.");
     interpreter.AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address.");
+    interpreter.AddCommand("syncblk", new sosCommand("SyncBlk"), "Displays the SyncBlock holder info.");
     interpreter.AddCommand("histclear", new sosCommand("HistClear"), "Releases any resources used by the family of Hist commands.");
     interpreter.AddCommand("histinit", new sosCommand("HistInit"), "Initializes the SOS structures from the stress log saved in the debuggee.");
     interpreter.AddCommand("histobj", new sosCommand("HistObj"), "Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument.");