Merge pull request #523 from AArnott/dumpasync

Swap out C++ !dumpasync extension for C# version
This commit is contained in:
Andrew Arnott 2019-10-01 19:11:25 -06:00 коммит произвёл GitHub
Родитель e84c49eda3 0cff68347f
Коммит 5788b1aba3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
32 изменённых файлов: 958 добавлений и 1981 удалений

Просмотреть файл

@ -55,14 +55,14 @@ steps:
msbuildArgs: /t:build /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild_x86.binlog"
platform: x86
configuration: $(BuildConfiguration)
displayName: Build AsyncDebugTools x86
displayName: Build SosThreadingTools x86
- task: VSBuild@1
inputs:
msbuildArgs: /t:build /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild_x64.binlog"
platform: x64
configuration: $(BuildConfiguration)
displayName: Build AsyncDebugTools x64
displayName: Build SosThreadingTools x64
- task: DotNetCoreCLI@2
displayName: Run tests
@ -131,8 +131,8 @@ steps:
md $(Build.ArtifactStagingDirectory)/deployables\x86
md $(Build.ArtifactStagingDirectory)/deployables\x64
Get-ChildItem bin\Packages\$(BuildConfiguration)\NuGet\*.nupkg -rec |% { copy $_ $(Build.ArtifactStagingDirectory)\deployables }
copy bin\AsyncDebugTools/x86/$(BuildConfiguration)/AsyncDebugTools.dll $(Build.ArtifactStagingDirectory)\deployables\x86
copy bin\AsyncDebugTools/x64/$(BuildConfiguration)/AsyncDebugTools.dll $(Build.ArtifactStagingDirectory)\deployables\x64
copy bin\SosThreadingTools/x86/$(BuildConfiguration)/net472/SosThreadingTools.dll $(Build.ArtifactStagingDirectory)\deployables\x86
copy bin\SosThreadingTools/x64/$(BuildConfiguration)/net472/SosThreadingTools.dll $(Build.ArtifactStagingDirectory)\deployables\x64
- task: PublishBuildArtifacts@1
inputs:

Просмотреть файл

@ -8,15 +8,15 @@ for an app hang that is waiting for async methods to complete.
Using this tool requires that you download and install [WinDbg][WinDbg], which can attach to a running process or DMP file. The `!dumpasync` extension is only available for WinDbg.
The `!dumpasync` extension itself is exported from the `AsyncDebugTools.dll` library, which you can acquire from [our releases page](https://github.com/Microsoft/vs-threading/releases).
The `!dumpasync` extension itself is exported from the `SosThreadingTools.dll` library, which you can acquire from [our releases page](https://github.com/Microsoft/vs-threading/releases).
## Usage
Use WinDbg to open your DMP file or attach to your process, then execute the following commands.
Be sure to use either the x86 or x64 version of the `AsyncDebugTools.dll` library, consistent with the version of WinDbg you are running.
Be sure to use either the x86 or x64 version of the `SosThreadingTools.dll` library, consistent with the version of WinDbg you are running.
```windbg
.load path\to\AsyncDebugTools.dll
.load path\to\SosThreadingTools.dll
!dumpasync
```

Просмотреть файл

@ -1,5 +0,0 @@
EXPORTS
dumpasync
DebugExtensionNotify
DebugExtensionInitialize
DebugExtensionUninitialize

Просмотреть файл

@ -1,225 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.props" Condition="Exists('..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.props')" />
<Import Project="..\..\packages\MicroBuild.2.0.54\build\MicroBuild.props" Condition="Exists('..\..\packages\MicroBuild.2.0.54\build\MicroBuild.props')" />
<Import Project="..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.props" Condition="Exists('..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>AsyncDebugTools</RootNamespace>
<MicroBuild_DoNotStrongNameSign>true</MicroBuild_DoNotStrongNameSign>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\bin\$(MSBuildProjectName)\x86\$(Configuration)\</OutDir>
<IntDir>..\..\obj\$(MSBuildProjectName)\x86\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\bin\$(MSBuildProjectName)\$(Platform)\$(Configuration)\</OutDir>
<IntDir>..\..\obj\$(MSBuildProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\bin\$(MSBuildProjectName)\x86\$(Configuration)\</OutDir>
<IntDir>..\..\obj\$(MSBuildProjectName)\x86\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\bin\$(MSBuildProjectName)\$(Platform)\$(Configuration)\</OutDir>
<IntDir>..\..\obj\$(MSBuildProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ModuleDefinitionFile>AsyncDebugTools.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ModuleDefinitionFile>AsyncDebugTools.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>AsyncDebugTools.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>AsyncDebugTools.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="DbgExts.h" />
<ClInclude Include="DML.h" />
<ClInclude Include="Helpers.h" />
<ClInclude Include="outputcallbacks.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="ThreadPoolExhausted.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dumpasync.cpp" />
<ClCompile Include="DbgExts.cpp" />
<ClCompile Include="dllmain.cpp">
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</PrecompiledHeader>
</ClCompile>
<ClCompile Include="DML.cpp" />
<ClCompile Include="Helpers.cpp" />
<ClCompile Include="outputcallbacks.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="ThreadPoolExhausted.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="AsyncDebugTools.def" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\packages\Nerdbank.GitVersioning.2.1.23\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.1.23\build\Nerdbank.GitVersioning.targets')" />
<Import Project="..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.targets" Condition="Exists('..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.targets')" />
<Import Project="..\..\packages\MicroBuild.2.0.54\build\MicroBuild.targets" Condition="Exists('..\..\packages\MicroBuild.2.0.54\build\MicroBuild.targets')" />
<Import Project="..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.targets" Condition="Exists('..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.1.23\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.1.23\build\Nerdbank.GitVersioning.targets'))" />
<Error Condition="!Exists('..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.props'))" />
<Error Condition="!Exists('..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MicroBuild.Core.0.3.0\build\MicroBuild.Core.targets'))" />
<Error Condition="!Exists('..\..\packages\MicroBuild.2.0.54\build\MicroBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MicroBuild.2.0.54\build\MicroBuild.props'))" />
<Error Condition="!Exists('..\..\packages\MicroBuild.2.0.54\build\MicroBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MicroBuild.2.0.54\build\MicroBuild.targets'))" />
<Error Condition="!Exists('..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.props'))" />
<Error Condition="!Exists('..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MicroBuild.VisualStudio.2.0.54\build\MicroBuild.VisualStudio.targets'))" />
</Target>
</Project>

Просмотреть файл

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DbgExts.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Helpers.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="outputcallbacks.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="ThreadPoolExhausted.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="DML.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DbgExts.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Helpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="outputcallbacks.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ThreadPoolExhausted.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dumpasync.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DML.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="AsyncDebugTools.def">
<Filter>Source Files</Filter>
</None>
<None Include="packages.config" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,37 +0,0 @@
#include "stdafx.h"
BOOL PreferDML(PDEBUG_CLIENT pDebugClient)
{
BOOL bPreferDML = FALSE;
IDebugControl* pDebugControl;
if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl),
(void **)& pDebugControl)))
{
ULONG ulOptions = 0;
if (SUCCEEDED(pDebugControl->GetEngineOptions(&ulOptions)))
{
bPreferDML = (ulOptions & DEBUG_ENGOPT_PREFER_DML);
}
pDebugControl->Release();
}
return bPreferDML;
}
BOOL AbilityDML(PDEBUG_CLIENT pDebugClient)
{
BOOL bAbilityDML = FALSE;
IDebugAdvanced2* pDebugAdvanced2;
if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugAdvanced2),
(void **)& pDebugAdvanced2)))
{
HRESULT hr = 0;
if (SUCCEEDED(hr = pDebugAdvanced2->Request(
DEBUG_REQUEST_CURRENT_OUTPUT_CALLBACKS_ARE_DML_AWARE,
NULL, 0, NULL, 0, NULL)))
{
if (hr == S_OK) bAbilityDML = TRUE;
}
pDebugAdvanced2->Release();
}
return bAbilityDML;
}

Просмотреть файл

@ -1,5 +0,0 @@
#pragma once
#include "stdafx.h"
BOOL PreferDML(PDEBUG_CLIENT pDebugClient);
BOOL AbilityDML(PDEBUG_CLIENT pDebugClient);

Просмотреть файл

@ -1,40 +0,0 @@
// dbgexts.cpp
#include "stdafx.h"
#include "dbgexts.h"
extern "C" HRESULT CALLBACK
DebugExtensionInitialize(PULONG Version, PULONG Flags)
{
*Version = DEBUG_EXTENSION_VERSION(EXT_MAJOR_VER, EXT_MINOR_VER);
*Flags = 0; // Reserved for future use.
return S_OK;
}
extern "C" void CALLBACK
DebugExtensionNotify(ULONG Notify, ULONG64 Argument)
{
UNREFERENCED_PARAMETER(Argument);
switch (Notify)
{
// A debugging session is active. The session may not necessarily be suspended.
case DEBUG_NOTIFY_SESSION_ACTIVE:
break;
// No debugging session is active.
case DEBUG_NOTIFY_SESSION_INACTIVE:
break;
// The debugging session has suspended and is now accessible.
case DEBUG_NOTIFY_SESSION_ACCESSIBLE:
break;
// The debugging session has started running and is now inaccessible.
case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
break;
}
return;
}
extern "C" void CALLBACK
DebugExtensionUninitialize(void)
{
return;
}

Просмотреть файл

@ -1,7 +0,0 @@
// dbgexts.h
#include <windows.h>
#include <dbgeng.h>
#define EXT_MAJOR_VER 1
#define EXT_MINOR_VER 0

Просмотреть файл

@ -1,764 +0,0 @@
#include "stdafx.h"
#include "Helpers.h"
#include "outputcallbacks.h"
bool TryFindSOS(
_In_ IDebugClient* pDebugClient,
_Out_ std::string &strSOS)
{
strSOS.clear();
std::string strOutput;
if (SUCCEEDED(Execute(pDebugClient, ".extmatch /e *\\sos*.dll Threads", strOutput))
&& !strOutput.empty())
{
size_t pos = strOutput.find_first_of("\r\n");
if (pos != std::string::npos)
{
strOutput.erase(pos);
}
size_t dot = strOutput.rfind('.');
if (dot != std::string::npos)
{
strSOS = strOutput.substr(1, dot - 1);
}
}
return !strSOS.empty();
}
bool EnsureLoadSOS(
_In_ IDebugClient* pDebugClient,
_Out_ std::string &strSOS,
_Out_ std::string &strOutput)
{
strSOS.clear();
strOutput.clear();
if (TryFindSOS(pDebugClient, strSOS))
{
return true;
}
if (SUCCEEDED(Execute(pDebugClient, ".loadby sos.dll clr", strOutput)))
{
return TryFindSOS(pDebugClient, strSOS);
}
return false;
}
std::string GetFullCommand(
_In_ const std::string &strSOS,
_In_ const std::string &strCommand)
{
return "!" + strSOS + "." + strCommand;
}
HRESULT Execute(
_In_ IDebugClient* pDebugClient,
_In_ const std::string &strCommand,
_Out_ std::string &strOutput)
{
HRESULT hr = E_FAIL;
strOutput.clear();
CComPtr<IDebugClient> srpNewClient;
if (SUCCEEDED(pDebugClient->CreateClient(&srpNewClient)))
{
COutputCallbacks *pOutputCallbacks = new COutputCallbacks();
if (SUCCEEDED(srpNewClient->SetOutputMask(pOutputCallbacks->SupportedMask()))
&& SUCCEEDED(srpNewClient->SetOutputCallbacks(pOutputCallbacks)))
{
CComQIPtr<IDebugControl> srpNewControl(srpNewClient);
if (srpNewControl)
{
hr = srpNewControl->Execute(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED, strCommand.c_str(), DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT);
if (pOutputCallbacks->BufferError())
{
strOutput = pOutputCallbacks->BufferError();
if (SUCCEEDED(hr))
{
hr = E_FAIL; // Ensure returning failure when there is error message.
}
}
else if (pOutputCallbacks->BufferNormal())
{
strOutput = pOutputCallbacks->BufferNormal();
}
// Print the command outputs if enable verbose mode.
{
CComQIPtr<IDebugControl> srpControl(pDebugClient);
srpControl->Output(DEBUG_OUTPUT_VERBOSE, "Execute command: %s\n%s\n", strCommand.c_str(), strOutput.c_str());
}
}
srpNewClient->SetOutputCallbacks(nullptr);
}
pOutputCallbacks->Release();
}
return hr;
}
void SplitString(
_In_ const std::string &strText,
_In_ const std::string &strDelim,
_Out_ std::vector<std::string> &tokens)
{
tokens.clear();
std::string strCopyText(strText);
char *pszContext = nullptr;
char *pszToken = strtok_s(const_cast<char *>(strCopyText.c_str()), strDelim.c_str(), &pszContext);
while (pszToken != nullptr)
{
tokens.push_back(pszToken);
pszToken = strtok_s(nullptr, strDelim.c_str(), &pszContext);
}
}
static std::regex g_addressPattern("[0-9a-fA-F]{8}");
void ExtractExceptionInfosFromThreadsOutput(
_In_ const std::string &strOutput,
_Out_ std::vector<ExceptionInfo> &exceptionInfos)
{
static std::string strDelim = " ()";
exceptionInfos.clear();
std::istringstream iss(strOutput);
std::string strLine;
std::vector<std::string> tokens;
while (std::getline(iss, strLine))
{
SplitString(strLine, strDelim, tokens);
for (auto i = tokens.size() - 1; i >= 10; --i)
{
if (std::regex_match(tokens[i], g_addressPattern))
{
exceptionInfos.push_back(ExceptionInfo{tokens[0], tokens[i]});
break;
}
}
}
}
bool TryParsePrintExceptionOutput(
_In_ const std::string &strOutput,
_Out_ ExceptionDetail &detail)
{
detail = ExceptionDetail{};
std::istringstream iss(strOutput);
std::string strLine;
// Exception object
{
if (!std::getline(iss, strLine))
{
return false;
}
std::smatch match;
if (!std::regex_search(strLine, match, g_addressPattern))
{
return false;
}
detail.m_strExceptionObject = match.str();
}
// Exception type
{
if (!std::getline(iss, strLine))
{
return false;
}
size_t pos = strLine.find_last_of(" :");
if (pos == std::string::npos)
{
return false;
}
detail.m_strExceptionType = strLine.substr(pos + 1);
}
// Skip Message
if (!std::getline(iss, strLine))
{
return false;
}
// InnerException [optional]
{
if (!std::getline(iss, strLine))
{
return false;
}
std::smatch match;
if (std::regex_search(strLine, match, g_addressPattern))
{
detail.m_strInnerExceptionObject = match.str();
}
}
// StackTrace (generated)
{
if (!std::getline(iss, strLine))
{
return false;
}
// SP IP Function
if (!std::getline(iss, strLine))
{
return false;
}
size_t pos = strLine.rfind("Function");
if (pos == std::string::npos)
{
return false;
}
while (std::getline(iss, strLine) && !strLine.empty())
{
detail.m_stackTraces.push_back(strLine.substr(pos));
}
if (detail.m_stackTraces.empty())
{
return false;
}
}
// Skip StackTraceString
if (!std::getline(iss, strLine))
{
return false;
}
// HResult
{
if (!std::getline(iss, strLine))
{
return false;
}
size_t pos = strLine.find_last_of(" :");
if (pos == std::string::npos)
{
return false;
}
detail.m_strHResult = strLine.substr(pos + 1);
}
detail.m_strRawText = strOutput;
return true;
}
void CollectExceptionDetails(
_In_ IDebugClient* pDebugClient,
_In_ const std::string &strSOS,
_In_ const std::string &strExceptionObject,
_Out_ std::vector<ExceptionDetail> &exceptionDetailsIncludingInnerExceptions)
{
std::string strPrintExceptionCommand = GetFullCommand(strSOS, "PrintException");
std::string strException = strExceptionObject;
while (!strException.empty())
{
std::string strCommand = strPrintExceptionCommand + " " + strException;
strException.clear();
std::string strOutput;
if (SUCCEEDED(Execute(pDebugClient, strCommand, strOutput)))
{
ExceptionDetail detail;
if (TryParsePrintExceptionOutput(strOutput, detail))
{
exceptionDetailsIncludingInnerExceptions.push_back(detail);
strException = detail.m_strInnerExceptionObject;
}
}
}
}
static std::vector<std::string> g_immunizedSymbolsStartWithIgnoreCase =
{
"mscorlib",
"System",
"Microsoft_VisualStudio_Validation",
"Microsoft_VisualStudio_Threading",
"Microsoft_Collections_Immutable",
"Microsoft.VisualStudio.Validation",
"Microsoft.VisualStudio.Threading",
"Microsoft.Collections.Immutable",
"Microsoft.VisualStudio.Shell.VsTaskLibraryHelper",
"Microsoft.VisualStudio.Services.VsTask",
"Microsoft.VisualStudio.ProjectSystem.ThreadHandlingMultithreaded",
"Microsoft.VisualStudio.ProjectSystem.VS.HResult",
"Microsoft.VisualStudio.Project.VisualC.VCProjectEngine.ApartmentMarshaler.Invoke",
};
static std::vector<std::string> g_immunizedSymbolsContain =
{
"ErrorUtilities.",
"ErrorHandler.",
"ThrowOnFailure",
"ThrowIfNotOnUIThread",
"HrInvoke",
};
bool IsImmunized(_In_ const std::string &strFrame)
{
for (const std::string &strImmunizedSymbolStartWithIgnoreCase : g_immunizedSymbolsStartWithIgnoreCase)
{
if (strFrame.length() > strImmunizedSymbolStartWithIgnoreCase.length()
&& 0 == _strnicmp(strFrame.c_str(), strImmunizedSymbolStartWithIgnoreCase.c_str(), strImmunizedSymbolStartWithIgnoreCase.length()))
{
return true;
}
}
for (const std::string &strImmunizedSymbolsContain : g_immunizedSymbolsContain)
{
if (strFrame.find(strImmunizedSymbolsContain) != std::string::npos)
{
return true;
}
}
return false;
}
std::string GetSymbolFromFrame(_In_ const std::string &strFrame)
{
size_t pos = strFrame.find('(');
std::string strSymbol = strFrame.substr(0, pos);
// Normalize NGENed image name.
pos = strSymbol.find("_ni!");
if (pos != std::string::npos)
{
strSymbol.erase(pos, 3);
}
return strSymbol;
}
std::string GuessBlamedSymbol(
_In_ const ExceptionDetail &detail)
{
for (const std::string &strFrame : detail.m_stackTraces)
{
if (!IsImmunized(strFrame))
{
return GetSymbolFromFrame(strFrame);
}
}
// Otherwise, fallback to the last frame.
return GetSymbolFromFrame(detail.m_stackTraces.back());
}
bool IsDueToHangDetected(
_In_ const ExceptionDetail &detail)
{
for (const std::string &stack : detail.m_stackTraces)
{
if (stack.find("OnHangDetected") != std::string::npos)
{
return true;
}
}
return false;
}
static std::regex g_framePattern("^[0-9a-f]{8} [0-9a-f]{8} ([a-z].+)", std::regex_constants::icase);
bool ParseClrStackFrame(
_In_ const std::string &strFrame,
_Out_ std::string &strFunc)
{
std::smatch matches;
if (std::regex_match(strFrame, matches, g_framePattern))
{
strFunc = matches[1].str();
return true;
}
return false;
}
bool ParseKStackFrame(
_In_ const std::string &strFrame,
_Out_ std::string &strFunc)
{
static std::regex s_funcPattern("[0-9a-fA-F-]{8} [0-9a-fA-F-]{8} (.+!.+\\+0x[0-9a-fA-F]+)");
std::smatch match;
if (std::regex_search(strFrame, match, s_funcPattern))
{
strFunc = match[1].str();
return true;
}
return false;
}
/* !threadpool
CPU utilization: 3%
Worker Thread: Total: 129 Running: 110 Idle: 0 MaxLimit: 2047 MinLimit: 12
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 4 Free: 4 MaxFree: 24 CurrentLimit: 4 MaxLimit: 1000 MinLimit: 12
*/
bool GetThreadPoolStatus(
_In_ const std::string &strThreadPoolOutput,
_Out_ ThreadPoolStatus &status)
{
static std::regex s_workerThreadPattern("^Worker Thread: Total: (\\d+) Running: (\\d+)", std::regex::icase);
std::stringstream ss(strThreadPoolOutput);
std::string strLine;
while (std::getline(ss, strLine))
{
std::smatch match;
if (std::regex_search(strLine, match, s_workerThreadPattern))
{
status.m_iRunningWorkers = std::atoi(match[2].str().c_str());
return true;
}
}
return false;
}
int GetNumberOfThreads(
_In_ PDEBUG_CLIENT pDebugClient)
{
static std::regex s_threadIdPattern("^\\s*(\\d+)\\s*Id:", std::regex::icase);
std::string strOutput;
if (SUCCEEDED(Execute(pDebugClient, "~*", strOutput)))
{
std::stringstream ss(strOutput);
std::string strLine;
int threadId = -1;
while (std::getline(ss, strLine))
{
std::smatch match;
if (std::regex_search(strLine, match, s_threadIdPattern))
{
threadId = std::atoi(match[1].str().c_str());
}
}
return threadId + 1;
}
return 0;
}
struct FieldHeaderInfo
{
std::string m_strName;
size_t m_iStart;
size_t m_iLength;
};
bool ParseFieldHeaders(
_In_ const std::string &strHeaders,
_Out_ std::vector<FieldHeaderInfo> &headers)
{
static std::regex s_fieldHeaderPattern("^\\s+(\\S+)");
headers.clear();
std::smatch match;
std::string::const_iterator begin = strHeaders.begin();
while (std::regex_search(begin, strHeaders.end(), match, s_fieldHeaderPattern))
{
FieldHeaderInfo header{};
header.m_strName.assign(match[1].first, match[1].second);
header.m_iStart = begin - strHeaders.begin();
header.m_iLength = match.length();
headers.push_back(std::move(header));
begin += match.length();
}
if (!headers.empty())
{
headers.back().m_iLength = std::string::npos;
}
return headers.size() == 8;
}
void Trim(_Inout_ std::string &strText)
{
size_t start = strText.find_first_not_of(' ');
if (start == std::string::npos)
{
strText.clear();
return;
}
size_t end = strText.find_last_not_of(' ');
if (end - start + 1 < strText.length())
{
strText.erase(end + 1);
strText.erase(0, start);
}
}
void SetFieldValue(
_Inout_ FieldInfo &field,
_In_ const std::string &strName,
_Inout_ std::string &&strValue)
{
Trim(strValue);
if (strName == "MT")
{
field.m_strMethodTable = strValue;
}
else if (strName == "Offset")
{
field.m_iOffset = atoi(strValue.c_str());
}
else if (strName == "VT")
{
field.m_fIsValueType = atoi(strValue.c_str()) != 0;
}
else if (strName == "Value")
{
field.m_strValue = strValue;
}
else if (strName == "Name")
{
field.m_strName = strValue;
}
}
/* !do 032f2398
Name: Microsoft.VisualStudio.Services.Settings.SettingsPackage+<ShellInitPlusFiveSeconds>d__1
MethodTable: 6f6a03fc
EEClass: 6f58da24
Size: 32(0x20) bytes
File: C:\windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualStudio.Shell.UI.Internal\v4.0_14.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Shell.UI.Internal.dll
Fields:
MT Field Offset Type VT Attr Value Name
6cc19158 4001249 8 System.Int32 1 instance 1 <>1__state
6cc223fc 400124a c ...TaskMethodBuilder 1 instance 032f23a4 <>t__builder
6cbe4fd0 400124b 18 ...olean, mscorlib]] 1 instance 032f23b0 <>u__1
6cc17670 400124c 4 System.Object 0 instance 0a4862f4 <>u__2
*/
bool ParseDumpObjectOutput(
_In_ const std::string &strDumpObject,
_Out_ ObjectInfo &objectInfo)
{
objectInfo.m_strType.clear();
objectInfo.m_fields.clear();
std::stringstream ss(strDumpObject);
std::string strLine;
bool fFoundFields = false;
while (std::getline(ss, strLine))
{
size_t sep = strLine.find(':');
if (sep != std::string::npos)
{
if (strncmp(strLine.c_str(), "Name", sep) == 0)
{
size_t start = strLine.find_first_not_of(' ', sep + 1);
if (start != std::string::npos)
{
objectInfo.m_strType.assign(strLine, start);
}
}
if (strncmp(strLine.c_str(), "String", sep) == 0)
{
size_t start = strLine.find_first_not_of(' ', sep + 1);
if (start != std::string::npos)
{
objectInfo.m_string.assign(strLine, start);
}
}
else if (strncmp(strLine.c_str(), "Fields", sep) == 0)
{
fFoundFields = true;
break;
}
}
}
if (fFoundFields && !objectInfo.m_strType.empty())
{
if (std::getline(ss, strLine))
{
std::vector<FieldHeaderInfo> headers;
if (ParseFieldHeaders(strLine, headers))
{
while (std::getline(ss, strLine))
{
if (strLine.empty() || isspace(strLine[0]) || (int)strLine.length() <= headers.back().m_iStart )
{
continue;
}
int start = 0;
int end = 0;
int fieldIndex = 0;
FieldInfo fieldInfo{};
for (const FieldHeaderInfo &headerInfo : headers)
{
// SOS doesn't align all fields with head correctly, causing the code reads wrong values and names.
if (fieldIndex < 6)
{
std::string strValue(strLine, headerInfo.m_iStart, headerInfo.m_iLength);
SetFieldValue(fieldInfo, headerInfo.m_strName, std::move(strValue));
start = headerInfo.m_iStart + headerInfo.m_iLength;
}
else
{
while (start < (int)strLine.length() && isspace(strLine[start]))
{
start++;
}
end = start;
while (end < (int)strLine.length() && !isspace(strLine[end]))
{
end++;
}
std::string strValue(strLine, start, end - start);
SetFieldValue(fieldInfo, headerInfo.m_strName, std::move(strValue));
start = end;
}
fieldIndex++;
}
objectInfo.m_fields.push_back(std::move(fieldInfo));
}
}
}
}
return !objectInfo.m_strType.empty() && !objectInfo.m_fields.empty();
}
/* !name2ee mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
Module: 6c7f1000
Assembly: mscorlib.dll
Token: 020008be
MethodTable: 6cbe4dbc
EEClass: 6c855780
Name: System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
*/
bool ParseName2EEOutput(
_In_ const std::string &strName2EE,
_Out_ std::string &strMethodTable)
{
std::stringstream ss(strName2EE);
std::string strLine;
while (std::getline(ss, strLine))
{
size_t sep = strLine.find(':');
if (sep != std::string::npos)
{
size_t start = strLine.find_first_not_of(' ', sep + 1);
if (start != std::string::npos)
{
if (strncmp(strLine.c_str(), "MethodTable", sep) == 0)
{
strMethodTable.assign(strLine, start);
return true;
}
}
}
}
return false;
}
bool GetFieldInfo(
_In_ const ObjectInfo &objectInfo,
_In_ const std::string &strFieldName,
_Out_ FieldInfo &fieldInfo)
{
for (const FieldInfo &fi: objectInfo.m_fields)
{
if (fi.m_strName == strFieldName)
{
fieldInfo = fi;
return true;
}
}
return false;
}
bool FindFieldAndGetObjectInfo(
_In_ PDEBUG_CLIENT pDebugClient,
_In_ const std::string &strSOS,
_In_ const ObjectInfo &objectInfo,
_In_ const std::string &strFieldName,
_Out_ ObjectInfo &fieldObjectInfo)
{
FieldInfo fieldInfo{};
if (!GetFieldInfo(objectInfo, strFieldName, fieldInfo) || fieldInfo.m_strValue == "00000000")
{
return false;
}
std::string strCommand;
if (fieldInfo.m_fIsValueType)
{
if (fieldInfo.m_strMethodTable == "00000000")
{
return false;
}
strCommand.append("dumpvc ")
.append(fieldInfo.m_strMethodTable)
.append(" ")
.append(fieldInfo.m_strValue);
}
else
{
strCommand.append("do ")
.append(fieldInfo.m_strValue);
}
std::string strOutput;
HRESULT hr = Execute(pDebugClient, GetFullCommand(strSOS, strCommand), strOutput);
if (FAILED(hr))
{
CComQIPtr<IDebugControl> srpControl(pDebugClient);
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to run !%s: %s\n", strCommand.c_str(), strOutput.c_str());
return false;
}
if (!ParseDumpObjectOutput(strOutput, fieldObjectInfo))
{
CComQIPtr<IDebugControl> srpControl(pDebugClient);
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to parse the output of !%s: %s\n", strCommand.c_str(), strOutput.c_str());
return false;
}
fieldObjectInfo.m_strAddress = fieldInfo.m_strValue;
return true;
}

Просмотреть файл

@ -1,112 +0,0 @@
#pragma once
#include "stdafx.h"
struct ExceptionInfo
{
std::string m_strThreadId;
std::string m_strExceptionObject;
};
struct ExceptionDetail
{
std::string m_strExceptionObject;
std::string m_strExceptionType;
std::string m_strInnerExceptionObject;
std::vector<std::string> m_stackTraces;
std::string m_strHResult;
std::string m_strRawText;
};
struct ThreadPoolStatus
{
int m_iRunningWorkers = 0;
};
struct FieldInfo
{
std::string m_strMethodTable;
int m_iOffset;
bool m_fIsValueType;
std::string m_strValue;
std::string m_strName;
};
struct ObjectInfo
{
std::string m_strAddress;
std::string m_strType;
std::string m_string;
std::vector<FieldInfo> m_fields;
};
bool EnsureLoadSOS(
_In_ IDebugClient* pDebugClient,
_Out_ std::string &strSOS,
_Out_ std::string &strOutput);
std::string GetFullCommand(
_In_ const std::string &strSOS,
_In_ const std::string &strCommand);
HRESULT Execute(
_In_ IDebugClient* pDebugClient,
_In_ const std::string &strCommand,
_Out_ std::string &strOutput);
void ExtractExceptionInfosFromThreadsOutput(
_In_ const std::string &strOutput,
_Out_ std::vector<ExceptionInfo> &exceptionInfos);
void CollectExceptionDetails(
_In_ IDebugClient* pDebugClient,
_In_ const std::string &strSOS,
_In_ const std::string &strExceptionObject,
_Out_ std::vector<ExceptionDetail> &exceptionDetailsIncludingInnerExceptions);
std::string GuessBlamedSymbol(
_In_ const ExceptionDetail &detail);
bool IsDueToHangDetected(
_In_ const ExceptionDetail &detail);
bool ParseClrStackFrame(
_In_ const std::string &strFrame,
_Out_ std::string &strFunc);
bool ParseKStackFrame(
_In_ const std::string &strFrame,
_Out_ std::string &strFunc);
bool IsImmunized(
_In_ const std::string &strFrame);
std::string GetSymbolFromFrame(
_In_ const std::string &strFrame);
bool GetThreadPoolStatus(
_In_ const std::string &strThreadPoolOutput,
_Out_ ThreadPoolStatus &status);
int GetNumberOfThreads(
_In_ PDEBUG_CLIENT pDebugClient);
bool ParseDumpObjectOutput(
_In_ const std::string &strDumpObject,
_Out_ ObjectInfo &objectInfo);
bool ParseName2EEOutput(
_In_ const std::string &strName2EE,
_Out_ std::string &strMethodTable);
bool GetFieldInfo(
_In_ const ObjectInfo &objectInfo,
_In_ const std::string &strFieldName,
_Out_ FieldInfo &fieldInfo);
bool FindFieldAndGetObjectInfo(
_In_ PDEBUG_CLIENT pDebugClient,
_In_ const std::string &strSOS,
_In_ const ObjectInfo &objectInfo,
_In_ const std::string &strFieldName,
_Out_ ObjectInfo &fieldObjectInfo);

Просмотреть файл

@ -1,143 +0,0 @@
#include "stdafx.h"
#include "dbgexts.h"
#include "Helpers.h"
static int s_iThresholdToDetectThreadPoolExhausted = 20;
static int s_iThresholdToBlameCallStack = 10;
struct Thread
{
int m_id;
std::vector<std::string> m_frames;
};
struct LessThanFrames
{
bool operator() (const std::vector<std::string> *pLeft, const std::vector<std::string> *pRight) const
{
if (pLeft->size() != pRight->size())
{
return pLeft->size() < pRight->size();
}
for (size_t i = 0; i < pLeft->size(); ++i)
{
int result = pLeft->at(i).compare(pRight->at(i));
if (result != 0)
{
return result < 0;
}
}
return false;
}
};
bool IsThreadPoolExhausted(
PDEBUG_CLIENT pDebugClient,
const std::string &strSOS)
{
// !ThreadPool
std::string strOutput;
ThreadPoolStatus threadPoolStatus;
if (SUCCEEDED(Execute(pDebugClient, GetFullCommand(strSOS, "ThreadPool"), strOutput))
&& GetThreadPoolStatus(strOutput, threadPoolStatus)
&& threadPoolStatus.m_iRunningWorkers >= s_iThresholdToDetectThreadPoolExhausted)
{
return true;
}
return false;
}
bool IsClrThreadPoolWorkingThread(const std::string &strOutput)
{
std::stringstream ss(strOutput);
std::string strLine;
while (std::getline(ss, strLine))
{
if (strLine.find("clr!ThreadpoolMgr::ExecuteWorkRequest") != std::string::npos)
{
return true;
}
}
return false;
}
HRESULT OnThreadPoolExhausted(
PDEBUG_CLIENT pDebugClient,
const std::string &strSOS)
{
CComQIPtr<IDebugControl> srpControl(pDebugClient);
HRESULT hr = S_OK;
int iThreads = GetNumberOfThreads(pDebugClient);
std::string strOutput;
std::vector<Thread> threads;
threads.reserve(iThreads);
for (int i = 1; i < iThreads; ++i) // Skip the main thread
{
char szCommand[0x20] = { 0 };
if (sprintf_s(szCommand, "~%dk", i) > 0
&& SUCCEEDED(Execute(pDebugClient, szCommand, strOutput))
&& IsClrThreadPoolWorkingThread(strOutput))
{
std::vector<std::string> frames;
std::stringstream ss(strOutput);
std::string strLine;
std::string strFunc;
while (std::getline(ss, strLine))
{
if (ParseKStackFrame(strLine, strFunc))
{
frames.push_back(strFunc);
}
}
if (!frames.empty())
{
threads.push_back({ i, std::move(frames) });
}
}
}
for (const Thread &thread : threads)
{
srpControl->Output(DEBUG_OUTPUT_VERBOSE, "%d\n", thread.m_id);
for (const std::string &strFrame : thread.m_frames)
{
srpControl->Output(DEBUG_OUTPUT_VERBOSE, "\t%s\n", strFrame.c_str());
}
}
// Group the threads by the frames
std::map<const std::vector<std::string> *, int, LessThanFrames> groups;
for (const Thread &thread : threads)
{
const std::vector<std::string> *pKey = &thread.m_frames;
++groups[pKey];
}
// Sort by count of the threads
std::vector<std::pair<const std::vector<std::string> *, int>> items(groups.size());
std::copy(groups.cbegin(), groups.cend(), items.begin());
std::sort(items.begin(), items.end(), [](const auto &x, const auto &y) { return x.second > y.second; });
for (const auto &item : items)
{
if (item.second >= s_iThresholdToBlameCallStack)
{
srpControl->Output(DEBUG_OUTPUT_NORMAL, "Detected thread pool exhausted due to this callstack: (%d threads)\n", item.second);
for (const std::string &strFrame : *item.first)
{
srpControl->Output(DEBUG_OUTPUT_NORMAL, "\t%s\n", strFrame.c_str());
}
srpControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
}
}
return hr;
}

Просмотреть файл

@ -1,11 +0,0 @@
#pragma once
#include "stdafx.h"
bool IsThreadPoolExhausted(
PDEBUG_CLIENT pDebugClient,
const std::string &strSOS);
HRESULT OnThreadPoolExhausted(
PDEBUG_CLIENT pDebugClient,
const std::string &strSOS);

Просмотреть файл

@ -1,18 +0,0 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

Просмотреть файл

@ -1,325 +0,0 @@
#include "stdafx.h"
#include "dbgexts.h"
#include "Helpers.h"
#include "ThreadPoolExhausted.h"
#include "DML.h"
#include <memory>
struct StateMachineNode
{
ObjectInfo m_objectInfo;
StateMachineNode *m_pNext;
StateMachineNode *m_pPrevious;
int m_state;
int m_iDepth;
std::string m_task;
};
bool GetContinuation(
_In_ PDEBUG_CLIENT pDebugClient,
_In_ const std::string &strSOS,
_In_ const ObjectInfo &stateMachine,
_Out_ std::string &strContinuation);
HRESULT CALLBACK
dumpasync(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
UNREFERENCED_PARAMETER(args);
CComQIPtr<IDebugControl> srpControl(pDebugClient);
std::string strSOS;
std::string strOutput;
if (!EnsureLoadSOS(pDebugClient, strSOS, strOutput))
{
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to load SOS.dll: %s\n", strOutput.c_str());
return E_FAIL;
}
/*
if (IsThreadPoolExhausted(pDebugClient, strSOS))
{
return OnThreadPoolExhausted(pDebugClient, strSOS);
}
*/
std::string strAsyncMethodBuilderModules[] = {
"mscorlib.dll", // .NET Framework
"System.Private.CoreLib.dll", // CoreCLR
};
HRESULT hr;
std::string strMethodTable;
for (int i = 0; i < 2; i++)
{
hr = Execute(pDebugClient, GetFullCommand(strSOS, "name2ee " + strAsyncMethodBuilderModules[i] + "!System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner"), strOutput);
if (FAILED(hr))
{
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to run !name2ee: %s\n", strOutput.c_str());
return hr;
}
if (ParseName2EEOutput(strOutput, strMethodTable))
{
break;
}
}
if (strMethodTable.empty())
{
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to parse the output of !name2ee: %s\n", strOutput.c_str());
return E_FAIL;
}
hr = Execute(pDebugClient, GetFullCommand(strSOS, "dumpheap -short -mt " + strMethodTable), strOutput);
if (FAILED(hr))
{
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to run !dumpheap: %s\n", strOutput.c_str());
return hr;
}
// Extract the inner m_stateMachine.
std::stringstream ss(strOutput);
std::string strObject;
ObjectInfo objectInfo{};
FieldInfo fieldInfo{};
std::vector<std::unique_ptr<StateMachineNode>> stateMachineNodes;
while (std::getline(ss, strObject))
{
// TODO: avoid calling !do 00000000 which will always error out.
hr = Execute(pDebugClient, GetFullCommand(strSOS, "do " + strObject), strOutput);
if (FAILED(hr))
{
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to run !do: %s\n", strOutput.c_str());
continue;
}
if (!ParseDumpObjectOutput(strOutput, objectInfo))
{
srpControl->Output(DEBUG_OUTPUT_ERROR, "Failed to parse the output of !do: %s\n", strOutput.c_str());
continue;
}
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, objectInfo, "m_stateMachine", objectInfo))
{
if (GetFieldInfo(objectInfo, "<>1__state", fieldInfo)
&& atoi(fieldInfo.m_strValue.c_str()) >= -1)
{
auto found = std::find_if(stateMachineNodes.begin(), stateMachineNodes.end(), [&](const std::unique_ptr<StateMachineNode> &node)
{
return node->m_objectInfo.m_strAddress == objectInfo.m_strAddress;
});
// state machine can be reported more than once...
if (found != stateMachineNodes.end())
{
continue;
}
stateMachineNodes.push_back(std::make_unique<StateMachineNode>());
stateMachineNodes.back()->m_objectInfo = std::move(objectInfo);
stateMachineNodes.back()->m_state = atoi(fieldInfo.m_strValue.c_str());
ObjectInfo asyncBuilder{};
FieldInfo taskFieldInfo{};
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, stateMachineNodes.back()->m_objectInfo, "<>t__builder", asyncBuilder))
{
ObjectInfo taskObject{};
while (!GetFieldInfo(asyncBuilder, "m_task", taskFieldInfo)
&& FindFieldAndGetObjectInfo(pDebugClient, strSOS, asyncBuilder, "m_builder", asyncBuilder));
if (!taskFieldInfo.m_strValue.empty())
{
stateMachineNodes.back()->m_task = taskFieldInfo.m_strValue;
}
}
}
}
}
// Link the state machines via the continuation.
std::string strContinuation;
for (size_t i = 0; i < stateMachineNodes.size(); ++i)
{
if (GetContinuation(pDebugClient, strSOS, stateMachineNodes[i]->m_objectInfo, strContinuation))
{
auto found = std::find_if(stateMachineNodes.begin(), stateMachineNodes.end(), [&](const std::unique_ptr<StateMachineNode> &node)
{
return node->m_objectInfo.m_strAddress == strContinuation;
});
if (found != stateMachineNodes.end())
{
StateMachineNode *pContinuation = (*found).get();
stateMachineNodes[i]->m_pNext = pContinuation;
pContinuation->m_pPrevious = (stateMachineNodes[i]).get();
}
}
}
// Fix Joinable Task chains
for (std::unique_ptr<StateMachineNode> &node : stateMachineNodes)
{
if (node->m_pPrevious == nullptr)
{
ObjectInfo joinableTask{};
FieldInfo wrappedTask{};
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, node->m_objectInfo, "<>4__this", joinableTask)
&& GetFieldInfo(joinableTask, "wrappedTask", wrappedTask))
{
std::string task = wrappedTask.m_strValue;
auto found = std::find_if(stateMachineNodes.begin(), stateMachineNodes.end(), [&](const std::unique_ptr<StateMachineNode> &node)
{
return node->m_task == task;
});
if (found != stateMachineNodes.end())
{
StateMachineNode *pPrevious = (*found).get();
node->m_pPrevious = pPrevious;
pPrevious->m_pNext = node.get();
}
}
}
}
// Compute the depth
for (std::unique_ptr<StateMachineNode> &node : stateMachineNodes)
{
node->m_iDepth = 0;
if (node->m_pPrevious == nullptr)
{
for (const StateMachineNode *p = node.get(); p != nullptr; p = p->m_pNext)
{
node->m_iDepth++;
}
}
}
// Sort by depth DESC
std::sort(stateMachineNodes.begin(), stateMachineNodes.end(), [](const std::unique_ptr<StateMachineNode> &x, const std::unique_ptr<StateMachineNode> &y)
{
return x->m_iDepth > y->m_iDepth;
});
bool fOutputDML = PreferDML(pDebugClient) && AbilityDML(pDebugClient);
// Dump the state machines
for (const std::unique_ptr<StateMachineNode> &node : stateMachineNodes)
{
if (node->m_iDepth > 0)
{
std::string strIndent;
for (const StateMachineNode *p = node.get(); p != nullptr; p = p->m_pNext)
{
if (fOutputDML)
{
srpControl->Output(DEBUG_OUTPUT_NORMAL, strIndent.c_str());
srpControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!do %s\">%s</link>",
p->m_objectInfo.m_strAddress.c_str(),
p->m_objectInfo.m_strAddress.c_str());
srpControl->Output(DEBUG_OUTPUT_NORMAL, " <%d> %s\n", p->m_state, p->m_objectInfo.m_strType.c_str());
}
else
{
srpControl->Output(DEBUG_OUTPUT_NORMAL, "%s%s <%d> %s\n", strIndent.c_str(), p->m_objectInfo.m_strAddress.c_str(), p->m_state, p->m_objectInfo.m_strType.c_str());
}
strIndent.append(".");
}
if (node->m_iDepth > 1)
{
srpControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
}
}
}
return S_OK;
}
bool GetContinuationFromTarget(
_In_ PDEBUG_CLIENT pDebugClient,
_In_ const std::string &strSOS,
_In_ const ObjectInfo &targetObject,
_Out_ std::string &strContinuation)
{
FieldInfo stateMachineField{};
if (GetFieldInfo(targetObject, "m_stateMachine", stateMachineField))
{
strContinuation = stateMachineField.m_strValue;
return true;
}
ObjectInfo nextContinuationObject{};
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, targetObject, "m_continuation", nextContinuationObject))
{
ObjectInfo targetObject{};
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, nextContinuationObject, "_target", targetObject)
&& GetContinuationFromTarget(pDebugClient, strSOS, targetObject, strContinuation))
{
return true;
}
}
return false;
}
bool GetContinuation(
_In_ PDEBUG_CLIENT pDebugClient,
_In_ const std::string &strSOS,
_In_ const ObjectInfo &stateMachine,
_Out_ std::string &strContinuation)
{
ObjectInfo builderObject{};
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, stateMachine, "<>t__builder", builderObject))
{
ObjectInfo taskObject{};
while (!FindFieldAndGetObjectInfo(pDebugClient, strSOS, builderObject, "m_task", taskObject)
&& FindFieldAndGetObjectInfo(pDebugClient, strSOS, builderObject, "m_builder", builderObject));
if (!taskObject.m_strType.empty())
{
ObjectInfo continuationObject{};
ObjectInfo actionObject{};
ObjectInfo targetObject{};
while (FindFieldAndGetObjectInfo(pDebugClient, strSOS, taskObject, "m_continuationObject", continuationObject))
{
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, continuationObject, "m_action", actionObject)
&& FindFieldAndGetObjectInfo(pDebugClient, strSOS, actionObject, "_target", targetObject))
{
if (GetContinuationFromTarget(pDebugClient, strSOS, targetObject, strContinuation))
{
return true;
}
}
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, continuationObject, "_target", targetObject))
{
if (GetContinuationFromTarget(pDebugClient, strSOS, targetObject, strContinuation))
{
return true;
}
}
ObjectInfo continuationTaskObject{};
ObjectInfo stateObject{};
if (FindFieldAndGetObjectInfo(pDebugClient, strSOS, continuationObject, "m_task", continuationTaskObject)
&& FindFieldAndGetObjectInfo(pDebugClient, strSOS, continuationTaskObject, "m_stateObject", stateObject)
&& FindFieldAndGetObjectInfo(pDebugClient, strSOS, stateObject, "_target", targetObject))
{
if (GetContinuationFromTarget(pDebugClient, strSOS, targetObject, strContinuation))
{
return true;
}
}
taskObject = continuationObject;
}
}
}
return false;
}

Просмотреть файл

@ -1,101 +0,0 @@
#include "stdafx.h"
#include "dbgexts.h"
#include "outputcallbacks.h"
#define MAX_OUTPUTCALLBACKS_BUFFER 0x1000000 // 1Mb
#define MAX_OUTPUTCALLBACKS_LENGTH 0x0FFFFFF // 1Mb - 1
STDMETHODIMP COutputCallbacks::QueryInterface(__in REFIID InterfaceId, __out PVOID* Interface)
{
*Interface = NULL;
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
{
*Interface = (IDebugOutputCallbacks *)this;
InterlockedIncrement(&m_ref);
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) COutputCallbacks::AddRef()
{
return InterlockedIncrement(&m_ref);
}
STDMETHODIMP_(ULONG) COutputCallbacks::Release()
{
if (InterlockedDecrement(&m_ref) == 0)
{
delete this;
return 0;
}
return m_ref;
}
STDMETHODIMP COutputCallbacks::Output(__in ULONG Mask, __in PCSTR Text)
{
if ((Mask & DEBUG_OUTPUT_NORMAL) == DEBUG_OUTPUT_NORMAL)
{
if (m_pBufferNormal == NULL)
{
m_nBufferNormal = 0;
m_pBufferNormal = (PCHAR)malloc(sizeof(CHAR)*(MAX_OUTPUTCALLBACKS_BUFFER));
if (m_pBufferNormal == NULL) return E_OUTOFMEMORY;
m_pBufferNormal[0] = '\0';
m_pBufferNormal[MAX_OUTPUTCALLBACKS_LENGTH] = '\0';
}
size_t len = strlen(Text);
if (len > (MAX_OUTPUTCALLBACKS_LENGTH-m_nBufferNormal))
{
len = MAX_OUTPUTCALLBACKS_LENGTH-m_nBufferNormal;
}
if (len > 0)
{
memcpy(&m_pBufferNormal[m_nBufferNormal], Text, len);
m_nBufferNormal += len;
m_pBufferNormal[m_nBufferNormal] = '\0';
}
}
if ((Mask & DEBUG_OUTPUT_ERROR) == DEBUG_OUTPUT_ERROR)
{
if (m_pBufferError == NULL)
{
m_nBufferError = 0;
m_pBufferError = (PCHAR)malloc(sizeof(CHAR)*(MAX_OUTPUTCALLBACKS_BUFFER));
if (m_pBufferError == NULL) return E_OUTOFMEMORY;
m_pBufferError[0] = '\0';
m_pBufferError[MAX_OUTPUTCALLBACKS_LENGTH] = '\0';
}
size_t len = strlen(Text);
if (len >= (MAX_OUTPUTCALLBACKS_LENGTH-m_nBufferError))
{
len = MAX_OUTPUTCALLBACKS_LENGTH-m_nBufferError;
}
if (len > 0)
{
memcpy(&m_pBufferError[m_nBufferError], Text, len);
m_nBufferError += len;
m_pBufferError[m_nBufferError] = '\0';
}
}
return S_OK;
}
void COutputCallbacks::Clear()
{
if (m_pBufferNormal)
{
free(m_pBufferNormal);
m_pBufferNormal = NULL;
m_nBufferNormal = 0;
}
if (m_pBufferError)
{
free(m_pBufferError);
m_pBufferError = NULL;
m_nBufferError = 0;
}
}

Просмотреть файл

@ -1,45 +0,0 @@
#ifndef __OUTPUTCALLBACKS_H__
#define __OUTPUTCALLBACKS_H__
#include "dbgexts.h"
class COutputCallbacks : public IDebugOutputCallbacks
{
private:
long m_ref;
PCHAR m_pBufferNormal;
size_t m_nBufferNormal;
PCHAR m_pBufferError;
size_t m_nBufferError;
public:
COutputCallbacks()
{
m_ref = 1;
m_pBufferNormal = NULL;
m_nBufferNormal = 0;
m_pBufferError = NULL;
m_nBufferError = 0;
}
~COutputCallbacks()
{
Clear();
}
// IUnknown
STDMETHOD(QueryInterface)(__in REFIID InterfaceId, __out PVOID* Interface);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IDebugOutputCallbacks
STDMETHOD(Output)(__in ULONG Mask, __in PCSTR Text);
// Helpers
ULONG SupportedMask() { return DEBUG_OUTPUT_NORMAL | DEBUG_OUTPUT_ERROR; }
PCHAR BufferNormal() { return m_pBufferNormal; }
PCHAR BufferError() { return m_pBufferError; }
void Clear();
};
#endif // __OUTPUTCALLBACKS_H__

Просмотреть файл

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MicroBuild" version="2.0.54" targetFramework="native" developmentDependency="true" />
<package id="MicroBuild.Core" version="0.3.0" targetFramework="native" developmentDependency="true" />
<package id="MicroBuild.VisualStudio" version="2.0.54" targetFramework="native" developmentDependency="true" />
<package id="Nerdbank.GitVersioning" version="2.1.23" targetFramework="native" developmentDependency="true" />
</packages>

Просмотреть файл

@ -1,8 +0,0 @@
// stdafx.cpp : source file that includes just the standard includes
// WinDbgExt1.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

Просмотреть файл

@ -1,26 +0,0 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// TODO: reference additional headers your program requires here
#include <DbgEng.h>
#include <string>
#include <vector>
#include <sstream>
#include <regex>
#include <array>
#include <map>
#include <list>
#include <atlbase.h>
#include <atlstr.h>

Просмотреть файл

@ -1,8 +0,0 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>

Просмотреть файл

@ -27,10 +27,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Thre
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IsolatedTestHost", "IsolatedTestHost\IsolatedTestHost.csproj", "{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncDebugTools", "AsyncDebugTools\AsyncDebugTools.vcxproj", "{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes", "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes\Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj", "{3BDB8F46-A39C-422B-8B0E-89E98B83073F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SosThreadingTools", "SosThreadingTools\SosThreadingTools.csproj", "{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -89,16 +89,6 @@ Global
{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}.Release|Any CPU.Build.0 = Release|Any CPU
{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}.Release|x64.ActiveCfg = Release|Any CPU
{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}.Release|x86.ActiveCfg = Release|Any CPU
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Debug|Any CPU.ActiveCfg = Debug|Win32
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Debug|x64.ActiveCfg = Debug|x64
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Debug|x64.Build.0 = Debug|x64
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Debug|x86.ActiveCfg = Debug|Win32
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Debug|x86.Build.0 = Debug|Win32
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Release|Any CPU.ActiveCfg = Release|Win32
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Release|x64.ActiveCfg = Release|x64
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Release|x64.Build.0 = Release|x64
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Release|x86.ActiveCfg = Release|Win32
{CF348EC1-1E7D-47DE-8431-8C61D5CAF924}.Release|x86.Build.0 = Release|Win32
{3BDB8F46-A39C-422B-8B0E-89E98B83073F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BDB8F46-A39C-422B-8B0E-89E98B83073F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BDB8F46-A39C-422B-8B0E-89E98B83073F}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -107,6 +97,16 @@ Global
{3BDB8F46-A39C-422B-8B0E-89E98B83073F}.Release|Any CPU.Build.0 = Release|Any CPU
{3BDB8F46-A39C-422B-8B0E-89E98B83073F}.Release|x64.ActiveCfg = Release|Any CPU
{3BDB8F46-A39C-422B-8B0E-89E98B83073F}.Release|x86.ActiveCfg = Release|Any CPU
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Debug|Any CPU.ActiveCfg = Debug|x86
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Debug|x64.ActiveCfg = Debug|x64
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Debug|x64.Build.0 = Debug|x64
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Debug|x86.ActiveCfg = Debug|x86
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Debug|x86.Build.0 = Debug|x86
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Release|Any CPU.ActiveCfg = Release|x86
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Release|x64.ActiveCfg = Release|x64
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Release|x64.Build.0 = Release|x64
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Release|x86.ActiveCfg = Release|x86
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace CpsDbg
{
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
internal static class Commands
{
private const string DumpAsyncCommand = "dumpasync";
private static Dictionary<string, ICommandHandler> commandHandlers;
static Commands()
{
commandHandlers = new Dictionary<string, ICommandHandler>(StringComparer.OrdinalIgnoreCase);
commandHandlers.Add("dumpasync", new DumpAsyncCommand());
}
[DllExport(DumpAsyncCommand)]
internal static void DumpAsync(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
ExecuteCommand(client, DumpAsyncCommand, args);
}
private static void ExecuteCommand(IntPtr client, string command, [MarshalAs(UnmanagedType.LPStr)] string args)
{
ICommandHandler handler;
if (!commandHandlers.TryGetValue(command, out handler))
{
return;
}
DebuggerContext context = DebuggerContext.GetDebuggerContext(client);
if (context == null)
{
return;
}
handler.Execute(context, args);
}
}
}

Просмотреть файл

@ -0,0 +1,122 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace CpsDbg
{
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.Runtime;
using Microsoft.Diagnostics.Runtime.Interop;
internal class DebuggerContext
{
private const string ClrMD = "Microsoft.Diagnostics.Runtime";
/// <summary>
/// The singleton instance used in a debug session.
/// </summary>
private static DebuggerContext instance;
static DebuggerContext()
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
}
private DebuggerContext(IDebugClient debugClient, DataTarget dataTarget, ClrRuntime runtime, DebuggerOutput output)
{
this.DebugClient = debugClient;
this.DataTarget = dataTarget;
this.Runtime = runtime;
this.Output = output;
}
internal ClrRuntime Runtime { get; }
internal DebuggerOutput Output { get; }
internal IDebugClient DebugClient { get; }
internal IDebugControl DebugControl => (IDebugControl)this.DebugClient;
private DataTarget DataTarget { get; }
internal static DebuggerContext GetDebuggerContext(IntPtr ptrClient)
{
// On our first call to the API:
// 1. Store a copy of IDebugClient in DebugClient.
// 2. Replace Console's output stream to be the debugger window.
// 3. Create an instance of DataTarget using the IDebugClient.
if (instance == null)
{
object client = Marshal.GetUniqueObjectForIUnknown(ptrClient);
var debugClient = (IDebugClient)client;
var output = new DebuggerOutput(debugClient);
var dataTarget = DataTarget.CreateFromDebuggerInterface(debugClient);
ClrRuntime runtime = null;
// If our ClrRuntime instance is null, it means that this is our first call, or
// that the dac wasn't loaded on any previous call. Find the dac loaded in the
// process (the user must use .cordll), then construct our runtime from it.
// Just find a module named mscordacwks and assume it's the one the user
// loaded into windbg.
Process p = Process.GetCurrentProcess();
foreach (ProcessModule module in p.Modules)
{
if (module.FileName.ToLower().Contains("mscordacwks"))
{
// TODO: This does not support side-by-side CLRs.
runtime = dataTarget.ClrVersions.Single().CreateRuntime(module.FileName);
break;
}
}
// Otherwise, the user didn't run .cordll.
if (runtime == null)
{
output.WriteLine("Mscordacwks.dll not loaded into the debugger.");
output.WriteLine("Run .cordll to load the dac before running this command.");
}
if (runtime != null)
{
instance = new DebuggerContext(debugClient, dataTarget, runtime, output);
}
}
else
{
// If we already had a runtime, flush it for this use. This is ONLY required
// for a live process or iDNA trace. If you use the IDebug* apis to detect
// that we are debugging a crash dump you may skip this call for better perf.
// instance.Runtime.Flush();
}
return instance;
}
private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
if (args.Name.Contains(ClrMD))
{
string codebase = Assembly.GetExecutingAssembly().CodeBase;
if (codebase.StartsWith("file://"))
{
codebase = codebase.Substring(8).Replace('/', '\\');
}
string directory = Path.GetDirectoryName(codebase);
string path = Path.Combine(directory, ClrMD) + ".dll";
return Assembly.LoadFile(path);
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace CpsDbg
{
using Microsoft.Diagnostics.Runtime.Interop;
internal class DebuggerOutput
{
private IDebugClient client;
private IDebugControl control;
internal DebuggerOutput(IDebugClient client)
{
this.client = client;
this.control = (IDebugControl)client;
}
internal void WriteString(string message)
{
this.control.ControlledOutput(DEBUG_OUTCTL.ALL_CLIENTS, DEBUG_OUTPUT.NORMAL, message);
}
internal void WriteLine(string message)
{
this.WriteString(message + "\n");
}
internal void WriteObjectAddress(ulong address)
{
this.WriteDml($"<link cmd=\"!do {address:x}\">{address:x8}</link>");
}
internal void WriteThreadLink(uint threadId)
{
this.WriteDml($"<link cmd=\"~~[{threadId:x}]kp\">Thread TID:[{threadId:x}]</link>");
}
internal void WriteMethodInfo(string name, ulong address)
{
this.WriteDml($"<link cmd=\".open -a 0x{address:x}\" alt=\"Try to open source, you might need click more than once to get it work.\">{name}</link>");
}
internal void WriteStringWithLink(string message, string linkCommand)
{
this.WriteDml($"<link cmd=\"{linkCommand}\">{message}</link>");
}
private void WriteDml(string dml)
{
this.control.ControlledOutput(DEBUG_OUTCTL.AMBIENT_DML, DEBUG_OUTPUT.NORMAL, dml);
}
}
}

Просмотреть файл

@ -0,0 +1,495 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace CpsDbg
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Diagnostics.Runtime;
using Microsoft.Diagnostics.Runtime.Interop;
internal class DumpAsyncCommand : ICommandHandler
{
public void Execute(DebuggerContext context, string args)
{
ClrHeap heap = context.Runtime.Heap;
var allStateMachines = new List<AsyncStateMachine>();
var knownStateMachines = new Dictionary<ulong, AsyncStateMachine>();
GetAllStateMachines(context, heap, allStateMachines, knownStateMachines);
ChainStateMachinesBasedOnTaskContinuations(context, knownStateMachines);
ChainStateMachinesBasedOnJointableTasks(context, allStateMachines);
MarkThreadingBlockTasks(context, allStateMachines);
MarkUIThreadDependingTasks(context, allStateMachines);
FixBrokenDependencies(allStateMachines);
PrintOutStateMachines(allStateMachines, context.Output);
this.LoadCodePages(context, allStateMachines);
}
private static void GetAllStateMachines(DebuggerContext context, ClrHeap heap, List<AsyncStateMachine> allStateMachines, Dictionary<ulong, AsyncStateMachine> knownStateMachines)
{
foreach (var obj in heap.GetObjectsOfType("System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner"))
{
try
{
var stateMachine = obj.GetObjectField("m_stateMachine");
if (!knownStateMachines.ContainsKey(stateMachine.Address))
{
try
{
var state = stateMachine.GetField<int>("<>1__state");
if (state >= -1)
{
ClrObject taskField = default(ClrObject);
var asyncBuilder = stateMachine.TryGetValueClassField("<>t__builder");
if (asyncBuilder.HasValue)
{
while (asyncBuilder.HasValue)
{
taskField = asyncBuilder.TryGetObjectField("m_task");
if (!taskField.IsNull)
{
break;
}
asyncBuilder = asyncBuilder.TryGetValueClassField("m_builder");
}
}
else
{
// CLR debugger may not be able to access t__builder, when NGEN assemblies are being used, and the type of the field could be lost.
// Our workaround is to pick up the first Task object referenced by the state machine, which seems to be correct.
// That function works with the raw data structure (like how GC scans the object, so it doesn't depend on symbols.
foreach (ClrObject referencedObject in stateMachine.EnumerateObjectReferences(true))
{
if (!referencedObject.IsNull && referencedObject.Type != null)
{
if (string.Equals(referencedObject.Type.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal) || string.Equals(referencedObject.Type.BaseType?.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal))
{
taskField = referencedObject;
break;
}
}
}
}
var asyncState = new AsyncStateMachine(state, stateMachine, taskField);
allStateMachines.Add(asyncState);
knownStateMachines.Add(stateMachine.Address, asyncState);
if (stateMachine.Type != null)
{
foreach (var method in stateMachine.Type.Methods)
{
if (method.Name == "MoveNext" && method.NativeCode != ulong.MaxValue)
{
asyncState.CodeAddress = method.NativeCode;
}
}
}
}
}
catch (Exception ex)
{
context.Output.WriteLine($"Fail to process state machine {stateMachine.Address:x} Type:'{stateMachine.Type.Name}' Module:'{stateMachine.Type.Module.Name}' Error: {ex.Message}");
}
}
}
catch (Exception ex)
{
context.Output.WriteLine($"Fail to process AsyncStateMachine Runner {obj.Address:x} Error: {ex.Message}");
}
}
}
private static void ChainStateMachinesBasedOnTaskContinuations(DebuggerContext context, Dictionary<ulong, AsyncStateMachine> knownStateMachines)
{
foreach (var stateMachine in knownStateMachines.Values)
{
var taskObject = stateMachine.Task;
try
{
while (!taskObject.IsNull)
{
// 3 cases in order to get the _target:
// 1. m_continuationObject.m_action._target
// 2. m_continuationObject._target
// 3. m_continuationObject.m_task.m_stateObject._target
var continuationObject = taskObject.TryGetObjectField("m_continuationObject");
if (continuationObject.IsNull)
{
break;
}
ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, continuationObject);
taskObject = continuationObject;
}
}
catch (Exception ex)
{
context.Output.WriteLine($"Fail to fix continuation of state {stateMachine.StateMachine.Address:x} Error: {ex.Message}");
}
}
}
private static void ChainStateMachineBasedOnTaskContinuations(Dictionary<ulong, AsyncStateMachine> knownStateMachines, AsyncStateMachine stateMachine, ClrObject continuationObject)
{
var continuationAction = continuationObject.TryGetObjectField("m_action");
// case 1
var continuationTarget = continuationAction.TryGetObjectField("_target");
if (continuationTarget.IsNull)
{
// case 2
continuationTarget = continuationObject.TryGetObjectField("_target");
if (continuationTarget.IsNull)
{
// case 3
continuationTarget = continuationObject.TryGetObjectField("m_task").TryGetObjectField("m_stateObject").TryGetObjectField("_target");
}
}
while (!continuationTarget.IsNull)
{
// now get the continuation from the target
var continuationTargetStateMachine = continuationTarget.TryGetObjectField("m_stateMachine");
if (!continuationTargetStateMachine.IsNull)
{
AsyncStateMachine targetAsyncState;
if (knownStateMachines.TryGetValue(continuationTargetStateMachine.Address, out targetAsyncState) && targetAsyncState != stateMachine)
{
stateMachine.Next = targetAsyncState;
stateMachine.DependentCount++;
targetAsyncState.Previous = stateMachine;
}
break;
}
else
{
var nextContinuation = continuationTarget.TryGetObjectField("m_continuation");
continuationTarget = nextContinuation.TryGetObjectField("_target");
}
}
var items = continuationObject.TryGetObjectField("_items");
if (!items.IsNull && items.IsArray && items.ContainsPointers)
{
foreach (var promise in items.EnumerateObjectReferences(true))
{
if (!promise.IsNull)
{
var innerContinuationObject = promise.TryGetObjectField("m_continuationObject");
if (!innerContinuationObject.IsNull)
{
ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, innerContinuationObject);
}
else
{
ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, promise);
}
}
}
}
}
private static void ChainStateMachinesBasedOnJointableTasks(DebuggerContext context, List<AsyncStateMachine> allStateMachines)
{
foreach (var stateMachine in allStateMachines)
{
if (stateMachine.Previous == null)
{
try
{
var joinableTask = stateMachine.StateMachine.TryGetObjectField("<>4__this");
var wrappedTask = joinableTask.TryGetObjectField("wrappedTask");
if (!wrappedTask.IsNull)
{
var previousStateMachine = allStateMachines
.FirstOrDefault(s => s.Task.Address == wrappedTask.Address);
if (previousStateMachine != null && stateMachine != previousStateMachine)
{
stateMachine.Previous = previousStateMachine;
previousStateMachine.Next = stateMachine;
previousStateMachine.DependentCount++;
}
}
}
catch (Exception ex)
{
context.Output.WriteLine($"Fail to fix continuation of state {stateMachine.StateMachine.Address:x} Error: {ex.Message}");
}
}
}
}
private static void MarkThreadingBlockTasks(DebuggerContext context, List<AsyncStateMachine> allStateMachines)
{
foreach (var thread in context.Runtime.Threads)
{
var stackFrame = thread.EnumerateStackTrace().Take(50).FirstOrDefault(
f => string.Equals(f.Method?.Name, "CompleteOnCurrentThread", StringComparison.Ordinal) &&
string.Equals(f.Method.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTask", StringComparison.Ordinal));
if (stackFrame != null)
{
var visitedObjects = new HashSet<ulong>();
foreach (var stackObject in thread.EnumerateStackObjects())
{
if (string.Equals(stackObject.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTask", StringComparison.Ordinal) ||
string.Equals(stackObject.Type?.BaseType?.Name, "Microsoft.VisualStudio.Threading.JoinableTask", StringComparison.Ordinal))
{
if (visitedObjects.Add(stackObject.Object))
{
var joinableTaskObject = new ClrObject(stackObject.Object, stackObject.Type);
int state = joinableTaskObject.GetField<int>("state");
if ((state & 0x10) == 0x10)
{
// This flag indicates the JTF is blocking the thread
var wrappedTask = joinableTaskObject.TryGetObjectField("wrappedTask");
if (!wrappedTask.IsNull)
{
var blockingStateMachine = allStateMachines
.FirstOrDefault(s => s.Task.Address == wrappedTask.Address);
if (blockingStateMachine != null)
{
blockingStateMachine.BlockedThread = thread.OSThreadId;
blockingStateMachine.BlockedJoinableTask = joinableTaskObject;
}
}
break;
}
}
}
}
}
}
}
private static void MarkUIThreadDependingTasks(DebuggerContext context, List<AsyncStateMachine> allStateMachines)
{
foreach (var stateMachine in allStateMachines)
{
if (stateMachine.Previous == null && stateMachine.State >= 0)
{
try
{
var awaitField = stateMachine.StateMachine.Type.GetFieldByName($"<>u__{stateMachine.State + 1}");
if (awaitField != null && awaitField.IsValueClass && string.Equals(awaitField.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTaskFactory+MainThreadAwaiter"))
{
var awaitObject = stateMachine.StateMachine.TryGetValueClassField($"<>u__{stateMachine.State + 1}");
if (awaitObject != null)
{
stateMachine.SwitchToMainThreadTask = awaitObject.TryGetObjectField("job");
}
}
}
catch (Exception)
{
}
}
}
}
private static void FixBrokenDependencies(List<AsyncStateMachine> allStateMachines)
{
foreach (var stateMachine in allStateMachines)
{
if (stateMachine.Previous != null && stateMachine.Previous.Next != stateMachine)
{
// If the previous task actually has two continuations, we end up in a one way dependencies chain, we need fix it in the future.
stateMachine.AlterPrevious = stateMachine.Previous;
stateMachine.Previous = null;
}
}
}
private static void PrintOutStateMachines(List<AsyncStateMachine> allStateMachines, DebuggerOutput output)
{
int loopMark = -1;
foreach (var stateMachine in allStateMachines)
{
int depth = 0;
if (stateMachine.Previous == null)
{
var p = stateMachine;
while (p != null)
{
depth++;
if (p.Depth == loopMark)
{
break;
}
p.Depth = loopMark;
p = p.Next;
}
}
if (stateMachine.AlterPrevious != null)
{
depth++;
}
stateMachine.Depth = depth;
loopMark--;
}
var printedMachines = new HashSet<AsyncStateMachine>();
foreach (var node in allStateMachines
.Where(m => m.Depth > 0)
.OrderByDescending(m => m.Depth)
.ThenByDescending(m => m.SwitchToMainThreadTask.Address))
{
bool multipleLineBlock = PrintAsyncStateMachineChain(output, node, printedMachines);
if (multipleLineBlock)
{
output.WriteLine(string.Empty);
}
}
// Print nodes which we didn't print because of loops.
if (allStateMachines.Count > printedMachines.Count)
{
output.WriteLine("States form dependencies loop -- could be an error caused by the analysis tool");
foreach (var node in allStateMachines)
{
if (!printedMachines.Contains(node))
{
PrintAsyncStateMachineChain(output, node, printedMachines);
output.WriteLine(string.Empty);
}
}
}
}
private static bool PrintAsyncStateMachineChain(DebuggerOutput output, AsyncStateMachine node, HashSet<AsyncStateMachine> printedMachines)
{
int nLevel = 0;
bool multipleLineBlock = false;
var loopDetection = new HashSet<AsyncStateMachine>();
for (var p = node; p != null; p = p.Next)
{
printedMachines.Add(p);
if (nLevel > 0)
{
output.WriteString("..");
multipleLineBlock = true;
}
else if (p.AlterPrevious != null)
{
output.WriteObjectAddress(p.AlterPrevious.StateMachine.Address);
output.WriteString($" <{p.AlterPrevious.State}> * {p.AlterPrevious.StateMachine.Type.Name} @ ");
output.WriteMethodInfo($"{p.AlterPrevious.CodeAddress:x}", p.AlterPrevious.CodeAddress);
output.WriteLine(string.Empty);
output.WriteString("..");
multipleLineBlock = true;
}
else if (!p.SwitchToMainThreadTask.IsNull)
{
output.WriteObjectAddress(p.SwitchToMainThreadTask.Address);
output.WriteLine(".SwitchToMainThreadAsync");
output.WriteString("..");
multipleLineBlock = true;
}
output.WriteObjectAddress(p.StateMachine.Address);
string doubleDependentTaskMark = p.DependentCount > 1 ? " * " : " ";
output.WriteString($" <{p.State}>{doubleDependentTaskMark}{p.StateMachine.Type.Name} @ ");
output.WriteMethodInfo($"{p.CodeAddress:x}", p.CodeAddress);
output.WriteLine(string.Empty);
if (!loopDetection.Add(p))
{
output.WriteLine("!!Loop task dependencies");
break;
}
if (p.Next == null && p.BlockedThread.HasValue)
{
output.WriteString("-- ");
output.WriteThreadLink(p.BlockedThread.Value);
output.WriteString(" - JoinableTask: ");
output.WriteObjectAddress(p.BlockedJoinableTask.Address);
int state = p.BlockedJoinableTask.GetField<int>("state");
if ((state & 0x20) == 0x20)
{
output.WriteLine(" SynchronouslyBlockingMainThread");
}
else
{
output.WriteLine(string.Empty);
}
multipleLineBlock = true;
}
nLevel++;
}
return multipleLineBlock;
}
private void LoadCodePages(DebuggerContext context, List<AsyncStateMachine> allStateMachines)
{
var loadedAddresses = new HashSet<ulong>();
foreach (var stateMachine in allStateMachines)
{
ulong codeAddress = stateMachine.CodeAddress;
if (loadedAddresses.Add(codeAddress))
{
context.DebugControl.Execute(DEBUG_OUTCTL.IGNORE, $"u {codeAddress} {codeAddress}", DEBUG_EXECUTE.NOT_LOGGED);
}
}
}
private class AsyncStateMachine
{
public AsyncStateMachine(int state, ClrObject stateMachine, ClrObject task)
{
this.State = state;
this.StateMachine = stateMachine;
this.Task = task;
}
public int State { get; } // -1 == currently running, 0 = still waiting on first await, 2= before the 3rd await
public ClrObject StateMachine { get; }
public ClrObject Task { get; }
public AsyncStateMachine Previous { get; set; }
public AsyncStateMachine Next { get; set; }
public int DependentCount { get; set; }
public int Depth { get; set; }
public uint? BlockedThread { get; set; }
public ClrObject BlockedJoinableTask { get; set; }
public ClrObject SwitchToMainThreadTask { get; set; }
public AsyncStateMachine AlterPrevious { get; set; }
public ulong CodeAddress { get; set; }
public override string ToString()
{
return $"state = {this.State} Depth {this.Depth} StateMachine = {this.StateMachine} Task = {this.Task}";
}
}
}
}

Просмотреть файл

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace CpsDbg
{
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
internal static class ExtensionContext
{
[DllExport(nameof(DebugExtensionInitialize))]
internal static int DebugExtensionInitialize(ref uint version, ref uint flags)
{
// Set the extension version to 1, which expects exports with this signature:
// void _stdcall function(IDebugClient *client, const char *args)
version = DEBUG_EXTENSION_VERSION(1, 0);
flags = 0;
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder);
return 0;
}
private static uint DEBUG_EXTENSION_VERSION(uint major, uint minor)
{
return ((major & 0xffff) << 16) | (minor & 0xffff);
}
private static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
{
string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
if (!File.Exists(assemblyPath))
{
return null;
}
Assembly assembly = Assembly.LoadFrom(assemblyPath);
return assembly;
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace CpsDbg
{
internal interface ICommandHandler
{
void Execute(DebuggerContext context, string args);
}
}

Просмотреть файл

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<IsPackable>false</IsPackable>
<Platforms>x86;x64</Platforms>
<CodeAnalysisRuleSet>SosThreadingTools.ruleset</CodeAnalysisRuleSet>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<DllExportOurILAsm>false</DllExportOurILAsm>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="1.0.2" />
<PackageReference Include="DllExport" Version="1.5.2" />
</ItemGroup>
<Import Project="$(NuGetPackageRoot)\dllexport\1.5.2\tools\net.r_eg.DllExport.targets" Condition="Exists('$(NuGetPackageRoot)\dllexport\1.5.2\tools\net.r_eg.DllExport.targets')" />
</Project>

Просмотреть файл

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Microsoft Managed Recommended Rules" Description="These rules focus on the most critical problems in your code, including potential security holes, application crashes, and other important logic and design errors. It is recommended to include this rule set in any custom rule set you create for your projects." ToolsVersion="10.0">
<Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">
<Name Resource="MinimumRecommendedRules_Name" />
<Description Resource="MinimumRecommendedRules_Description" />
</Localization>
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1001" Action="Warning" />
<Rule Id="CA1009" Action="Warning" />
<Rule Id="CA1016" Action="Warning" />
<Rule Id="CA1033" Action="Warning" />
<Rule Id="CA1049" Action="Warning" />
<Rule Id="CA1060" Action="Warning" />
<Rule Id="CA1061" Action="Warning" />
<Rule Id="CA1063" Action="Warning" />
<Rule Id="CA1065" Action="Warning" />
<Rule Id="CA1301" Action="Warning" />
<Rule Id="CA1400" Action="Warning" />
<Rule Id="CA1401" Action="Warning" />
<Rule Id="CA1403" Action="Warning" />
<Rule Id="CA1404" Action="Warning" />
<Rule Id="CA1405" Action="Warning" />
<Rule Id="CA1410" Action="Warning" />
<Rule Id="CA1415" Action="Warning" />
<Rule Id="CA1821" Action="Warning" />
<Rule Id="CA1900" Action="Warning" />
<Rule Id="CA1901" Action="Warning" />
<Rule Id="CA2002" Action="Warning" />
<Rule Id="CA2100" Action="Warning" />
<Rule Id="CA2101" Action="Warning" />
<Rule Id="CA2108" Action="Warning" />
<Rule Id="CA2111" Action="Warning" />
<Rule Id="CA2112" Action="Warning" />
<Rule Id="CA2114" Action="Warning" />
<Rule Id="CA2116" Action="Warning" />
<Rule Id="CA2117" Action="Warning" />
<Rule Id="CA2122" Action="Warning" />
<Rule Id="CA2123" Action="Warning" />
<Rule Id="CA2124" Action="Warning" />
<Rule Id="CA2126" Action="Warning" />
<Rule Id="CA2131" Action="Warning" />
<Rule Id="CA2132" Action="Warning" />
<Rule Id="CA2133" Action="Warning" />
<Rule Id="CA2134" Action="Warning" />
<Rule Id="CA2137" Action="Warning" />
<Rule Id="CA2138" Action="Warning" />
<Rule Id="CA2140" Action="Warning" />
<Rule Id="CA2141" Action="Warning" />
<Rule Id="CA2146" Action="Warning" />
<Rule Id="CA2147" Action="Warning" />
<Rule Id="CA2149" Action="Warning" />
<Rule Id="CA2200" Action="Warning" />
<Rule Id="CA2202" Action="Warning" />
<Rule Id="CA2207" Action="Warning" />
<Rule Id="CA2212" Action="Warning" />
<Rule Id="CA2213" Action="Warning" />
<Rule Id="CA2214" Action="Warning" />
<Rule Id="CA2216" Action="Warning" />
<Rule Id="CA2220" Action="Warning" />
<Rule Id="CA2229" Action="Warning" />
<Rule Id="CA2231" Action="Warning" />
<Rule Id="CA2232" Action="Warning" />
<Rule Id="CA2235" Action="Warning" />
<Rule Id="CA2236" Action="Warning" />
<Rule Id="CA2237" Action="Warning" />
<Rule Id="CA2238" Action="Warning" />
<Rule Id="CA2240" Action="Warning" />
<Rule Id="CA2241" Action="Warning" />
<Rule Id="CA2242" Action="Warning" />
</Rules>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1600" Action="Hidden" />
<Rule Id="SA1601" Action="Hidden" />
<Rule Id="SA1602" Action="Hidden" />
</Rules>
</RuleSet>

Просмотреть файл

@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace CpsDbg
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Diagnostics.Runtime;
internal static class Utilities
{
internal static ClrObject TryGetObjectField(this ClrObject clrObject, string fieldName)
{
if (!clrObject.IsNull)
{
var field = clrObject.Type.GetFieldByName(fieldName);
if (field != null && field.IsObjectReference)
{
ulong address = field.GetAddress(clrObject.Address);
ulong reference;
if (clrObject.Type.Heap.ReadPointer(address, out reference) && reference != 0)
{
return new ClrObject(reference, clrObject.Type.Heap.GetObjectType(reference));
}
}
}
return default(ClrObject);
}
internal static ClrValueClass? TryGetValueClassField(this ClrObject clrObject, string fieldName)
{
if (!clrObject.IsNull)
{
var field = clrObject.Type.GetFieldByName(fieldName);
if (field != null && field.Type.IsValueClass)
{
// System.Console.WriteLine("{0} {1:x} Field {2} {3} {4} {5}", clrObject.Type.Name, clrObject.Address, fieldName, field.Type.Name, field.Type.IsValueClass, field.Type.IsRuntimeType);
return clrObject.GetValueClassField(fieldName);
}
}
return null;
}
internal static ClrObject TryGetObjectField(this ClrValueClass? clrObject, string fieldName)
{
if (clrObject != null)
{
var field = clrObject.Value.Type.GetFieldByName(fieldName);
if (field != null && field.IsObjectReference)
{
return clrObject.Value.GetObjectField(fieldName);
}
}
return default(ClrObject);
}
internal static ClrValueClass? TryGetValueClassField(this ClrValueClass? clrObject, string fieldName)
{
if (clrObject.HasValue)
{
var field = clrObject.Value.Type.GetFieldByName(fieldName);
if (field != null && field.IsValueClass)
{
return clrObject.Value.GetValueClassField(fieldName);
}
}
return null;
}
internal static IEnumerable<ClrObject> GetObjectsOfType(this ClrHeap heap, string typeName)
{
return heap.EnumerateObjects().Where(obj => string.Equals(obj.Type?.Name, typeName, StringComparison.Ordinal));
}
}
}

Просмотреть файл

@ -2,6 +2,7 @@
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"documentationRules": {
"companyName": "Microsoft Corporation",
"xmlHeader": false,
"fileNamingConvention": "metadata"
}