--- /dev/null
+# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.192.0/containers/dotnet/.devcontainer/base.Dockerfile
+# For specifics about the dotnet base container see: https://github.com/microsoft/vscode-dev-containers/tree/main/containers/dotnet
+
+# [Choice] .NET version: 6.0, 5.0, 3.1, 2.1
+ARG VARIANT="6.0-focal"
+FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT}
+
+# Set up machine requirements to build the repo
+RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
+ && apt-get -y install --no-install-recommends cmake clang \
+ curl gdb gettext git libicu-dev lldb liblldb-dev libunwind8 \
+ llvm make python python-lldb tar wget zip
\ No newline at end of file
--- /dev/null
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
+// https://github.com/microsoft/vscode-dev-containers/tree/v0.192.0/containers/dotnet
+{
+ "name": "C# (.NET)",
+ "build": {
+ "dockerfile": "Dockerfile",
+ "args": {
+ // Update 'VARIANT' to pick a .NET Core version: 2.1, 3.1, 5.0, 6.0
+ "VARIANT": "6.0-focal",
+ }
+ },
+
+ "settings": {
+ // Loading projects on demand is better for larger codebases
+ "omnisharp.enableMsBuildLoadProjectsOnDemand": true,
+ "omnisharp.enableRoslynAnalyzers": true,
+ "omnisharp.enableEditorConfigSupport": true,
+ "omnisharp.enableAsyncCompletion": true,
+ },
+
+ // Add the IDs of extensions you want installed when the container is created.
+ "extensions": [
+ "ms-dotnettools.csharp",
+ "ms-vscode.cpptools-extension-pack"
+ ],
+
+ // Add the locally installed dotnet to the path to ensure that it is activated
+ // This allows developers to just use 'dotnet build' on the command-line, and the local dotnet version will be used.
+ // Add the global tools dir to the PATH so that globally installed tools will work
+ "remoteEnv": {
+ "PATH": "${containerWorkspaceFolder}/.dotnet:${containerWorkspaceFolder}/.dotnet-tools-global:${containerEnv:PATH}",
+ "DOTNET_MULTILEVEL_LOOKUP": "0"
+ },
+
+ // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
+ "remoteUser": "vscode"
+}
*.sln.docstates
.vs/
*.VC.db
+.venv/
# Build results
[Aa]rtifacts/
.packages/
.tools/
.vscode/
+build/
# Per-user project properties
launchSettings.json
<clear />
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
<!-- Begin: Package sources from dotnet-aspnetcore -->
- <add key="darc-pub-dotnet-aspnetcore-14468ef" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-aspnetcore-14468ef1/nuget/v3/index.json" />
+ <add key="darc-pub-dotnet-aspnetcore-fac970d" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-aspnetcore-fac970de/nuget/v3/index.json" />
<!-- End: Package sources from dotnet-aspnetcore -->
<!-- Begin: Package sources from dotnet-runtime -->
- <add key="darc-pub-dotnet-runtime-0bb27bd" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-runtime-0bb27bd5/nuget/v3/index.json" />
<!-- End: Package sources from dotnet-runtime -->
<!--End: Package sources managed by Dependency Flow automation. Do not edit the sources above.-->
<!-- Feeds used in Maestro/Arcade publishing -->
--- /dev/null
+Please see the [EventPipeFormat documentation in Perfview](https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md)
- dotnet-counters
- dotnet-monitor
- Microsoft.Diagnostics.NETCore.Client
+ - Microsoft.Diagnostics.DbgShim
To release the latest tools:
Installing LLDB on Linux
========================
-These instructions will lead you through installing or building the best version of lldb for your distro to use with SOS. If you have already followed the diagnostics repo build [prerequisites](../building/linux-instructions.md) and built the diagnostics repo, then the best version of lldb is already installed.
+These instructions will lead you through installing or building the best version of lldb for your distro to use with SOS.
-SOS needs at least lldb 3.9 or greater. Some distros only have older versions available by default so there are directions and scripts to build lldb 3.9 for that platform. These instructions assume that you have dotnet cli and its prerequisites installed.
+If you have already followed the diagnostics repo build [prerequisites](../building/linux-instructions.md) and built the diagnostics repo, then the best version of lldb is already installed.
-The libsosplugin.so built for lldb 3.9 does work with lldb 4.0 and greater but most of the testing has been on lldb 3.9.
+You need lldb 3.9 or later to use SOS. Some distros only have older versions available by default so there are directions and scripts to build lldb 3.9 for that platform. These instructions assume that you have dotnet cli and its prerequisites installed.
-lldb 10.0 or greater is recommended if available for the distro version. For arm32, it is recommended to debug on Ubuntu 18.04 if possible with lldb 10.0 which is the only version of lldb found that works with SOS on arm32.
-
-#### Ubuntu 14.04 ####
-
-In order to get lldb-3.9, we need to add additional package sources (see [http://llvm.org/apt/](http://llvm.org/apt/) for the other Ubuntu versions not listed here):
-
- sudo apt-get update
- sudo apt-get install wget
- echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty main" | sudo tee /etc/apt/sources.list.d/llvm.list
- echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee -a /etc/apt/sources.list.d/llvm.list
- wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-get update
-
-Install the lldb packages:
-
- sudo apt-get install lldb-3.9 python-lldb-3.9
-
-To launch lldb:
-
- lldb-3.9
+lldb 10.0 or later is best if your distro has that available. In particular if you are debugging for Arm32 you should use Ubuntu 18.04 or later if possible and lldb 10.0 or later.
#### Ubuntu 16.04 ####
lldb-3.9
-#### Ubuntu 17.10 ####
-
-Add the additional package sources:
-
- sudo apt-get update
- sudo apt-get install wget
- echo "deb http://llvm.org/apt/artful/ llvm-toolchain-artful main" | sudo tee /etc/apt/sources.list.d/llvm.list
- echo "deb http://llvm.org/apt/artful/ llvm-toolchain-artful-3.9 main" | sudo tee -a /etc/apt/sources.list.d/llvm.list
- wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-get update
-
-Install the lldb packages:
-
- sudo apt-get install lldb-3.9 python-lldb-3.9
-
-To launch lldb:
-
- lldb-3.9
-
#### Ubuntu 18.04 ####
To install the lldb packages:
lldb-3.9
-10.0 is the only version of lldb found that works with SOS for arm32 on Ubuntu 18.04.
+lldb 10.0 is the only version of lldb that works with SOS for Arm32 on Ubuntu 18.04.
-#### Ubuntu 20.04 ####
+#### Ubuntu 20.04 and later ####
To install the lldb packages:
- sudo get-get update
+ sudo apt-get update
sudo apt-get install lldb
-This installs lldb version 10.0.0.
+This installs lldb version 10.0.
-#### Alpine 3.9 to 3.12 ####
+#### Alpine 3.9 and later ####
-lldb 10.0 is available and works for these Apline versions.
-
-#### CentOS 6 ####
-
-[TBD]
+ apk update
+ apk add lldb
+
+This installs lldb version 10.0.
#### CentOS 7 ####
To launch lldb:
lldb-3.9.1
+
+#### Centos Stream 8 and later ####
-#### Debian 8.2/8.7 ####
-
-In order to get lldb-5.0 (3.9 doesn't seem to work that well), we need to add additional package sources:
-
- sudo apt-get update
- sudo apt-get install wget
- echo "deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie main" | sudo tee /etc/apt/sources.list.d/llvm.list
- echo "deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-5.0 main" | sudo tee -a /etc/apt/sources.list.d/llvm.list
- wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-get update
-
-Install the lldb packages:
-
- sudo apt-get install lldb-5.0 python-lldb-5.0
-
-To launch lldb:
-
- lldb-5.0
+[TBD]
-#### Debian 9 (Stretch) ####
+#### Debian 9 and later ####
sudo apt-get install lldb-3.9 python-lldb-3.9
lldb-3.9
-#### Fedora 24 ####
-
- sudo dnf install clang cmake findutils git libicu libunwind make python tar wget which zip
- sudo dnf install doxygen libedit-devel libxml2-devel python-argparse python-devel readline-devel swig xz
-
-Now build and install llvm/lldb 3.9 using the script provided here: [build-install-lldb.sh](../lldb/fedora24/build-install-lldb.sh).
-
-WARNING: This script installs llvm and lldb as root (via sudo) and may overwrite any previously installed versions.
-
- cd $HOME
- git clone https://github.com/dotnet/diagnostics.git
- $HOME/diagnostics/documentation/lldb/fedora24/build-install-lldb.sh
-
-This will take some time to complete. After the build is finished, run these commands to remove the no longer needed packages:
-
- sudo dnf remove doxygen libedit-devel libxml2-devel readline-devel swig
- sudo dnf clean all
-
-To launch lldb:
-
- lldb
-
-#### Fedora 27, 28, 29 ####
+#### Fedora 29 and later ####
sudo dnf install lldb python2-lldb
#### RHEL 7.5 ####
-See [LLDB](https://access.redhat.com/documentation/en-us/red_hat_developer_tools/2018.2/html/using_clang_and_llvm_toolset/chap-lldb) on RedHat's web site.
+See [LLDB](https://access.redhat.com/documentation/en-us/red_hat_developer_tools/1/html/using_llvm_12.0.1_toolset/assembly_llvm#proc_installing-comp-toolset_assembly_llvm) on RedHat's web site.
#### SLES ####
call "%__ThisScriptDir%"\native\init-vs-env.cmd
if NOT '%ERRORLEVEL%' == '0' goto ExitWithError
+if defined VS170COMNTOOLS (
+ set "__VSToolsRoot=%VS170COMNTOOLS%"
+ set "__VCToolsRoot=%VS170COMNTOOLS%\..\..\VC\Auxiliary\Build"
+ set __VSVersion=vs2022
+)
if defined VS160COMNTOOLS (
set "__VSToolsRoot=%VS160COMNTOOLS%"
set "__VCToolsRoot=%VS160COMNTOOLS%\..\..\VC\Auxiliary\Build"
echo %__MsgPrefix%Checking prerequisites
:: Eval the output from probe-win1.ps1
-for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& ""%__ProjectDir%\eng\set-cmake-path.ps1"""') do %%a
+for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& ""%__ProjectDir%\eng\native\set-cmake-path.ps1"""') do %%a
REM =========================================================================================
REM ===
@if defined _echo @echo on
:: Parse the optdata package versions out of msbuild so that we can pass them on to CMake
-set __DotNetCli=%__ProjectDir%\.dotnet\dotnet.exe
-if not exist "%__DotNetCli%" (
- echo %__MsgPrefix%Assertion failed: dotnet cli not found at path "%__DotNetCli%"
- goto ExitWithError
-)
+set __DotNetCli=%__ProjectDir%\dotnet.cmd
REM =========================================================================================
REM ===
popd
:SkipConfigureCrossBuild
- if not exist "%__CrossCompIntermediatesDir%\install.vcxproj" (
+ if not exist "%__CrossCompIntermediatesDir%\CMakeCache.txt" (
echo %__MsgPrefix%Error: failed to generate cross-arch components build project!
goto ExitWithError
)
set __BuildLog="%__LogDir%\Cross.Build.binlog"
- :: MSBuild.exe is the only one that has the C++ targets. "%__DotNetCli% msbuild" fails because VCTargetsPath isn't defined.
- msbuild.exe %__CrossCompIntermediatesDir%\install.vcxproj /bl:!__BuildLog! %__CommonBuildArgs%
+ echo running "%CMakePath%" --build %__CrossCompIntermediatesDir% --target install --config %__BuildType% -- /bl:!__BuildLog! !__CommonBuildArgs!
+ "%CMakePath%" --build %__CrossCompIntermediatesDir% --target install --config %__BuildType% -- /bl:!__BuildLog! !__CommonBuildArgs!
if not !ERRORLEVEL! == 0 (
echo %__MsgPrefix%Error: cross-arch components build failed. Refer to the build log files for details:
:SkipConfigure
if defined __ConfigureOnly goto SkipNativeBuild
- if not exist "%__IntermediatesDir%\install.vcxproj" (
+ if not exist "%__IntermediatesDir%\CMakeCache.txt" (
echo %__MsgPrefix%Error: failed to generate native component build project!
goto ExitWithError
)
set __BuildLog="%__LogDir%\Native.Build.binlog"
- :: MSBuild.exe is the only one that has the C++ targets. "%__DotNetCli% msbuild" fails because VCTargetsPath isn't defined.
- msbuild.exe %__IntermediatesDir%\install.vcxproj /bl:!__BuildLog! %__CommonBuildArgs%
+ echo running "%CMakePath%" --build %__IntermediatesDir% --target install --config %__BuildType% -- /bl:!__BuildLog! !__CommonBuildArgs!
+ "%CMakePath%" --build %__IntermediatesDir% --target install --config %__BuildType% -- /bl:!__BuildLog! !__CommonBuildArgs!
if not !ERRORLEVEL! == 0 (
echo %__MsgPrefix%Error: native component build failed. Refer to the build log files for details:
<Dependencies>
<ProductDependencies>
- <Dependency Name="Microsoft.SymbolStore" Version="1.0.320401">
+ <Dependency Name="Microsoft.SymbolStore" Version="1.0.326601">
<Uri>https://github.com/dotnet/symstore</Uri>
- <Sha>aebde3c78a70fbbc067a059a98f27f7c4d0abbd5</Sha>
+ <Sha>073abc4f492cbc6795989e4a813b0d32017a8623</Sha>
</Dependency>
- <Dependency Name="Microsoft.Diagnostics.Runtime" Version="2.0.317902">
+ <Dependency Name="Microsoft.Diagnostics.Runtime" Version="2.0.325901">
<Uri>https://github.com/microsoft/clrmd</Uri>
- <Sha>5444e45c321f681bdd6b5fbfb4d2115de19c39a2</Sha>
+ <Sha>a64d9ac11086f28fbd4b2b2337c19be7826fbfa9</Sha>
</Dependency>
- <Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="2.0.317902">
+ <Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="2.0.325901">
<Uri>https://github.com/microsoft/clrmd</Uri>
- <Sha>5444e45c321f681bdd6b5fbfb4d2115de19c39a2</Sha>
+ <Sha>a64d9ac11086f28fbd4b2b2337c19be7826fbfa9</Sha>
</Dependency>
- <Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="7.0.0-alpha.1.22173.1">
+ <Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="7.0.0-alpha.1.22261.1">
<Uri>https://github.com/dotnet/source-build-reference-packages</Uri>
- <Sha>e7f2964ac4a556d2ba99a185f52aa93dcbba4f7e</Sha>
+ <Sha>c71ce108eda9f657c9f58a613a1fe56b6b08235d</Sha>
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
- <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="7.0.0-beta.22181.2">
+ <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="7.0.0-beta.22269.3">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>e0b311bcd81fc9e27bcf7715dcda62fa38dfa49a</Sha>
+ <Sha>0403b0d07aff1b103256cfbe082c97a5c8846d20</Sha>
<SourceBuild RepoName="arcade" ManagedOnly="true" />
</Dependency>
- <Dependency Name="Microsoft.DotNet.RemoteExecutor" Version="7.0.0-beta.22181.2">
+ <Dependency Name="Microsoft.DotNet.RemoteExecutor" Version="7.0.0-beta.22269.3">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>e0b311bcd81fc9e27bcf7715dcda62fa38dfa49a</Sha>
+ <Sha>0403b0d07aff1b103256cfbe082c97a5c8846d20</Sha>
</Dependency>
- <Dependency Name="Microsoft.Dotnet.Sdk.Internal" Version="6.0.104-servicing.22181.15">
+ <Dependency Name="Microsoft.Dotnet.Sdk.Internal" Version="6.0.106-servicing.22263.13">
<Uri>https://github.com/dotnet/installer</Uri>
- <Sha>f6e3766b7adfac2863e91ba6453912d24d0fbd9e</Sha>
+ <Sha>ee1b7085a6eb99c97851ed67fc3fd52b8bda3ba0</Sha>
</Dependency>
- <Dependency Name="Microsoft.AspNetCore.App.Ref.Internal" Version="6.0.5-servicing.22205.16">
+ <Dependency Name="Microsoft.AspNetCore.App.Ref.Internal" Version="6.0.6-servicing.22262.8">
<Uri>https://github.com/dotnet/aspnetcore</Uri>
- <Sha>14468ef10b172da67df3e452ade3d7e0f7e256ca</Sha>
+ <Sha>fac970ded3047f63328ff56f59a874bcabf18126</Sha>
</Dependency>
- <Dependency Name="Microsoft.AspNetCore.App.Ref" Version="6.0.5">
+ <Dependency Name="Microsoft.AspNetCore.App.Ref" Version="6.0.6">
<Uri>https://github.com/dotnet/aspnetcore</Uri>
- <Sha>14468ef10b172da67df3e452ade3d7e0f7e256ca</Sha>
+ <Sha>fac970ded3047f63328ff56f59a874bcabf18126</Sha>
</Dependency>
<Dependency Name="Microsoft.NETCore.App.Runtime.win-x64" Version="6.0.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>0bb27bd5aa461aea3d8a0ab58c6cb1369534f8f6</Sha>
+ <Sha>a21b9a2dd4c31cf5bd37626562b7612faf21cee6</Sha>
</Dependency>
- <Dependency Name="VS.Redist.Common.NetCore.SharedFramework.x64.6.0" Version="6.0.5-servicing.22205.8">
+ <Dependency Name="VS.Redist.Common.NetCore.SharedFramework.x64.6.0" Version="6.0.5-servicing.22213.11">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>0bb27bd5aa461aea3d8a0ab58c6cb1369534f8f6</Sha>
+ <Sha>a21b9a2dd4c31cf5bd37626562b7612faf21cee6</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>
</PropertyGroup>
<PropertyGroup>
<!-- The SDK runtime version used to build single-file apps (currently hardcoded) -->
- <SingleFileRuntimeVersion>6.0.3</SingleFileRuntimeVersion>
+ <SingleFileRuntimeVersion>6.0.5</SingleFileRuntimeVersion>
<!-- Latest symstore version updated by darc -->
- <MicrosoftSymbolStoreVersion>1.0.320401</MicrosoftSymbolStoreVersion>
+ <MicrosoftSymbolStoreVersion>1.0.326601</MicrosoftSymbolStoreVersion>
<!-- Runtime versions to test -->
<MicrosoftNETCoreApp31Version>3.1.18</MicrosoftNETCoreApp31Version>
<MicrosoftAspNetCoreApp31Version>$(MicrosoftNETCoreApp31Version)</MicrosoftAspNetCoreApp31Version>
<MicrosoftNETCoreApp50Version>5.0.9</MicrosoftNETCoreApp50Version>
<MicrosoftAspNetCoreApp50Version>$(MicrosoftNETCoreApp50Version)</MicrosoftAspNetCoreApp50Version>
<!-- Latest shared runtime version updated by darc -->
- <VSRedistCommonNetCoreSharedFrameworkx6460Version>6.0.5-servicing.22205.8</VSRedistCommonNetCoreSharedFrameworkx6460Version>
+ <VSRedistCommonNetCoreSharedFrameworkx6460Version>6.0.5-servicing.22213.11</VSRedistCommonNetCoreSharedFrameworkx6460Version>
<MicrosoftNETCoreAppRuntimewinx64Version>6.0.5</MicrosoftNETCoreAppRuntimewinx64Version>
<!-- Latest shared aspnetcore version updated by darc -->
- <MicrosoftAspNetCoreAppRefInternalVersion>6.0.5-servicing.22205.16</MicrosoftAspNetCoreAppRefInternalVersion>
- <MicrosoftAspNetCoreAppRefVersion>6.0.5</MicrosoftAspNetCoreAppRefVersion>
+ <MicrosoftAspNetCoreAppRefInternalVersion>6.0.6-servicing.22262.8</MicrosoftAspNetCoreAppRefInternalVersion>
+ <MicrosoftAspNetCoreAppRefVersion>6.0.6</MicrosoftAspNetCoreAppRefVersion>
<!-- dotnet/installer: Testing version of the SDK. Needed for the signed & entitled host. -->
- <MicrosoftDotnetSdkInternalVersion>6.0.104-servicing.22181.15</MicrosoftDotnetSdkInternalVersion>
+ <MicrosoftDotnetSdkInternalVersion>6.0.106-servicing.22263.13</MicrosoftDotnetSdkInternalVersion>
</PropertyGroup>
<PropertyGroup>
<!-- Opt-in/out repo features -->
<MicrosoftWin32PrimitivesVersion>4.3.0</MicrosoftWin32PrimitivesVersion>
<!-- Other libs -->
<MicrosoftBclAsyncInterfacesVersion>1.1.0</MicrosoftBclAsyncInterfacesVersion>
- <MicrosoftDiagnosticsRuntimeVersion>2.0.317902</MicrosoftDiagnosticsRuntimeVersion>
- <MicrosoftDiagnosticsRuntimeUtilitiesVersion>2.0.317902</MicrosoftDiagnosticsRuntimeUtilitiesVersion>
+ <MicrosoftDiagnosticsRuntimeVersion>2.0.325901</MicrosoftDiagnosticsRuntimeVersion>
+ <MicrosoftDiagnosticsRuntimeUtilitiesVersion>2.0.325901</MicrosoftDiagnosticsRuntimeUtilitiesVersion>
<MicrosoftDiaSymReaderNativePackageVersion>16.9.0-beta1.21055.5</MicrosoftDiaSymReaderNativePackageVersion>
<MicrosoftDiagnosticsTracingTraceEventVersion>2.0.64</MicrosoftDiagnosticsTracingTraceEventVersion>
<MicrosoftExtensionsLoggingVersion>2.1.1</MicrosoftExtensionsLoggingVersion>
<SystemTextEncodingsWebVersion>4.7.2</SystemTextEncodingsWebVersion>
<SystemTextJsonVersion>4.7.1</SystemTextJsonVersion>
<XUnitAbstractionsVersion>2.0.3</XUnitAbstractionsVersion>
- <MicrosoftDotNetRemoteExecutorVersion>7.0.0-beta.22181.2</MicrosoftDotNetRemoteExecutorVersion>
+ <MicrosoftDotNetRemoteExecutorVersion>7.0.0-beta.22269.3</MicrosoftDotNetRemoteExecutorVersion>
<cdbsosversion>10.0.18362</cdbsosversion>
<NewtonSoftJsonVersion>12.0.2</NewtonSoftJsonVersion>
</PropertyGroup>
fi
build_native "$__TargetOS" "$__BuildArch" "$__RepoRootDir" "$__IntermediatesDir" "install" "$__ExtraCmakeArgs" "diagnostic component" | tee "$__LogsDir"/make.log
+
+ if [ "$?" != 0 ]; then
+ echo "Native build FAILED"
+ exit 1
+ fi
fi
#
usage()
{
- echo "Usage: $0 [BuildArch] [CodeName] [lldbx.y] [--skipunmount] --rootfsdir <directory>]"
+ echo "Usage: $0 [BuildArch] [CodeName] [lldbx.y] [llvmx[.y]] [--skipunmount] --rootfsdir <directory>]"
echo "BuildArch can be: arm(default), armel, arm64, x86"
echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine, alpine3.13 or alpine3.14. If BuildArch is armel, LinuxCodeName is jessie(default) or tizen."
echo " for FreeBSD can be: freebsd12, freebsd13"
echo " for illumos can be: illumos."
echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD"
+ echo "llvmx[.y] - optional, LLVM version for LLVM related packages."
echo "--skipunmount - optional, will skip the unmount of rootfs folder."
echo "--use-mirror - optional, use mirror URL to fetch resources, when available."
exit 1
__AlpinePackages+=" icu-dev"
__AlpinePackages+=" libunwind-dev"
__AlpinePackages+=" lttng-ust-dev"
+__AlpinePackages+=" compiler-rt-static"
# CoreFX dependencies
__UbuntuPackages+=" libcurl4-openssl-dev"
no-lldb)
unset __LLDB_Package
;;
+ llvm*)
+ version="$(echo "$lowerI" | tr -d '[:alpha:]-=')"
+ parts=(${version//./ })
+ __LLVM_MajorVersion="${parts[0]}"
+ __LLVM_MinorVersion="${parts[1]}"
+ if [[ -z "$__LLVM_MinorVersion" && "$__LLVM_MajorVersion" -le 6 ]]; then
+ __LLVM_MinorVersion=0;
+ fi
+ ;;
xenial) # Ubuntu 16.04
if [ "$__CodeName" != "jessie" ]; then
__CodeName=xenial
fi
__UbuntuPackages+=" ${__LLDB_Package:-}"
+if [ ! -z "$__LLVM_MajorVersion" ]; then
+ __UbuntuPackages+=" libclang-common-${__LLVM_MajorVersion}${__LLVM_MinorVersion:+.$__LLVM_MinorVersion}-dev"
+fi
+
if [ -z "$__RootfsDir" ] && [ ! -z "$ROOTFS_DIR" ]; then
__RootfsDir=$ROOTFS_DIR
fi
# Use uname to determine what the CPU is, see https://en.wikipedia.org/wiki/Uname#Examples
cpuname=$(uname -m)
case $cpuname in
- aarch64)
+ arm64|aarch64)
buildarch=arm64
;;
loongarch64)
armv*l)
buildarch=arm
;;
- i686)
+ i[3-6]86)
buildarch=x86
;;
*)
.PARAMETER GlobalJsonFile
File path to global.json file
+.PARAMETER PathPromotion
+Optional switch to enable either promote native tools specified in the global.json to the path (in Azure Pipelines)
+or break the build if a native tool is not found on the path (on a local dev machine)
+
.NOTES
#>
[CmdletBinding(PositionalBinding=$false)]
[switch] $Force = $False,
[int] $DownloadRetries = 5,
[int] $RetryWaitTimeInSeconds = 30,
- [string] $GlobalJsonFile
+ [string] $GlobalJsonFile,
+ [switch] $PathPromotion
)
if (!$GlobalJsonFile) {
ConvertFrom-Json |
Select-Object -Expand 'native-tools' -ErrorAction SilentlyContinue
if ($NativeTools) {
- $NativeTools.PSObject.Properties | ForEach-Object {
- $ToolName = $_.Name
- $ToolVersion = $_.Value
- $LocalInstallerArguments = @{ ToolName = "$ToolName" }
- $LocalInstallerArguments += @{ InstallPath = "$InstallBin" }
- $LocalInstallerArguments += @{ BaseUri = "$BaseUri" }
- $LocalInstallerArguments += @{ CommonLibraryDirectory = "$EngCommonBaseDir" }
- $LocalInstallerArguments += @{ Version = "$ToolVersion" }
-
- if ($Verbose) {
- $LocalInstallerArguments += @{ Verbose = $True }
- }
- if (Get-Variable 'Force' -ErrorAction 'SilentlyContinue') {
- if($Force) {
- $LocalInstallerArguments += @{ Force = $True }
- }
- }
- if ($Clean) {
- $LocalInstallerArguments += @{ Clean = $True }
- }
-
- Write-Verbose "Installing $ToolName version $ToolVersion"
- Write-Verbose "Executing '$InstallerPath $($LocalInstallerArguments.Keys.ForEach({"-$_ '$($LocalInstallerArguments.$_)'"}) -join ' ')'"
- & $InstallerPath @LocalInstallerArguments
- if ($LASTEXITCODE -Ne "0") {
- $errMsg = "$ToolName installation failed"
- if ((Get-Variable 'DoNotAbortNativeToolsInstallationOnFailure' -ErrorAction 'SilentlyContinue') -and $DoNotAbortNativeToolsInstallationOnFailure) {
- $showNativeToolsWarning = $true
- if ((Get-Variable 'DoNotDisplayNativeToolsInstallationWarnings' -ErrorAction 'SilentlyContinue') -and $DoNotDisplayNativeToolsInstallationWarnings) {
- $showNativeToolsWarning = $false
+ if ($PathPromotion -eq $True) {
+ if ($env:SYSTEM_TEAMPROJECT) { # check to see if we're in an Azure pipelines build
+ $NativeTools.PSObject.Properties | ForEach-Object {
+ $ToolName = $_.Name
+ $ToolVersion = $_.Value
+
+ if ((Get-Command "$ToolName" -ErrorAction SilentlyContinue) -eq $null) {
+ if ($ToolVersion -eq "latest") {
+ $ToolVersion = ""
+ }
+ $ArcadeToolsDirectory = "C:\arcade-tools"
+ if (-not (Test-Path $ArcadeToolsDirectory)) {
+ Write-Error "Arcade tools directory '$ArcadeToolsDirectory' was not found; artifacts were not properly installed."
+ exit 1
}
- if ($showNativeToolsWarning) {
- Write-Warning $errMsg
+ $ToolDirectory = (Get-ChildItem -Path "$ArcadeToolsDirectory" -Filter "$ToolName-$ToolVersion*" | Sort-Object -Descending)[0]
+ if ([string]::IsNullOrWhiteSpace($ToolDirectory)) {
+ Write-Error "Unable to find directory for $ToolName $ToolVersion; please make sure the tool is installed on this image."
+ exit 1
}
- $toolInstallationFailure = $true
- } else {
- # We cannot change this to Write-PipelineTelemetryError because of https://github.com/dotnet/arcade/issues/4482
- Write-Host $errMsg
- exit 1
+ $BinPathFile = "$($ToolDirectory.FullName)\binpath.txt"
+ if (-not (Test-Path -Path "$BinPathFile")) {
+ Write-Error "Unable to find binpath.txt in '$($ToolDirectory.FullName)' ($ToolName $ToolVersion); artifact is either installed incorrectly or is not a bootstrappable tool."
+ exit 1
+ }
+ $BinPath = Get-Content "$BinPathFile"
+ $ToolPath = Convert-Path -Path $BinPath
+ Write-Host "Adding $ToolName to the path ($ToolPath)..."
+ Write-Host "##vso[task.prependpath]$ToolPath"
+ }
+ }
+ exit 0
+ } else {
+ $NativeTools.PSObject.Properties | ForEach-Object {
+ $ToolName = $_.Name
+ $ToolVersion = $_.Value
+
+ if ((Get-Command "$ToolName" -ErrorAction SilentlyContinue) -eq $null) {
+ Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message "$ToolName not found on path. Please install $ToolName $ToolVersion before proceeding."
+ }
}
+ exit 0
+ }
+ } else {
+ $NativeTools.PSObject.Properties | ForEach-Object {
+ $ToolName = $_.Name
+ $ToolVersion = $_.Value
+ $LocalInstallerArguments = @{ ToolName = "$ToolName" }
+ $LocalInstallerArguments += @{ InstallPath = "$InstallBin" }
+ $LocalInstallerArguments += @{ BaseUri = "$BaseUri" }
+ $LocalInstallerArguments += @{ CommonLibraryDirectory = "$EngCommonBaseDir" }
+ $LocalInstallerArguments += @{ Version = "$ToolVersion" }
+
+ if ($Verbose) {
+ $LocalInstallerArguments += @{ Verbose = $True }
+ }
+ if (Get-Variable 'Force' -ErrorAction 'SilentlyContinue') {
+ if($Force) {
+ $LocalInstallerArguments += @{ Force = $True }
+ }
+ }
+ if ($Clean) {
+ $LocalInstallerArguments += @{ Clean = $True }
+ }
+
+ Write-Verbose "Installing $ToolName version $ToolVersion"
+ Write-Verbose "Executing '$InstallerPath $($LocalInstallerArguments.Keys.ForEach({"-$_ '$($LocalInstallerArguments.$_)'"}) -join ' ')'"
+ & $InstallerPath @LocalInstallerArguments
+ if ($LASTEXITCODE -Ne "0") {
+ $errMsg = "$ToolName installation failed"
+ if ((Get-Variable 'DoNotAbortNativeToolsInstallationOnFailure' -ErrorAction 'SilentlyContinue') -and $DoNotAbortNativeToolsInstallationOnFailure) {
+ $showNativeToolsWarning = $true
+ if ((Get-Variable 'DoNotDisplayNativeToolsInstallationWarnings' -ErrorAction 'SilentlyContinue') -and $DoNotDisplayNativeToolsInstallationWarnings) {
+ $showNativeToolsWarning = $false
+ }
+ if ($showNativeToolsWarning) {
+ Write-Warning $errMsg
+ }
+ $toolInstallationFailure = $true
+ } else {
+ # We cannot change this to Write-PipelineTelemetryError because of https://github.com/dotnet/arcade/issues/4482
+ Write-Host $errMsg
+ exit 1
+ }
+ }
+ }
+
+ if ((Get-Variable 'toolInstallationFailure' -ErrorAction 'SilentlyContinue') -and $toolInstallationFailure) {
+ # We cannot change this to Write-PipelineTelemetryError because of https://github.com/dotnet/arcade/issues/4482
+ Write-Host 'Native tools bootstrap failed'
+ exit 1
}
- }
-
- if ((Get-Variable 'toolInstallationFailure' -ErrorAction 'SilentlyContinue') -and $toolInstallationFailure) {
- # We cannot change this to Write-PipelineTelemetryError because of https://github.com/dotnet/arcade/issues/4482
- Write-Host 'Native tools bootstrap failed'
- exit 1
}
}
else {
Write-Host "##vso[task.prependpath]$(Convert-Path -Path $InstallBin)"
return $InstallBin
}
- else {
+ elseif (-not ($PathPromotion)) {
Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message 'Native tools install directory does not exist, installation failed'
exit 1
}
<ItemGroup>
<!-- Clear references, the SDK may add some depending on UsuingToolXxx settings, but we only want to restore the following -->
<PackageReference Remove="@(PackageReference)"/>
+ <PackageReference Include="Microsoft.ManifestTool.CrossPlatform" Version="$(MicrosoftManifestToolCrossPlatformVersion)" />
+ <PackageReference Include="Microsoft.VisualStudioEng.MicroBuild.Core" Version="$(MicrosoftVisualStudioEngMicroBuildCoreVersion)" />
+ <PackageReference Include="Microsoft.VisualStudioEng.MicroBuild.Plugins.SwixBuild" Version="$(MicrosoftVisualStudioEngMicroBuildPluginsSwixBuildVersion)" />
<PackageReference Include="Microsoft.DotNet.IBCMerge" Version="$(MicrosoftDotNetIBCMergeVersion)" Condition="'$(UsingToolIbcOptimization)' == 'true'" />
<PackageReference Include="Drop.App" Version="$(DropAppVersion)" ExcludeAssets="all" Condition="'$(UsingToolVisualStudioIbcTraining)' == 'true'"/>
</ItemGroup>
$GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty
}
if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) {
- $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "16.10.0-preview2" -MemberType NoteProperty
+ $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.1.0" -MemberType NoteProperty
}
if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") {
$xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true
lclSource: ${{ parameters.LclSource }}
lclPackageId: ${{ parameters.LclPackageId }}
isCreatePrSelected: ${{ parameters.CreatePr }}
+ isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }}
${{ if eq(parameters.CreatePr, true) }}:
- isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }}
isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }}
${{ if eq(parameters.RepoType, 'gitHub') }}:
isShouldReusePrSelected: ${{ parameters.ReusePr }}
# Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
publishUsingPipelines: false
+ # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
+ publishAssetsImmediately: false
+
+ artifactsPublishingAdditionalParameters: ''
+
+ signingValidationAdditionalParameters: ''
+
jobs:
- job: Asset_Registry_Publish
dependsOn: ${{ parameters.dependsOn }}
- displayName: Publish to Build Asset Registry
+ ${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
+ displayName: Publish Assets
+ ${{ else }}:
+ displayName: Publish to Build Asset Registry
pool: ${{ parameters.pool }}
variables:
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- - name: _BuildConfig
- value: ${{ parameters.configuration }}
- group: Publish-Build-Assets
- group: AzureDevOps-Artifact-Feeds-Pats
- name: runCodesignValidationInjection
value: false
+ - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
+ - template: /eng/common/templates/post-build/common-variables.yml
steps:
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
condition: ${{ parameters.condition }}
continueOnError: ${{ parameters.continueOnError }}
- - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- - task: NuGetAuthenticate@0
+ - task: NuGetAuthenticate@0
- - task: PowerShell@2
- displayName: Enable cross-org NuGet feed authentication
- inputs:
- filePath: $(Build.SourcesDirectory)/eng/common/enable-cross-org-publishing.ps1
- arguments: -token $(dn-bot-all-orgs-artifact-feeds-rw)
+ - task: PowerShell@2
+ displayName: Enable cross-org NuGet feed authentication
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/enable-cross-org-publishing.ps1
+ arguments: -token $(dn-bot-all-orgs-artifact-feeds-rw)
- task: PowerShell@2
displayName: Publish Build Assets
/p:BuildAssetRegistryToken=$(MaestroAccessToken)
/p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com
/p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }}
- /p:Configuration=$(_BuildConfig)
/p:OfficialBuildId=$(Build.BuildNumber)
condition: ${{ parameters.condition }}
continueOnError: ${{ parameters.continueOnError }}
PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt'
PublishLocation: Container
ArtifactName: ReleaseConfigs
-
+
+ - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
+ - template: /eng/common/templates/post-build/setup-maestro-vars.yml
+ parameters:
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
+
+ - task: PowerShell@2
+ displayName: Publish Using Darc
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1
+ arguments: -BuildId $(BARBuildId)
+ -PublishingInfraVersion 3
+ -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)'
+ -MaestroToken '$(MaestroApiAccessToken)'
+ -WaitPublishingFinish true
+ -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}'
+ -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}'
+
- ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}:
- template: /eng/common/templates/steps/publish-logs.yml
parameters:
binlogPath: artifacts/log/Debug/Build.binlog
condition: ''
dependsOn: ''
+ pool: ''
jobs:
- job: SourceIndexStage1
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- group: source-dot-net stage1 variables
- pool:
- ${{ if eq(variables['System.TeamProject'], 'public') }}:
- name: NetCore1ESPool-Public
- demands: ImageOverride -equals Build.Server.Amd64.VS2019.Open
- ${{ if eq(variables['System.TeamProject'], 'internal') }}:
- name: NetCore1ESPool-Internal
- demands: ImageOverride -equals Build.Server.Amd64.VS2019
+ ${{ if ne(parameters.pool, '') }}:
+ pool: ${{ parameters.pool }}
+ ${{ if eq(parameters.pool, '') }}:
+ pool:
+ ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ name: NetCore1ESPool-Public
+ demands: ImageOverride -equals Build.Server.Amd64.VS2019.Open
+ ${{ if eq(variables['System.TeamProject'], 'internal') }}:
+ name: NetCore1ESPool-Internal
+ demands: ImageOverride -equals Build.Server.Amd64.VS2019
+
steps:
- ${{ each preStep in parameters.preSteps }}:
- ${{ preStep }}
# Optional: Override automatically derived dependsOn value for "publish build assets" job
publishBuildAssetsDependsOn: ''
+ # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage.
+ publishAssetsImmediately: false
+
+ # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml)
+ artifactsPublishingAdditionalParameters: ''
+ signingValidationAdditionalParameters: ''
+
# Optional: should run as a public build even in the internal project
# if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects.
runAsPublic: false
${{ parameter.key }}: ${{ parameter.value }}
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
-
- ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}:
- template: ../job/publish-build-assets.yml
parameters:
runAsPublic: ${{ parameters.runAsPublic }}
publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }}
+ publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }}
enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }}
+ artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }}
+ signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }}
default:
- Validate
+ # Optional: Call asset publishing rather than running in a separate stage
+ - name: publishAssetsImmediately
+ type: boolean
+ default: false
+
stages:
- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}:
- stage: Validate
name: VSEngSS-MicroBuild2022-1ES
demands: Cmd
# If it's not devdiv, it's dnceng
- ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
+ ${{ else }}:
name: NetCore1ESPool-Internal
demands: ImageOverride -equals Build.Server.Amd64.VS2019
name: VSEngSS-MicroBuild2022-1ES
demands: Cmd
# If it's not devdiv, it's dnceng
- ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
+ ${{ else }}:
name: NetCore1ESPool-Internal
demands: ImageOverride -equals Build.Server.Amd64.VS2019
steps:
name: VSEngSS-MicroBuild2022-1ES
demands: Cmd
# If it's not devdiv, it's dnceng
- ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
+ ${{ else }}:
name: NetCore1ESPool-Internal
demands: ImageOverride -equals Build.Server.Amd64.VS2019
steps:
artifactNames: ${{ parameters.SDLValidationParameters.artifactNames }}
downloadArtifacts: ${{ parameters.SDLValidationParameters.downloadArtifacts }}
-- stage: publish_using_darc
- ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}:
- dependsOn: ${{ parameters.publishDependsOn }}
- ${{ if and(ne(parameters.enableNugetValidation, 'true'), ne(parameters.enableSigningValidation, 'true'), ne(parameters.enableSourceLinkValidation, 'true'), ne(parameters.SDLValidationParameters.enable, 'true')) }}:
- dependsOn: ${{ parameters.validateDependsOn }}
- displayName: Publish using Darc
- variables:
- - template: common-variables.yml
- jobs:
- - job:
- displayName: Publish Using Darc
- timeoutInMinutes: 120
- pool:
- # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}:
+ - stage: publish_using_darc
+ ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}:
+ dependsOn: ${{ parameters.publishDependsOn }}
+ ${{ else }}:
+ dependsOn: ${{ parameters.validateDependsOn }}
+ displayName: Publish using Darc
+ variables:
+ - template: common-variables.yml
+ jobs:
+ - job:
+ displayName: Publish Using Darc
+ timeoutInMinutes: 120
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
name: VSEngSS-MicroBuild2022-1ES
demands: Cmd
# If it's not devdiv, it's dnceng
- ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
+ ${{ else }}:
name: NetCore1ESPool-Internal
demands: ImageOverride -equals Build.Server.Amd64.VS2019
- steps:
- - template: setup-maestro-vars.yml
- parameters:
- BARBuildId: ${{ parameters.BARBuildId }}
- PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
+ steps:
+ - template: setup-maestro-vars.yml
+ parameters:
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
- - task: NuGetAuthenticate@0
+ - task: NuGetAuthenticate@0
- - task: PowerShell@2
- displayName: Publish Using Darc
- inputs:
- filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1
- arguments: -BuildId $(BARBuildId)
- -PublishingInfraVersion ${{ parameters.publishingInfraVersion }}
- -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)'
- -MaestroToken '$(MaestroApiAccessToken)'
- -WaitPublishingFinish true
- -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}'
- -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}'
\ No newline at end of file
+ - task: PowerShell@2
+ displayName: Publish Using Darc
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1
+ arguments: -BuildId $(BARBuildId)
+ -PublishingInfraVersion ${{ parameters.publishingInfraVersion }}
+ -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)'
+ -MaestroToken '$(MaestroApiAccessToken)'
+ -WaitPublishingFinish true
+ -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}'
+ -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}'
\ No newline at end of file
# If the version of msbuild is going to be xcopied,
# use this version. Version matches a package here:
- # https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-eng&package=RoslynTools.MSBuild&protocolType=NuGet&version=16.10.0-preview2&view=overview
- $defaultXCopyMSBuildVersion = '16.10.0-preview2'
+ # https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-eng&package=RoslynTools.MSBuild&protocolType=NuGet&version=17.1.0&view=overview
+ $defaultXCopyMSBuildVersion = '17.1.0'
if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs }
$vsMinVersionStr = if ($vsRequirements.version) { $vsRequirements.version } else { $vsMinVersionReqdStr }
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /PDBCOMPRESS")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:1572864")
+ if(EXISTS ${CLR_SOURCELINK_FILE_PATH})
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /sourcelink:${CLR_SOURCELINK_FILE_PATH}")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /sourcelink:${CLR_SOURCELINK_FILE_PATH}")
+ endif(EXISTS ${CLR_SOURCELINK_FILE_PATH})
+
# Checked build specific flags
add_linker_flag(/INCREMENTAL:NO CHECKED) # prevent "warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:REF' specification"
add_linker_flag(/OPT:REF CHECKED)
add_compile_options(-Wno-unused-but-set-variable)
+ # Turn off floating point expression contraction because it is considered a value changing
+ # optimization in the IEEE 754 specification and is therefore considered unsafe.
+ add_compile_options(-ffp-contract=off)
+
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-unknown-warning-option)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_ANDROID>)
elseif(CLR_CMAKE_TARGET_LINUX)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_LINUX>)
+ if(CLR_CMAKE_TARGET_LINUX_MUSL)
+ add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_LINUX_MUSL>)
+ endif()
elseif(CLR_CMAKE_TARGET_NETBSD)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_NETBSD>)
elseif(CLR_CMAKE_TARGET_SUNOS)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_SUNOS>)
+ if(CLR_CMAKE_TARGET_OS_ILLUMOS)
+ add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_ILLUMOS>)
+ endif()
endif()
else(CLR_CMAKE_TARGET_UNIX)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_WINDOWS>)
# /W3 is added by default by CMake, so remove it
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/W3" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
- # set default warning level to 3 but allow targets to override it.
- add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/W$<GENEX_EVAL:$<IF:$<BOOL:$<TARGET_PROPERTY:MSVC_WARNING_LEVEL>>,$<TARGET_PROPERTY:MSVC_WARNING_LEVEL>,3>>>)
+
+ # [[! Microsoft.Security.SystemsADM.10086 !]] - SDL required warnings
+ # set default warning level to 4 but allow targets to override it.
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/W$<GENEX_EVAL:$<IF:$<BOOL:$<TARGET_PROPERTY:MSVC_WARNING_LEVEL>>,$<TARGET_PROPERTY:MSVC_WARNING_LEVEL>,4>>>)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/WX>) # treat warnings as errors
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/Oi>) # enable intrinsics
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/Oy->) # disable suppressing of the creation of frame pointers on the call stack for quicker function calls
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4456>) # declaration of 'identifier' hides previous local declaration
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4457>) # declaration of 'identifier' hides function parameter
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4458>) # declaration of 'identifier' hides class member
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4702>) # unreachable code
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4733>) # Inline asm assigning to 'FS:0' : handler not registered as safe handler
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4838>) # conversion from 'type_1' to 'type_2' requires a narrowing conversion
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4960>) # 'function' is too big to be profiled
# Treat Warnings as Errors:
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4007>) # 'main' : must be __cdecl.
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4013>) # 'function' undefined - assuming extern returning int.
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4018>) # 'expression' : signed/unsigned mismatch
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4055>) # 'conversion' : from data pointer 'type1' to function pointer 'type2'
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4102>) # "'%$S' : unreferenced label".
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4146>) # unary minus operator applied to unsigned type, result still unsigned
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4242>) # 'identifier' : conversion from 'type1' to 'type2', possible loss of data
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4244>) # 'conversion' conversion from 'type1' to 'type2', possible loss of data
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4267>) # 'var' : conversion from 'size_t' to 'type', possible loss of data
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4302>) # 'conversion' : truncation from 'type 1' to 'type 2'
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4308>) # negative integral constant converted to unsigned type
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4509>) # nonstandard extension used: 'function' uses SEH and 'object' has destructor
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4510>) # 'class' : default constructor could not be generated
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4532>) # 'continue' : jump out of __finally/finally block has undefined behavior during termination handling
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4533>) # initialization of 'variable' is skipped by 'instruction'
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4551>) # Function call missing argument list.
- add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4700>) # Local used w/o being initialized.
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4610>) # object 'class' can never be instantiated - user-defined constructor required
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4611>) # interaction between 'function' and C++ object destruction is non-portable
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4640>) # 'instance' : construction of local static object is not thread-safe
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4700>) # Local used w/o being initialized.
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4701>) # Potentially uninitialized local variable 'name' used
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4703>) # Potentially uninitialized local pointer variable 'name' used
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4789>) # destination of memory copy is too small
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4806>) # Unsafe operation involving type 'bool'.
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4995>) # 'function': name was marked as #pragma deprecated
+ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/we4996>) # 'function': was declared deprecated also 'std::': Function call with parameters that are potentially unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
# Set Warning Level 3:
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/w34092>) # Sizeof returns 'unsigned long'.
elseif (NOT CLR_CMAKE_HOST_BROWSER)
enable_language(ASM)
-endif(CLR_CMAKE_HOST_WIN32)
+endif(CLR_CMAKE_HOST_WIN32)
\ No newline at end of file
+++ /dev/null
-# This file probes for the prerequisites for the build system, and outputs commands for eval'ing
-# from the cmd scripts to set variables (and exit on error)
-
-function GetCMakeVersions
-{
- $items = @()
- $items += @(Get-ChildItem hklm:\SOFTWARE\Wow6432Node\Kitware -ErrorAction SilentlyContinue)
- $items += @(Get-ChildItem hklm:\SOFTWARE\Kitware -ErrorAction SilentlyContinue)
- return $items | where { $_.PSChildName.StartsWith("CMake") }
-}
-
-function GetCMakeInfo($regKey)
-{
- try {
- $version = [System.Version] $regKey.PSChildName.Split(' ')[1]
- }
- catch {
- return $null
- }
- $itemProperty = Get-ItemProperty $regKey.PSPath;
- if (Get-Member -inputobject $itemProperty -name "InstallDir" -Membertype Properties) {
- $cmakeDir = $itemProperty.InstallDir
- }
- else {
- $cmakeDir = $itemProperty.'(default)'
- }
- $cmakePath = [System.IO.Path]::Combine($cmakeDir, "bin\cmake.exe")
- if (![System.IO.File]::Exists($cmakePath)) {
- return $null
- }
- return @{'version' = $version; 'path' = $cmakePath}
-}
-
-function LocateCMake
-{
- $errorMsg = "CMake is a pre-requisite to build this repository but it was not found on the path. Please install CMake from http://www.cmake.org/download/ and ensure it is on your path."
- $inPathPath = (get-command cmake.exe -ErrorAction SilentlyContinue)
- if ($inPathPath -ne $null) {
- # Resolve the first version of CMake if multiple commands are found
- if ($inPathPath.Length -gt 1) {
- return $inPathPath[0].Path
- }
- return $inPathPath.Path
- }
- # Let us hope that CMake keep using their current version scheme
- $validVersions = @()
- foreach ($regKey in GetCMakeVersions) {
- $info = GetCMakeInfo($regKey)
- if ($info -ne $null) {
- $validVersions += @($info)
- }
- }
- $newestCMakePath = ($validVersions |
- Sort-Object -property @{Expression={$_.version}; Ascending=$false} |
- select -first 1).path
- if ($newestCMakePath -eq $null) {
- Throw $errorMsg
- }
- return $newestCMakePath
-}
-
-try {
- $cmakePath = LocateCMake
- [System.Console]::WriteLine("set CMakePath=" + $cmakePath)
-
-}
-catch {
- [System.Console]::Error.WriteLine($_.Exception.Message)
- [System.Console]::WriteLine("exit /b 1")
-}
},
"msbuild-sdks": {
"Microsoft.Build.NoTargets": "2.0.1",
- "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22181.2"
+ "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22269.3"
}
}
class LocalHelpBuilder : IHelpBuilder
{
private readonly CommandService _commandService;
- private readonly IConsole _console;
+ private readonly LocalConsole _console;
private readonly bool _useHelpBuilder;
public LocalHelpBuilder(CommandService commandService, IConsole console, bool useHelpBuilder)
{
_commandService = commandService;
- _console = console;
+ _console = (LocalConsole)console;
_useHelpBuilder = useHelpBuilder;
}
bool useHelpBuilder = _useHelpBuilder;
if (_commandService._commandHandlers.TryGetValue(command.Name, out CommandHandler handler))
{
- if (handler.InvokeHelp(_commandService.Parser, LocalConsole.ToServices(_console))) {
+ if (handler.InvokeHelp(_commandService.Parser, _console.Services)) {
return;
}
useHelpBuilder = true;
}
if (useHelpBuilder)
{
- var helpBuilder = new HelpBuilder(_console, maxWidth: LocalConsole.ToConsoleService(_console).WindowWidth);
+ var helpBuilder = new HelpBuilder(_console, maxWidth: _console.ConsoleService.WindowWidth);
helpBuilder.Write(command);
}
}
/// </summary>
class LocalConsole : IConsole
{
- public static IServiceProvider ToServices(IConsole console) => ((LocalConsole)console)._services;
+ private IConsoleService _console;
- public static IConsoleService ToConsoleService(IConsole console) => ((LocalConsole)console)._console;
+ public LocalConsole(IServiceProvider services)
+ {
+ Services = services;
+ Out = new StandardStreamWriter((text) => ConsoleService.Write(text));
+ Error = new StandardStreamWriter((text) => ConsoleService.WriteError(text));
+ }
- private readonly IServiceProvider _services;
- private readonly IConsoleService _console;
+ internal readonly IServiceProvider Services;
- public LocalConsole(IServiceProvider services)
+ internal IConsoleService ConsoleService
{
- _services = services;
- _console = services.GetService<IConsoleService>();
- Debug.Assert(_console != null);
- Out = new StandardStreamWriter((text) => _console.Write(text));
- Error = new StandardStreamWriter((text) => _console.WriteError(text));
+ get
+ {
+ if (_console is null)
+ {
+ _console = Services.GetService<IConsoleService>();
+ }
+ return _console;
+ }
}
#region IConsole
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.FileFormats;
+using Microsoft.FileFormats.ELF;
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ /// <summary>
+ /// Disposable ELFFile wrapper around the module file.
+ /// </summary>
+ public class ELFModule : ELFFile, IDisposable
+ {
+ private readonly Stream _stream;
+
+ /// <summary>
+ /// Opens and returns an ELFFile instance from the local file path
+ /// </summary>
+ /// <param name="filePath">ELF file to open</param>
+ /// <returns>ELFFile instance or null</returns>
+ public static ELFModule OpenFile(string filePath)
+ {
+ Stream stream = Utilities.TryOpenFile(filePath);
+ if (stream is not null)
+ {
+ try
+ {
+ ELFModule elfModule = new(stream);
+ if (!elfModule.IsValid())
+ {
+ Trace.TraceError($"OpenELFFile: not a valid file");
+ return null;
+ }
+ return elfModule;
+ }
+ catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
+ {
+ Trace.TraceError($"OpenELFFile: exception {ex.Message}");
+ }
+ }
+ return null;
+ }
+
+ public ELFModule(Stream stream) :
+ base(new StreamAddressSpace(stream), position: 0, isDataSourceVirtualAddressSpace: false)
+ {
+ _stream = stream;
+ }
+
+ public void Dispose() => _stream.Dispose();
+ }
+}
\ No newline at end of file
else
{
// Find or download the ELF image, if one.
- Reader virtualAddressReader = module.Services.GetService<ELFFile>()?.VirtualAddressReader;
+ Reader virtualAddressReader = module.Services.GetService<ELFModule>()?.VirtualAddressReader;
if (virtualAddressReader is null)
{
// Find or download the MachO image, if one.
- virtualAddressReader = module.Services.GetService<MachOFile>()?.VirtualAddressReader;
+ virtualAddressReader = module.Services.GetService<MachOModule>()?.VirtualAddressReader;
}
if (virtualAddressReader is not null)
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.FileFormats;
+using Microsoft.FileFormats.MachO;
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ /// <summary>
+ /// Disposable MachOFile wrapper around the module file.
+ /// </summary>
+ public class MachOModule : MachOFile, IDisposable
+ {
+ private readonly Stream _stream;
+
+ /// <summary>
+ /// Opens and returns an MachOFile instance from the local file path
+ /// </summary>
+ /// <param name="filePath">MachO file to open</param>
+ /// <returns>MachOFile instance or null</returns>
+ public static MachOModule OpenFile(string filePath)
+ {
+ Stream stream = Utilities.TryOpenFile(filePath);
+ if (stream is not null)
+ {
+ try
+ {
+ var machoModule = new MachOModule(stream);
+ if (!machoModule.IsValid())
+ {
+ Trace.TraceError($"OpenMachOFile: not a valid file");
+ return null;
+ }
+ return machoModule;
+ }
+ catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
+ {
+ Trace.TraceError($"OpenMachOFile: exception {ex.Message}");
+ }
+ }
+ return null;
+ }
+
+ public MachOModule(Stream stream) :
+ base(new StreamAddressSpace(stream), position: 0, dataSourceIsVirtualAddressSpace: false)
+ {
+ _stream = stream;
+ }
+
+ public void Dispose() => _stream.Dispose();
+ }
+}
InitializePEInfo = 0x10,
InitializeVersion = 0x20,
InitializeProductVersion = 0x40,
+ InitializeSymbolFileName = 0x80
}
private readonly IDisposable _onChangeEvent;
private IEnumerable<PdbFileInfo> _pdbFileInfos;
protected ImmutableArray<byte> _buildId;
private PEFile _peFile;
+ private string _symbolFileName;
public readonly ServiceProvider ServiceProvider;
{
ServiceProvider = new ServiceProvider();
ServiceProvider.AddServiceFactoryWithNoCaching<PEFile>(() => GetPEInfo());
+ ServiceProvider.AddService<IExportSymbols>(this);
- ServiceProvider.AddServiceFactory<PEReader>(() => Utilities.OpenPEReader(ModuleService.SymbolService.DownloadModule(this)));
- if (target.OperatingSystem == OSPlatform.Linux) {
- ServiceProvider.AddServiceFactory<ELFFile>(() => Utilities.OpenELFFile(ModuleService.SymbolService.DownloadModule(this)));
+ ServiceProvider.AddServiceFactory<PEReader>(() => {
+ if (!IndexTimeStamp.HasValue || !IndexFileSize.HasValue) {
+ return null;
+ }
+ return Utilities.OpenPEReader(ModuleService.SymbolService.DownloadModuleFile(this));
+ });
+
+ if (target.OperatingSystem == OSPlatform.Linux)
+ {
+ ServiceProvider.AddServiceFactory<ELFModule>(() => {
+ if (BuildId.IsDefaultOrEmpty) {
+ return null;
+ }
+ return ELFModule.OpenFile(ModuleService.SymbolService.DownloadModuleFile(this));
+ });
+ ServiceProvider.AddServiceFactory<ELFFile>(() => {
+ Stream stream = ModuleService.MemoryService.CreateMemoryStream();
+ var elfFile = new ELFFile(new StreamAddressSpace(stream), ImageBase, true);
+ return elfFile.IsValid() ? elfFile : null;
+ });
}
- if (target.OperatingSystem == OSPlatform.OSX) {
- ServiceProvider.AddServiceFactory<MachOFile>(() => Utilities.OpenMachOFile(ModuleService.SymbolService.DownloadModule(this)));
+
+ if (target.OperatingSystem == OSPlatform.OSX)
+ {
+ ServiceProvider.AddServiceFactory<MachOModule>(() => {
+ if (BuildId.IsDefaultOrEmpty) {
+ return null;
+ }
+ return MachOModule.OpenFile(ModuleService.SymbolService.DownloadModuleFile(this));
+ });
+ ServiceProvider.AddServiceFactory<MachOFile>(() => {
+ Stream stream = ModuleService.MemoryService.CreateMemoryStream();
+ var machoFile = new MachOFile(new StreamAddressSpace(stream), ImageBase, true);
+ return machoFile.IsValid() ? machoFile : null;
+ });
}
+
_onChangeEvent = target.Services.GetService<ISymbolService>()?.OnChangeEvent.Register(() => {
- ServiceProvider.RemoveService(typeof(MachOFile));
- ServiceProvider.RemoveService(typeof(ELFFile));
+ ServiceProvider.RemoveService(typeof(MachOModule));
+ ServiceProvider.RemoveService(typeof(ELFModule));
ServiceProvider.RemoveService(typeof(PEReader));
});
- ServiceProvider.AddService<IExportSymbols>(this);
}
public void Dispose()
}
}
- public IEnumerable<PdbFileInfo> PdbFileInfos
- {
- get
- {
- GetPEInfo();
- Debug.Assert(_pdbFileInfos is not null);
- return _pdbFileInfos;
- }
- }
-
public virtual ImmutableArray<byte> BuildId
{
get
}
}
- public abstract VersionData VersionData { get; }
+ public IEnumerable<PdbFileInfo> GetPdbFileInfos()
+ {
+ GetPEInfo();
+ Debug.Assert(_pdbFileInfos is not null);
+ return _pdbFileInfos;
+ }
- public abstract string VersionString { get; }
+ public string GetSymbolFileName()
+ {
+ if (InitializeValue(Flags.InitializeSymbolFileName))
+ {
+ if (Target.OperatingSystem == OSPlatform.Linux)
+ {
+ try
+ {
+ Stream stream = ModuleService.RawMemoryService.CreateMemoryStream();
+ var elfFile = new ELFFile(new StreamAddressSpace(stream), ImageBase, true);
+ if (elfFile.IsValid())
+ {
+ ELFSection section = elfFile.FindSectionByName(".gnu_debuglink");
+ if (section != null)
+ {
+ _symbolFileName = section.Contents.Read<string>(0);
+ }
+ }
+ }
+ catch (Exception ex) when
+ (ex is InvalidVirtualAddressException ||
+ ex is ArgumentOutOfRangeException ||
+ ex is IndexOutOfRangeException ||
+ ex is BadInputFormatException)
+
+ {
+ Trace.TraceWarning("ELF .gnu_debuglink section in {0}: {1}", this, ex.Message);
+ }
+ }
+ }
+ return _symbolFileName;
+ }
+
+ public abstract VersionData GetVersionData();
+
+ public abstract string GetVersionString();
+
+ public abstract string LoadSymbols();
#endregion
{
if (Target.OperatingSystem == OSPlatform.Windows)
{
- Stream stream = ModuleService.MemoryService.CreateMemoryStream(ImageBase, ImageSize);
- PEFile image = new(new StreamAddressSpace(stream), isDataSourceVirtualAddressSpace: true);
- if (image.IsValid())
+ PEFile image = Services.GetService<PEFile>();
+ if (image is not null)
{
if (image.TryGetExportSymbol(name, out ulong offset))
{
else
{
// If we can't get the version from the PE, search for version string embedded in the module data
- string versionString = VersionString;
+ string versionString = GetVersionString();
if (versionString != null)
{
int spaceIndex = versionString.IndexOf(' ');
}
}
}
- else
- {
- Trace.TraceInformation($"Module.GetVersion no version string");
- }
}
return versionData;
{
// First try getting the PE info as loaded layout (native Windows DLLs and most managed PEs).
peFile = GetPEInfo(isVirtual: true, address, size, out List<PdbFileInfo> pdbs, out Module.Flags flags);
- if (peFile is null || pdbs.Count == 0)
+
+ // Continue only if marked as a PE. This bit regardless of the layout if the module has a PE header/signature.
+ if ((flags & Module.Flags.IsPEImage) != 0)
{
- // If PE file is invalid or there are no PDB records, try getting the PE info as file layout. No PDB records can mean
- // that either the layout is wrong or that there really no PDB records. If file layout doesn't have any pdb records
- // either default to loaded layout PEFile.
- PEFile peFileLayout = GetPEInfo(isVirtual: false, address, size, out List<PdbFileInfo> pdbsFileLayout, out Module.Flags flagsFileLayout);
- if (peFileLayout is not null && (peFile is null || pdbsFileLayout.Count > 0))
+ if (peFile is null || pdbs.Count == 0)
{
- flags = flagsFileLayout;
- pdbs = pdbsFileLayout;
- peFile = peFileLayout;
+ // If PE file is invalid or there are no PDB records, try getting the PE info as file layout. No PDB records can mean
+ // that either the layout is wrong or that there really no PDB records. If file layout doesn't have any pdb records
+ // either default to loaded layout PEFile.
+ PEFile peFileLayout = GetPEInfo(isVirtual: false, address, size, out List<PdbFileInfo> pdbsFileLayout, out Module.Flags flagsFileLayout);
+ Debug.Assert((flagsFileLayout & Module.Flags.IsPEImage) != 0);
+ if (peFileLayout is not null && (peFile is null || pdbsFileLayout.Count > 0))
+ {
+ flags = flagsFileLayout;
+ pdbs = pdbsFileLayout;
+ peFile = peFileLayout;
+ }
+ }
+ if (peFile is not null)
+ {
+ moduleFlags |= flags;
+ pdbFileInfos = pdbs;
}
- }
- if (peFile is not null)
- {
- moduleFlags |= flags;
- pdbFileInfos = pdbs;
}
}
flags |= isVirtual ? Module.Flags.IsLoadedLayout : Module.Flags.IsFileLayout;
return peFile;
}
- else
- {
- Trace.TraceError($"GetPEInfo: PE invalid {address:X16} isVirtual {isVirtual}");
- }
}
catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException)
{
/// <returns>build id or null</returns>
internal byte[] GetBuildId(ulong address)
{
+ // This code is called by the image mapping memory service so it needs to use the
+ // original or raw memory service to prevent recursion so it can't use the ELFFile
+ // or MachOFile instance that is available from the IModule.Services provider.
Stream stream = RawMemoryService.CreateMemoryStream();
byte[] buildId = null;
try
{
buildId = elfFile.BuildID;
}
- else
- {
- Trace.TraceError($"GetBuildId: invalid ELF file {address:X16}");
- }
}
else if (Target.OperatingSystem == OSPlatform.OSX)
{
{
buildId = machOFile.Uuid;
}
- else
- {
- Trace.TraceError($"GetBuildId: invalid MachO file {address:X16}");
- }
}
}
catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
/// <summary>
/// Get the version string from a Linux or MacOS image
/// </summary>
- /// <param name="address">image base</param>
+ /// <param name="module">module to get version string</param>
/// <returns>version string or null</returns>
- protected string GetVersionString(ulong address)
+ protected string GetVersionString(IModule module)
{
- Stream stream = MemoryService.CreateMemoryStream();
try
{
- if (Target.OperatingSystem == OSPlatform.Linux)
+ ELFFile elfFile = module.Services.GetService<ELFFile>();
+ if (elfFile is not null)
{
- var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
- if (elfFile.IsValid())
+ foreach (ELFProgramHeader programHeader in elfFile.Segments.Select((segment) => segment.Header))
{
- foreach (ELFProgramHeader programHeader in elfFile.Segments.Select((segment) => segment.Header))
+ uint flags = MemoryService.PointerSize == 8 ? programHeader.Flags : programHeader.Flags32;
+ if (programHeader.Type == ELFProgramHeaderType.Load &&
+ (flags & (uint)ELFProgramHeaderAttributes.Writable) != 0)
{
- uint flags = MemoryService.PointerSize == 8 ? programHeader.Flags : programHeader.Flags32;
- if (programHeader.Type == ELFProgramHeaderType.Load &&
- (flags & (uint)ELFProgramHeaderAttributes.Writable) != 0)
+ ulong loadAddress = programHeader.VirtualAddress.Value;
+ long loadSize = (long)programHeader.VirtualSize;
+ if (SearchVersionString(module.ImageBase + loadAddress, loadSize, out string productVersion))
{
- ulong loadAddress = programHeader.VirtualAddress.Value;
- long loadSize = (long)programHeader.VirtualSize;
- if (SearchVersionString(address + loadAddress, loadSize, out string productVersion))
- {
- return productVersion;
- }
+ return productVersion;
}
}
- Trace.TraceInformation($"GetVersionString: not found in ELF file {address:X16}");
- }
- else
- {
- Trace.TraceError($"GetVersionString: invalid ELF file {address:X16}");
}
+ Trace.TraceInformation($"GetVersionString: not found in ELF file {module}");
}
- else if (Target.OperatingSystem == OSPlatform.OSX)
+ else
{
- var machOFile = new MachOFile(new StreamAddressSpace(stream), address, true);
- if (machOFile.IsValid())
+ MachOFile machOFile = module.Services.GetService<MachOFile>();
+ if (machOFile is not null)
{
foreach (MachSegmentLoadCommand loadCommand in machOFile.Segments.Select((segment) => segment.LoadCommand))
{
if (loadCommand.Command == LoadCommandType.Segment64 &&
- (loadCommand.InitProt & VmProtWrite) != 0 &&
+ (loadCommand.InitProt & VmProtWrite) != 0 &&
loadCommand.SegName.ToString() != "__LINKEDIT")
{
ulong loadAddress = loadCommand.VMAddress + machOFile.PreferredVMBaseAddress;
}
}
}
- Trace.TraceInformation($"GetVersionString: not found in MachO file {address:X16}");
+ Trace.TraceInformation($"GetVersionString: not found in MachO file {module}");
}
else
{
- Trace.TraceError($"GetVersionString: invalid MachO file {address:X16}");
+ Trace.TraceError($"GetVersionString: unsupported module {module} or platform {Target.OperatingSystem}");
}
}
- else
- {
- Trace.TraceError("GetVersionString: unsupported platform {0}", Target.OperatingSystem);
- }
}
catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
{
- Trace.TraceError($"GetVersionString: {address:X16} exception {ex.Message}");
+ Trace.TraceError($"GetVersionString: {module} exception {ex.Message}");
}
return null;
}
}
}
- public override VersionData VersionData
+ public override VersionData GetVersionData()
{
- get
+ if (InitializeValue(Module.Flags.InitializeVersion))
{
- if (InitializeValue(Module.Flags.InitializeVersion))
+ if (_moduleInfo.Version != EmptyVersionInfo)
{
- if (_moduleInfo.Version != EmptyVersionInfo)
- {
- _versionData = _moduleInfo.Version.ToVersionData();
- }
- else
+ _versionData = _moduleInfo.Version.ToVersionData();
+ }
+ else
+ {
+ if (_moduleService.Target.OperatingSystem != OSPlatform.Windows)
{
- if (_moduleService.Target.OperatingSystem != OSPlatform.Windows)
- {
- _versionData = GetVersion();
- }
+ _versionData = GetVersion();
}
}
- return _versionData;
}
+ return _versionData;
}
- public override string VersionString
+ public override string GetVersionString()
{
- get
+ if (InitializeValue(Module.Flags.InitializeProductVersion))
{
- if (InitializeValue(Module.Flags.InitializeProductVersion))
+ if (_moduleService.Target.OperatingSystem != OSPlatform.Windows && !IsPEImage)
{
- if (_moduleService.Target.OperatingSystem != OSPlatform.Windows && !IsPEImage)
- {
- _versionString = _moduleService.GetVersionString(ImageBase);
- }
+ _versionString = _moduleService.GetVersionString(this);
}
- return _versionString;
}
+ return _versionString;
+ }
+
+ public override string LoadSymbols()
+ {
+ return _moduleService.SymbolService.DownloadSymbolFile(this);
}
#endregion
{
try
{
- VersionData versionData = ModuleService.GetModuleFromBaseAddress(baseAddress).VersionData;
+ VersionData versionData = ModuleService.GetModuleFromBaseAddress(baseAddress).GetVersionData();
if (versionData is not null)
{
version = versionData.ToVersionInfo();
}
}
+ /// <summary>
+ /// The time out in minutes passed to the HTTP symbol store when not overridden in AddSymbolServer.
+ /// </summary>
+ public int DefaultTimeout { get; set; } = 4;
+
+ /// <summary>
+ /// The retry count passed to the HTTP symbol store when not overridden in AddSymbolServer.
+ /// </summary>
+ public int DefaultRetryCount { get; set; } = 0;
+
+ /// <summary>
+ /// Reset any HTTP symbol stores marked with a client failure
+ /// </summary>
+ public void Reset() => ForEachSymbolStore<HttpSymbolStore>((httpSymbolStore) => httpSymbolStore.ResetClientFailure());
+
/// <summary>
/// Parses the Windows debugger symbol path (srv*, cache*, etc.).
/// </summary>
}
if (symbolServerPath != null)
{
- if (!AddSymbolServer(msdl: false, symweb: false, symbolServerPath.Trim(), authToken: null, timeoutInMinutes: 0))
+ if (!AddSymbolServer(msdl: false, symweb: false, symbolServerPath.Trim()))
{
return false;
}
/// <param name="msdl">if true, use the public Microsoft server</param>
/// <param name="symweb">if true, use symweb internal server and protocol (file.ptr)</param>
/// <param name="symbolServerPath">symbol server url (optional)</param>
- /// <param name="authToken"></param>
- /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional)</param>
+ /// <param name="authToken">PAT for secure symbol server (optional)</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
/// <returns>if false, failure</returns>
public bool AddSymbolServer(
bool msdl,
bool symweb,
- string symbolServerPath,
- string authToken,
- int timeoutInMinutes)
+ string symbolServerPath = null,
+ string authToken = null,
+ int? timeoutInMinutes = null,
+ int? retryCount = null)
{
bool internalServer = false;
{
httpSymbolStore = new HttpSymbolStore(Tracer.Instance, store, uri, personalAccessToken: authToken);
}
- if (timeoutInMinutes != 0)
- {
- httpSymbolStore.Timeout = TimeSpan.FromMinutes(timeoutInMinutes);
- }
+ httpSymbolStore.Timeout = TimeSpan.FromMinutes(timeoutInMinutes.GetValueOrDefault(DefaultTimeout));
+ httpSymbolStore.RetryCount = retryCount.GetValueOrDefault(DefaultRetryCount);
SetSymbolStore(httpSymbolStore);
}
}
/// </summary>
/// <param name="module">module interface</param>
/// <returns>module path or null</returns>
- public string DownloadModule(IModule module)
+ public string DownloadModuleFile(IModule module)
{
- string downloadFilePath = DownloadPE(module);
+ string downloadFilePath = DownloadPE(module, KeyTypeFlags.IdentityKey);
if (downloadFilePath is null)
{
if (module.Target.OperatingSystem == OSPlatform.Linux)
{
- downloadFilePath = DownloadELF(module);
+ downloadFilePath = DownloadELF(module, KeyTypeFlags.IdentityKey);
}
else if (module.Target.OperatingSystem == OSPlatform.OSX)
{
- downloadFilePath = DownloadMachO(module);
+ downloadFilePath = DownloadMachO(module, KeyTypeFlags.IdentityKey);
+ }
+ }
+ return downloadFilePath;
+ }
+
+ /// <summary>
+ /// Downloads the symbol file for module
+ /// </summary>
+ /// <param name="module">module interface</param>
+ /// <returns>module path or null</returns>
+ public string DownloadSymbolFile(IModule module)
+ {
+ string downloadFilePath = DownloadPE(module, KeyTypeFlags.SymbolKey);
+ if (downloadFilePath is null)
+ {
+ if (module.Target.OperatingSystem == OSPlatform.Linux)
+ {
+ downloadFilePath = DownloadELF(module, KeyTypeFlags.SymbolKey);
+ }
+ else if (module.Target.OperatingSystem == OSPlatform.OSX)
+ {
+ downloadFilePath = DownloadMachO(module, KeyTypeFlags.SymbolKey);
}
}
return downloadFilePath;
/// Finds or downloads the PE module
/// </summary>
/// <param name="module">module instance</param>
+ /// <param name="flags"></param>
/// <returns>module path or null</returns>
- private string DownloadPE(IModule module)
+ private string DownloadPE(IModule module, KeyTypeFlags flags)
{
- if (!module.IndexTimeStamp.HasValue || !module.IndexFileSize.HasValue)
+ SymbolStoreKey fileKey = null;
+ string fileName = null;
+ if ((flags & KeyTypeFlags.IdentityKey) != 0)
{
- Trace.TraceWarning($"DownLoadPE: module {module.FileName} has no index timestamp/filesize");
- return null;
+ if (!module.IndexTimeStamp.HasValue || !module.IndexFileSize.HasValue)
+ {
+ return null;
+ }
+ fileName = module.FileName;
+ fileKey = PEFileKeyGenerator.GetKey(Path.GetFileName(fileName), module.IndexTimeStamp.Value, module.IndexFileSize.Value);
+ if (fileKey is null)
+ {
+ Trace.TraceWarning($"DownLoadPE: no key generated for module {fileName} ");
+ return null;
+ }
+ }
+ else if ((flags & KeyTypeFlags.SymbolKey) != 0)
+ {
+ IEnumerable<PdbFileInfo> pdbInfos = module.GetPdbFileInfos();
+ if (!pdbInfos.Any())
+ {
+ return null;
+ }
+ foreach (PdbFileInfo pdbInfo in pdbInfos)
+ {
+ if (pdbInfo.IsPortable)
+ {
+ fileKey = PortablePDBFileKeyGenerator.GetKey(pdbInfo.Path, pdbInfo.Guid);
+ if (fileKey is not null)
+ {
+ fileName = pdbInfo.Path;
+ break;
+ }
+ }
+ }
+ if (fileKey is null)
+ {
+ foreach (PdbFileInfo pdbInfo in pdbInfos)
+ {
+ if (!pdbInfo.IsPortable)
+ {
+ fileKey = PDBFileKeyGenerator.GetKey(pdbInfo.Path, pdbInfo.Guid, pdbInfo.Revision);
+ if (fileKey is not null)
+ {
+ fileName = pdbInfo.Path;
+ break;
+ }
+ }
+ }
+ }
+ if (fileKey is null)
+ {
+ Trace.TraceWarning($"DownLoadPE: no key generated for module PDB {module.FileName} ");
+ return null;
+ }
}
-
- SymbolStoreKey moduleKey = PEFileKeyGenerator.GetKey(Path.GetFileName(module.FileName), module.IndexTimeStamp.Value, module.IndexFileSize.Value);
- if (moduleKey is null)
+ else
{
- Trace.TraceWarning($"DownLoadPE: no index generated for module {module.FileName} ");
- return null;
+ throw new ArgumentException($"Key flag not supported {flags}");
}
- if (File.Exists(module.FileName))
+ // Check if the file is local and the key matches the module
+ if (File.Exists(fileName))
{
- using Stream stream = Utilities.TryOpenFile(module.FileName);
+ using Stream stream = Utilities.TryOpenFile(fileName);
if (stream is not null)
{
var peFile = new PEFile(new StreamAddressSpace(stream), false);
- var generator = new PEFileKeyGenerator(Tracer.Instance, peFile, module.FileName);
- IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
- foreach (SymbolStoreKey key in keys)
+ var generator = new PEFileKeyGenerator(Tracer.Instance, peFile, fileName);
+ foreach (SymbolStoreKey key in generator.GetKeys(flags))
{
- if (moduleKey.Equals(key))
+ if (fileKey.Equals(key))
{
- Trace.TraceInformation("DownloadPE: local file match {0}", module.FileName);
- return module.FileName;
+ Trace.TraceInformation($"DownloadPE: local file match {fileName}");
+ return fileName;
}
}
}
}
// Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
- string downloadFilePath = DownloadFile(moduleKey);
+ string downloadFilePath = DownloadFile(fileKey);
if (!string.IsNullOrEmpty(downloadFilePath))
{
Trace.TraceInformation("DownloadPE: downloaded {0}", downloadFilePath);
/// Finds or downloads the ELF module
/// </summary>
/// <param name="module">module instance</param>
+ /// <param name="flags"></param>
/// <returns>module path or null</returns>
- private string DownloadELF(IModule module)
+ private string DownloadELF(IModule module, KeyTypeFlags flags)
{
+ if ((flags & (KeyTypeFlags.IdentityKey | KeyTypeFlags.SymbolKey)) == 0)
+ {
+ throw new ArgumentException($"Key flag not supported {flags}");
+ }
+
if (module.BuildId.IsDefaultOrEmpty)
{
Trace.TraceWarning($"DownloadELF: module {module.FileName} has no build id");
return null;
}
- SymbolStoreKey moduleKey = ELFFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, module.FileName, module.BuildId.ToArray(), symbolFile: false, symbolFileName: null).SingleOrDefault();
- if (moduleKey is null)
+ SymbolStoreKey fileKey = ELFFileKeyGenerator.GetKeys(flags, module.FileName, module.BuildId.ToArray(), symbolFile: false, module.GetSymbolFileName()).SingleOrDefault();
+ if (fileKey is null)
{
Trace.TraceWarning($"DownloadELF: no index generated for module {module.FileName} ");
return null;
}
- if (File.Exists(module.FileName))
+ // Check if the file is local and the key matches the module
+ string fileName = fileKey.FullPathName;
+ if (File.Exists(fileName))
{
- using Utilities.ELFModule elfModule = Utilities.OpenELFFile(module.FileName);
+ using ELFModule elfModule = ELFModule.OpenFile(fileName);
if (elfModule is not null)
{
- var generator = new ELFFileKeyGenerator(Tracer.Instance, elfModule, module.FileName);
- IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
- foreach (SymbolStoreKey key in keys)
+ var generator = new ELFFileKeyGenerator(Tracer.Instance, elfModule, fileName);
+ foreach (SymbolStoreKey key in generator.GetKeys(flags))
{
- if (moduleKey.Equals(key))
+ if (fileKey.Equals(key))
{
- Trace.TraceInformation("DownloadELF: local file match {0}", module.FileName);
- return module.FileName;
+ Trace.TraceInformation("DownloadELF: local file match {0}", fileName);
+ return fileName;
}
}
}
}
// Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
- string downloadFilePath = DownloadFile(moduleKey);
+ string downloadFilePath = DownloadFile(fileKey);
if (!string.IsNullOrEmpty(downloadFilePath))
{
Trace.TraceInformation("DownloadELF: downloaded {0}", downloadFilePath);
/// Finds or downloads the MachO module.
/// </summary>
/// <param name="module">module instance</param>
+ /// <param name="flags"></param>
/// <returns>module path or null</returns>
- private string DownloadMachO(IModule module)
+ private string DownloadMachO(IModule module, KeyTypeFlags flags)
{
+ if ((flags & (KeyTypeFlags.IdentityKey | KeyTypeFlags.SymbolKey)) == 0)
+ {
+ throw new ArgumentException($"Key flag not supported {flags}");
+ }
+
if (module.BuildId.IsDefaultOrEmpty)
{
Trace.TraceWarning($"DownloadMachO: module {module.FileName} has no build id");
return null;
}
- SymbolStoreKey moduleKey = MachOFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, module.FileName, module.BuildId.ToArray(), symbolFile: false, symbolFileName: null).SingleOrDefault();
- if (moduleKey is null)
+ SymbolStoreKey fileKey = MachOFileKeyGenerator.GetKeys(flags, module.FileName, module.BuildId.ToArray(), symbolFile: false, module.GetSymbolFileName()).SingleOrDefault();
+ if (fileKey is null)
{
Trace.TraceWarning($"DownloadMachO: no index generated for module {module.FileName} ");
return null;
}
- if (File.Exists(module.FileName))
+ // Check if the file is local and the key matches the module
+ string fileName = fileKey.FullPathName;
+ if (File.Exists(fileName))
{
- using Utilities.MachOModule machOModule = Utilities.OpenMachOFile(module.FileName);
+ using MachOModule machOModule = MachOModule.OpenFile(fileName);
if (machOModule is not null)
{
- var generator = new MachOFileKeyGenerator(Tracer.Instance, machOModule, module.FileName);
- IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
+ var generator = new MachOFileKeyGenerator(Tracer.Instance, machOModule, fileName);
+ IEnumerable<SymbolStoreKey> keys = generator.GetKeys(flags);
foreach (SymbolStoreKey key in keys)
{
- if (moduleKey.Equals(key))
+ if (fileKey.Equals(key))
{
- Trace.TraceInformation("DownloadMachO: local file match {0}", module.FileName);
- return module.FileName;
+ Trace.TraceInformation("DownloadMachO: local file match {0}", fileName);
+ return fileName;
}
}
}
}
// Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
- string downloadFilePath = DownloadFile(moduleKey);
+ string downloadFilePath = DownloadFile(fileKey);
if (!string.IsNullOrEmpty(downloadFilePath))
{
Trace.TraceInformation("DownloadMachO: downloaded {0}", downloadFilePath);
public override string ToString()
{
StringBuilder sb = new StringBuilder();
- Microsoft.SymbolStore.SymbolStores.SymbolStore symbolStore = _symbolStore;
- while (symbolStore != null)
+ ForEachSymbolStore<Microsoft.SymbolStore.SymbolStores.SymbolStore>((symbolStore) =>
{
- sb.AppendLine(symbolStore.ToString());
- symbolStore = symbolStore.BackingStore;
- }
+ if (symbolStore is HttpSymbolStore httpSymbolStore)
+ {
+ sb.AppendLine($"{httpSymbolStore} Timeout: {httpSymbolStore.Timeout.Minutes} RetryCount: {httpSymbolStore.RetryCount}");
+ }
+ else
+ {
+ sb.AppendLine(symbolStore.ToString());
+ }
+ });
return sb.ToString();
}
return false;
}
+ /// <summary>
+ /// Enumerates the symbol stores.
+ /// </summary>
+ /// <typeparam name="T">type of symbol store or SymbolStore for all</typeparam>
+ /// <param name="callback">called for each store found</param>
+ public void ForEachSymbolStore<T>(Action<T> callback)
+ where T : Microsoft.SymbolStore.SymbolStores.SymbolStore
+ {
+ Microsoft.SymbolStore.SymbolStores.SymbolStore symbolStore = _symbolStore;
+ while (symbolStore != null)
+ {
+ if (symbolStore is T store)
+ {
+ callback(store);
+ }
+ symbolStore = symbolStore.BackingStore;
+ }
+ }
+
/// <summary>
/// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux
/// </summary>
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using Microsoft.FileFormats;
-using Microsoft.FileFormats.ELF;
-using Microsoft.FileFormats.MachO;
using Microsoft.FileFormats.PE;
using System;
using System.Diagnostics;
var reader = new PEReader(stream);
if (reader.PEHeaders == null || reader.PEHeaders.PEHeader == null)
{
- Trace.TraceError($"OpenPEReader: PEReader invalid headers");
+ Trace.TraceWarning($"OpenPEReader: PEReader invalid headers");
return null;
}
return reader;
return null;
}
- /// <summary>
- /// Disposable ELFFile wrapper
- /// </summary>
- public class ELFModule : ELFFile, IDisposable
- {
- private readonly Stream _stream;
-
- public ELFModule(Stream stream) :
- base(new StreamAddressSpace(stream), position: 0, isDataSourceVirtualAddressSpace: false)
- {
- _stream = stream;
- }
-
- public void Dispose() => _stream.Dispose();
- }
-
- /// <summary>
- /// Opens and returns an ELFFile instance from the local file path
- /// </summary>
- /// <param name="filePath">ELF file to open</param>
- /// <returns>ELFFile instance or null</returns>
- public static ELFModule OpenELFFile(string filePath)
- {
- Stream stream = TryOpenFile(filePath);
- if (stream is not null)
- {
- try
- {
- ELFModule elfModule = new (stream);
- if (!elfModule.IsValid())
- {
- Trace.TraceError($"OpenELFFile: not a valid file");
- return null;
- }
- return elfModule;
- }
- catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
- {
- Trace.TraceError($"OpenELFFile: exception {ex.Message}");
- }
- }
- return null;
- }
-
- /// <summary>
- /// Disposable MachOFile wrapper
- /// </summary>
- public class MachOModule : MachOFile, IDisposable
- {
- private readonly Stream _stream;
-
- public MachOModule(Stream stream) :
- base(new StreamAddressSpace(stream), position: 0, dataSourceIsVirtualAddressSpace: false)
- {
- _stream = stream;
- }
-
- public void Dispose() => _stream.Dispose();
- }
-
- /// <summary>
- /// Opens and returns an MachOFile instance from the local file path
- /// </summary>
- /// <param name="filePath">MachO file to open</param>
- /// <returns>MachOFile instance or null</returns>
- public static MachOModule OpenMachOFile(string filePath)
- {
- Stream stream = TryOpenFile(filePath);
- if (stream is not null)
- {
- try
- {
- var machoModule = new MachOModule(stream);
- if (!machoModule.IsValid())
- {
- Trace.TraceError($"OpenMachOFile: not a valid file");
- return null;
- }
- return machoModule;
- }
- catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
- {
- Trace.TraceError($"OpenMachOFile: exception {ex.Message}");
- }
- }
- return null;
- }
-
/// <summary>
/// Attempt to open a file stream.
/// </summary>
/// <param name="value"></param>
void WriteError(string value);
+ /// <summary>Writes Debugger Markup Language (DML) markup text.</summary>
+ void WriteDml(string text);
+
+ /// <summary>Gets whether <see cref="WriteDml"/> is supported.</summary>
+ bool SupportsDml { get; }
+
/// <summary>
/// Cancellation token for current command
/// </summary>
bool? IsFileLayout { get; }
/// <summary>
- /// PDB information for Windows PE modules (managed or native).
+ /// Returns PDB information for Windows PE modules (managed or native).
/// </summary>
- IEnumerable<PdbFileInfo> PdbFileInfos { get; }
+ IEnumerable<PdbFileInfo> GetPdbFileInfos();
/// <summary>
- /// Version information for Window PE modules (managed or native).
+ /// Returns the Linux or MacOS symbol file name.
/// </summary>
- VersionData VersionData { get; }
+ string GetSymbolFileName();
/// <summary>
- /// This is the file version string containing the build version and commit id.
+ /// Returns the version information for the modules.
/// </summary>
- string VersionString { get; }
+ VersionData GetVersionData();
+
+ /// <summary>
+ /// Returns the file version string containing the build version and commit id.
+ /// </summary>
+ string GetVersionString();
+
+ /// <summary>
+ /// Loads or downloads the module's symbol file and registers it with the underlying host debugger.
+ /// </summary>
+ /// <returns>the symbol file name</returns>
+ string LoadSymbols();
}
}
{
public interface ISymbolService
{
- /// <summary>
- /// Symbol file reader instance
- /// </summary>
- public class SymbolFile : IDisposable
- {
- public virtual void Dispose()
- {
- }
- }
-
/// <summary>
/// Invoked when anything changes in the symbol service (adding servers, caches, or directories, clearing store, etc.)
/// </summary>
/// </summary>
string DefaultSymbolCache { get; set; }
+ /// <summary>
+ /// The time out in minutes passed to the HTTP symbol store when not overridden in AddSymbolServer.
+ /// </summary>
+ int DefaultTimeout { get; set; }
+
+ /// <summary>
+ /// The retry count passed to the HTTP symbol store when not overridden in AddSymbolServer.
+ /// </summary>
+ int DefaultRetryCount { get; set; }
+
+ /// <summary>
+ /// Reset any HTTP symbol stores marked with a client failure
+ /// </summary>
+ void Reset();
+
/// <summary>
/// Parses the Windows debugger symbol path (srv*, cache*, etc.).
/// </summary>
/// <param name="msdl">if true, use the public Microsoft server</param>
/// <param name="symweb">if true, use symweb internal server and protocol (file.ptr)</param>
/// <param name="symbolServerPath">symbol server url (optional)</param>
- /// <param name="authToken"></param>
- /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional)</param>
+ /// <param name="authToken">PAT for secure symbol server (optional)</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
/// <returns>if false, failure</returns>
- bool AddSymbolServer(bool msdl, bool symweb, string symbolServerPath, string authToken, int timeoutInMinutes);
+ bool AddSymbolServer(bool msdl, bool symweb, string symbolServerPath = null, string authToken = null, int? timeoutInMinutes = null, int? retryCount = null);
/// <summary>
/// Add cache path to symbol search path
void DisableSymbolStore();
/// <summary>
- /// Downloads module file
+ /// Downloads the module file
+ /// </summary>
+ /// <param name="module">module interface</param>
+ /// <returns>module path or null</returns>
+ string DownloadModuleFile(IModule module);
+
+ /// <summary>
+ /// Downloads the symbol file for module
/// </summary>
/// <param name="module">module interface</param>
/// <returns>module path or null</returns>
- string DownloadModule(IModule module);
+ string DownloadSymbolFile(IModule module);
/// <summary>
/// Download a file from the symbol stores/server.
{
Debug.Assert(address != 0);
Debug.Assert(size != 0);
+ Debug.Assert((address & ~memoryService.SignExtensionMask()) == 0);
return new TargetStream(memoryService, address, size);
}
VersionData version = null;
try
{
- version = ModuleService.GetModuleFromBaseAddress(module.ImageBase).VersionData;
+ version = ModuleService.GetModuleFromBaseAddress(module.ImageBase).GetVersionData();
}
catch (DiagnosticsException)
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+ [Command(Name = CommandName, Help = "Displays information about async \"stacks\" on the garbage-collected heap.")]
+ public sealed class DumpAsyncCommand : ExtensionCommandBase
+ {
+ /// <summary>The name of the command.</summary>
+ private const string CommandName = "dumpasync";
+
+ /// <summary>Indent width.</summary>
+ private const int TabWidth = 2;
+ /// <summary>The command invocation syntax when used in Debugger Markup Language (DML) commands.</summary>
+ private const string DmlCommandInvoke = $"!{CommandName}";
+
+ /// <summary>The help text to render when asked for help.</summary>
+ private static readonly string s_detailedHelpText =
+ $"Usage: {CommandName} [--stats] [--coalesce] [--address <object address>] [--methodtable <mt address>] [--type <partial type name>] [--tasks] [--completed] [--fields]" + Environment.NewLine +
+ Environment.NewLine +
+ "Displays information about async \"stacks\" on the garbage-collected heap. Stacks" + Environment.NewLine +
+ "are synthesized by finding all task objects (including async state machine box" + Environment.NewLine +
+ "objects) on the GC heap and chaining them together based on continuations." + Environment.NewLine +
+ Environment.NewLine +
+ "Options:" + Environment.NewLine +
+ " --stats Summarize all async frames found rather than showing detailed stacks." + Environment.NewLine +
+ " --coalesce Coalesce stacks and portions of stacks that are the same." + Environment.NewLine +
+ " --address Only show stacks that include the object with the specified address." + Environment.NewLine +
+ " --methodtable Only show stacks that include objects with the specified method table." + Environment.NewLine +
+ " --type Only show stacks that include objects whose type includes the specified name in its name." + Environment.NewLine +
+ " --tasks Include stacks that contain only non-state machine task objects." + Environment.NewLine +
+ " --completed Include completed tasks in stacks." + Environment.NewLine +
+ " --fields Show fields for each async stack frame." + Environment.NewLine +
+ Environment.NewLine +
+ "Examples:" + Environment.NewLine +
+ $"Summarize all async frames associated with a specific method table address: !{CommandName} --stats --methodtable 0x00007ffbcfbe0970" + Environment.NewLine +
+ $"Show all stacks coalesced by common frames: !{CommandName} --coalesce" + Environment.NewLine +
+ $"Show each stack that includes \"ReadAsync\": !{CommandName} --type ReadAsync" + Environment.NewLine +
+ $"Show each stack that includes an object at a specific address, and include fields: !{CommandName} --address 0x000001264adce778 --fields";
+
+ /// <summary>Gets the runtime for the process. Set by the command framework.</summary>
+ public ClrRuntime? Runtime { get; set; }
+
+ /// <summary>Gets whether to only show stacks that include the object with the specified address.</summary>
+ [Option(Name = "--address", Aliases = new string[] { "-addr" }, Help = "Only show stacks that include the object with the specified address.")]
+ public ulong? ObjectAddress { get; set; }
+
+ /// <summary>Gets whether to only show stacks that include objects with the specified method table.</summary>
+ [Option(Name = "--methodtable", Aliases = new string[] { "-mt" }, Help = "Only show stacks that include objects with the specified method table.")]
+ public ulong? MethodTableAddress { get; set; }
+
+ /// <summary>Gets whether to only show stacks that include objects whose type includes the specified name in its name.</summary>
+ [Option(Name = "--type", Help = "Only show stacks that include objects whose type includes the specified name in its name.")]
+ public string? NameSubstring { get; set; }
+
+ /// <summary>Gets whether to include stacks that contain only non-state machine task objects.</summary>
+ [Option(Name = "--tasks", Aliases = new string[] { "-t" }, Help = "Include stacks that contain only non-state machine task objects.")]
+ public bool IncludeTasks { get; set; }
+
+ /// <summary>Gets whether to include completed tasks in stacks.</summary>
+ [Option(Name = "--completed", Aliases = new string[] { "-c" }, Help = "Include completed tasks in stacks.")]
+ public bool IncludeCompleted { get; set; }
+
+ /// <summary>Gets whether to show state machine fields for every async stack frame that has them.</summary>
+ [Option(Name = "--fields", Aliases = new string[] { "-f" }, Help = "Show state machine fields for every async stack frame that has them.")]
+ public bool DisplayFields { get; set; }
+
+ /// <summary>Gets whether to summarize all async frames found rather than showing detailed stacks.</summary>
+ [Option(Name = "--stats", Help = "Summarize all async frames found rather than showing detailed stacks.")]
+ public bool Summarize { get; set; }
+
+ /// <summary>Gets whether to coalesce stacks and portions of stacks that are the same.</summary>
+ [Option(Name = "--coalesce", Help = "Coalesce stacks and portions of stacks that are the same.")]
+ public bool CoalesceStacks { get; set; }
+
+ /// <summary>Invokes the command.</summary>
+ public override void ExtensionInvoke()
+ {
+ ClrRuntime? runtime = Runtime;
+ if (runtime is null)
+ {
+ WriteLineError("Unable to access runtime.");
+ return;
+ }
+
+ ClrHeap heap = runtime.Heap;
+ if (!heap.CanWalkHeap)
+ {
+ WriteLineError("Unable to examine the heap.");
+ return;
+ }
+
+ ClrType? taskType = runtime.BaseClassLibrary.GetTypeByName("System.Threading.Tasks.Task");
+ if (taskType is null)
+ {
+ WriteLineError("Unable to find required type.");
+ return;
+ }
+
+ ClrStaticField? taskCompletionSentinelType = taskType.GetStaticFieldByName("s_taskCompletionSentinel");
+
+ ClrObject taskCompletionSentinel = default;
+
+ if (taskCompletionSentinelType is not null)
+ {
+ Debug.Assert(taskCompletionSentinelType.IsObjectReference);
+ taskCompletionSentinel = taskCompletionSentinelType.ReadObject(runtime.BaseClassLibrary.AppDomain);
+ }
+
+ // Enumerate the heap, gathering up all relevant async-related objects.
+ Dictionary<ClrObject, AsyncObject> objects = CollectObjects();
+
+ // Render the data according to the options specified.
+ if (Summarize)
+ {
+ RenderStats();
+ }
+ else if (CoalesceStacks)
+ {
+ RenderCoalescedStacks();
+ }
+ else
+ {
+ RenderStacks();
+ }
+ return;
+
+ // <summary>Group frames and summarize how many of each occurred.</summary>
+ void RenderStats()
+ {
+ // Enumerate all of the "frames", and create a mapping from a rendering of that
+ // frame to its associated type and how many times that frame occurs.
+ var typeCounts = new Dictionary<string, (ClrType Type, int Count)>();
+ foreach (KeyValuePair<ClrObject, AsyncObject> pair in objects)
+ {
+ ClrObject obj = pair.Key;
+ if (obj.Type is null)
+ {
+ continue;
+ }
+
+ string description = Describe(obj);
+
+ if (!typeCounts.TryGetValue(description, out (ClrType Type, int Count) value))
+ {
+ value = (obj.Type, 0);
+ }
+
+ value.Count++;
+ typeCounts[description] = value;
+ }
+
+ // Render one line per frame.
+ WriteHeaderLine($"{"MT",-16} {"Count",-8} Type");
+ foreach (KeyValuePair<string, (ClrType Type, int Count)> entry in typeCounts.OrderByDescending(e => e.Value.Count))
+ {
+ WriteMethodTable(entry.Value.Type.MethodTable, asyncObject: true);
+ WriteLine($" {entry.Value.Count,-8:N0} {entry.Key}");
+ }
+ }
+
+ // <summary>Group stacks at each frame in order to render a tree of coalesced stacks.</summary>
+ void RenderCoalescedStacks()
+ {
+ // Find all stacks to include.
+ var startingList = new List<ClrObject>();
+ foreach (KeyValuePair<ClrObject, AsyncObject> entry in objects)
+ {
+ Console.CancellationToken.ThrowIfCancellationRequested();
+
+ AsyncObject obj = entry.Value;
+ if (obj.TopLevel && ShouldIncludeStack(obj))
+ {
+ startingList.Add(entry.Key);
+ }
+ }
+
+ // If we found any, render them.
+ if (startingList.Count > 0)
+ {
+ RenderLevel(startingList, 0);
+ }
+
+ // <summary>Renders the next level of frames for coalesced stacks.</summary>
+ void RenderLevel(List<ClrObject> frames, int depth)
+ {
+ Console.CancellationToken.ThrowIfCancellationRequested();
+ List<ClrObject> nextLevel = new List<ClrObject>();
+
+ // Grouping function. We want to treat all objects that render the same as the same entity.
+ // For async state machines, we include the await state, both because we want it to render
+ // and because we want to see state machines at different positions as part of different groups.
+ Func<ClrObject, string> groupBy = o =>
+ {
+ string description = Describe(o);
+ if (objects.TryGetValue(o, out AsyncObject asyncObject) && asyncObject.IsStateMachine)
+ {
+ description = $"({asyncObject.AwaitState}) {description}";
+ }
+ return description;
+ };
+
+ // Group all of the frames, rendering each group as a single line with a count.
+ // Then recur for each.
+ int stackId = 1;
+ foreach (IGrouping<string, ClrObject> group in frames.GroupBy(groupBy).OrderByDescending(g => g.Count()))
+ {
+ int count = group.Count();
+ Debug.Assert(count > 0);
+
+ // For top-level frames, write out a header.
+ if (depth == 0)
+ {
+ WriteHeaderLine($"STACKS {stackId++}");
+ }
+
+ // Write out the count and frame.
+ Write($"{Tabs(depth)}[{count}] ");
+ WriteMethodTable(group.First().Type?.MethodTable ?? 0, asyncObject: true);
+ WriteLine($" {group.Key}");
+
+ // Gather up all of the next level of frames.
+ nextLevel.Clear();
+ foreach (ClrObject next in group)
+ {
+ if (objects.TryGetValue(next, out AsyncObject asyncObject))
+ {
+ // Note that the merging of multiple continuations can lead to numbers increasing at a particular
+ // level of the coalesced stacks. It's not clear there's a better answer.
+ nextLevel.AddRange(asyncObject.Continuations);
+ }
+ }
+
+ // If we found any, recur.
+ if (nextLevel.Count != 0)
+ {
+ RenderLevel(nextLevel, depth + 1);
+ }
+
+ if (depth == 0)
+ {
+ WriteLine("");
+ }
+ }
+ }
+ }
+
+ // <summary>Render each stack of frames.</summary>
+ void RenderStacks()
+ {
+ var stack = new Stack<(AsyncObject AsyncObject, int Depth)>();
+
+ // Find every top-level object (ones that nothing else has as a continuation) and output
+ // a stack starting from each.
+ int stackId = 1;
+ foreach (KeyValuePair<ClrObject, AsyncObject> entry in objects)
+ {
+ Console.CancellationToken.ThrowIfCancellationRequested();
+ AsyncObject top = entry.Value;
+ if (!top.TopLevel || !ShouldIncludeStack(top))
+ {
+ continue;
+ }
+
+ int depth = 0;
+
+ WriteHeaderLine($"STACK {stackId++}");
+
+ // If the top-level frame is an async method that's paused at an await, it must be waiting on
+ // something. Try to synthesize a frame to represent that thing, just to provide a little more information.
+ if (top.IsStateMachine && top.AwaitState >= 0 && !IsCompleted(top.TaskStateFlags) &&
+ top.StateMachine is IAddressableTypedEntity stateMachine &&
+ stateMachine.Type is not null)
+ {
+ // Short of parsing the method's IL, we don't have a perfect way to know which awaiter field
+ // corresponds to the current await state, as awaiter fields are shared across all awaits that
+ // use the same awaiter type. We instead employ a heuristic. If the await state is 0, the
+ // associated field will be the first one (<>u__1); even if other awaits share it, it's fine
+ // to use. Similarly, if there's only one awaiter field, we know that must be the one being
+ // used. In all other situations, we can't know which of the multiple awaiter fields maps
+ // to the await state, so we instead employ a heuristic of looking for one that's non-zero.
+ // The C# compiler zero's out awaiter fields when it's done with them, so if we find an awaiter
+ // field with any non-zero bytes, it must be the one in use. This can have false negatives,
+ // as it's perfectly valid for an awaiter to be all zero bytes, but it's better than nothing.
+
+ if ((top.AwaitState == 0) ||
+ stateMachine.Type.Fields.Count(f => f.Name is null || f.Name.StartsWith("<>u__", StringComparison.Ordinal) == true) == 1) // if the name is null, we have to assume it's an awaiter
+ {
+ if (stateMachine.Type.GetFieldByName("<>u__1") is ClrInstanceField field &&
+ TrySynthesizeAwaiterFrame(field))
+ {
+ depth++;
+ }
+ }
+ else
+ {
+ foreach (ClrInstanceField field in stateMachine.Type.Fields)
+ {
+ // Look for awaiter fields. This is the naming convention employed by the C# compiler.
+ if (field.Name?.StartsWith("<>u__") == true)
+ {
+ if (field.IsObjectReference)
+ {
+ if (stateMachine.ReadObjectField(field.Name) is ClrObject { IsNull: false } awaiter)
+ {
+ if (TrySynthesizeAwaiterFrame(field))
+ {
+ depth++;
+ }
+ break;
+ }
+ }
+ else if (field.IsValueType &&
+ stateMachine.ReadValueTypeField(field.Name) is ClrValueType { IsValid: true } awaiter &&
+ awaiter.Type is not null)
+ {
+ byte[] awaiterBytes = new byte[awaiter.Type.StaticSize - (runtime.DataTarget!.DataReader.PointerSize * 2)];
+ if (runtime.DataTarget!.DataReader.Read(awaiter.Address, awaiterBytes) == awaiterBytes.Length && !AllZero(awaiterBytes))
+ {
+ if (TrySynthesizeAwaiterFrame(field))
+ {
+ depth++;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // <summary>Writes out a frame for the specified awaiter field, if possible.</summary>
+ bool TrySynthesizeAwaiterFrame(ClrInstanceField field)
+ {
+ if (field?.Name is string name)
+ {
+ if (field.IsObjectReference)
+ {
+ ClrObject awaiter = stateMachine.ReadObjectField(name);
+ if (awaiter.Type is not null)
+ {
+ Write("<< Awaiting: ");
+ WriteAddress(awaiter.Address, asyncObject: false);
+ Write(" ");
+ WriteMethodTable(awaiter.Type.MethodTable, asyncObject: false);
+ Write(awaiter.Type.Name);
+ WriteLine(" >>");
+ return true;
+ }
+ }
+ else if (field.IsValueType)
+ {
+ ClrValueType awaiter = stateMachine.ReadValueTypeField(name);
+ if (awaiter.Type is not null)
+ {
+ Write("<< Awaiting: ");
+ WriteValueTypeAddress(awaiter.Address, awaiter.Type.MethodTable);
+ Write(" ");
+ WriteMethodTable(awaiter.Type.MethodTable, asyncObject: false);
+ Write($" {awaiter.Type.Name}");
+ WriteLine(" >>");
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+
+ // Push the root node onto the stack to start the iteration. Then as long as there are nodes left
+ // on the stack, pop the next, render it, and push any continuations it may have back onto the stack.
+ Debug.Assert(stack.Count == 0);
+ stack.Push((top, depth));
+ while (stack.Count > 0)
+ {
+ (AsyncObject frame, depth) = stack.Pop();
+
+ Write($"{Tabs(depth)}");
+ WriteAddress(frame.Object.Address, asyncObject: true);
+ Write(" ");
+ WriteMethodTable(frame.Object.Type?.MethodTable ?? 0, asyncObject: true);
+ Write($" {(frame.IsStateMachine ? $"({frame.AwaitState})" : $"({DescribeTaskFlags(frame.TaskStateFlags)})")} {Describe(frame.Object)}");
+ WriteCodeLink(frame.NativeCode);
+ WriteLine("");
+
+ if (DisplayFields)
+ {
+ RenderFields(frame.StateMachine ?? frame.Object, depth + 4); // +4 for extra indent for fields
+ }
+
+ foreach (ClrObject continuation in frame.Continuations)
+ {
+ if (objects.TryGetValue(continuation, out AsyncObject asyncContinuation))
+ {
+ stack.Push((asyncContinuation, depth + 1));
+ }
+ else
+ {
+ string state = TryGetTaskStateFlags(continuation, out int flags) ? DescribeTaskFlags(flags) : "";
+ Write($"{Tabs(depth + 1)}");
+ WriteAddress(continuation.Address, asyncObject: true);
+ Write(" ");
+ WriteMethodTable(continuation.Type?.MethodTable ?? 0, asyncObject: true);
+ WriteLine($" ({state}) {Describe(continuation)}");
+ }
+ }
+ }
+
+ WriteLine("");
+ }
+ }
+
+ // <summary>Determine whether the stack rooted in this object should be rendered.</summary>
+ bool ShouldIncludeStack(AsyncObject obj)
+ {
+ // We want to render the stack for this object once we find any node that should be
+ // included based on the criteria specified as arguments _and_ if the include tasks
+ // options wasn't specified, once we find any node that's an async state machine.
+ // That way, we scope the output down to just stacks that contain something the
+ // user is interested in seeing.
+ bool sawShouldInclude = false;
+ bool sawStateMachine = IncludeTasks;
+
+ var stack = new Stack<AsyncObject>();
+ stack.Push(obj);
+ while (stack.Count > 0)
+ {
+ obj = stack.Pop();
+ sawShouldInclude |= obj.IncludeInOutput;
+ sawStateMachine |= obj.IsStateMachine;
+
+ if (sawShouldInclude && sawStateMachine)
+ {
+ return true;
+ }
+
+ foreach (ClrObject continuation in obj.Continuations)
+ {
+ if (objects.TryGetValue(continuation, out AsyncObject asyncContinuation))
+ {
+ stack.Push(asyncContinuation);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // <summary>Outputs a line of information for each instance field on the object.</summary>
+ void RenderFields(IAddressableTypedEntity? obj, int depth)
+ {
+ if (obj is not null)
+ {
+ string depthTab = new string(' ', depth * TabWidth);
+
+ WriteHeaderLine($"{depthTab}{"Address",16} {"MT",16} {"Type",-32} {"Value",16} Name");
+ foreach (ClrInstanceField field in obj.Type.Fields)
+ {
+ if (field.Type is not null)
+ {
+ Write($"{depthTab}");
+ if (field.IsObjectReference)
+ {
+ ClrObject objRef = field.ReadObject(obj.Address, obj.Type.IsValueType);
+ WriteAddress(objRef.Address, asyncObject: false);
+ }
+ else
+ {
+ WriteValueTypeAddress(field.GetAddress(obj.Address, obj.Type.IsValueType), field.Type.MethodTable);
+ }
+ Write(" ");
+ WriteMethodTable(field.Type.MethodTable, asyncObject: false);
+ WriteLine($" {Truncate(field.Type.Name, 32),-32} {Truncate(GetDisplay(obj, field).ToString(), 16),16} {field.Name}");
+ }
+ }
+ }
+ }
+
+ // <summary>Gets a printable description for the specified object.</summary>
+ string Describe(ClrObject obj)
+ {
+ // Default the description to the type name.
+ string description = obj.Type.Name;
+
+ if (IsStateMachineBox(obj.Type))
+ {
+ // Remove the boilerplate box type from the name.
+ int pos = description.IndexOf("StateMachineBox<", StringComparison.Ordinal);
+ if (pos >= 0)
+ {
+ ReadOnlySpan<char> slice = description.AsSpan(pos + "StateMachineBox<".Length);
+ slice = slice.Slice(0, slice.Length - 1); // remove trailing >
+ description = slice.ToString();
+ }
+ }
+ else if (TryGetValidObjectField(obj, "m_action", out ClrObject taskDelegate))
+ {
+ // If we can figure out what the task's delegate points to, append the method signature.
+ if (TryGetMethodFromDelegate(runtime, taskDelegate, out ClrMethod? method))
+ {
+ description = $"{description} {{{method!.Signature}}}";
+ }
+ }
+ else if (obj.Address != 0 && taskCompletionSentinel.Address == obj.Address)
+ {
+ description = "TaskCompletionSentinel";
+ }
+
+ return description;
+ }
+
+ // <summary>Determines whether the specified object is of interest to the user based on their criteria provided as command arguments.</summary>
+ bool IncludeInOutput(ClrObject obj)
+ {
+ if (ObjectAddress is ulong addr && obj.Address != addr)
+ {
+ return false;
+ }
+
+ if (MethodTableAddress is ulong mt && obj.Type.MethodTable != mt)
+ {
+ return false;
+ }
+
+ if (NameSubstring is not null && !obj.Type.Name.Contains(NameSubstring))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ // <summary>Finds all of the relevant async-related objects on the heap.</summary>
+ Dictionary<ClrObject, AsyncObject> CollectObjects()
+ {
+ var found = new Dictionary<ClrObject, AsyncObject>();
+
+ // Enumerate the heap, looking for all relevant objects.
+ foreach (ClrObject obj in heap.EnumerateObjects())
+ {
+ Console.CancellationToken.ThrowIfCancellationRequested();
+
+ if (!obj.IsValid || obj.Type is null)
+ {
+ Trace.TraceError($"(Skipping invalid object {obj})");
+ continue;
+ }
+
+ // Skip objects too small to be state machines or tasks, simply to help with performance.
+ if (obj.Size <= 24)
+ {
+ continue;
+ }
+
+ // We only care about task-related objects (all boxes are tasks).
+ if (!IsTask(obj.Type))
+ {
+ continue;
+ }
+
+ // This is currently working around an issue that result in enumerating segments multiple times in 6.0 runtimes
+ // up to 6.0.5. The PR that fixes it is https://github.com/dotnet/runtime/pull/67995, but we have this here for back compat.
+ if (found.ContainsKey(obj))
+ {
+ continue;
+ }
+
+ // If we're only going to render a summary (which only considers objects individually and not
+ // as part of chains) and if this object shouldn't be included, we don't need to do anything more.
+ if (Summarize &&
+ (!IncludeInOutput(obj) || (!IncludeTasks && !IsStateMachineBox(obj.Type))))
+ {
+ continue;
+ }
+
+ // If we couldn't get state flags for the task, something's wrong; skip it.
+ if (!TryGetTaskStateFlags(obj, out int taskStateFlags))
+ {
+ continue;
+ }
+
+ // If we're supposed to ignore already completed tasks and this one is completed, skip it.
+ if (!IncludeCompleted && IsCompleted(taskStateFlags))
+ {
+ continue;
+ }
+
+ // Gather up the necessary data for the object and store it.
+ AsyncObject result = new()
+ {
+ Object = obj,
+ IsStateMachine = IsStateMachineBox(obj.Type),
+ IncludeInOutput = IncludeInOutput(obj),
+ TaskStateFlags = taskStateFlags,
+ };
+
+ if (result.IsStateMachine && TryGetStateMachine(obj, out result.StateMachine))
+ {
+ bool gotState = TryRead(result.StateMachine!, "<>1__state", out result.AwaitState);
+ Debug.Assert(gotState);
+
+ if (result.StateMachine?.Type is ClrType stateMachineType)
+ {
+ foreach (ClrMethod method in stateMachineType.Methods)
+ {
+ if (method.NativeCode != ulong.MaxValue)
+ {
+ result.NativeCode = method.NativeCode;
+ if (method.Name == "MoveNext")
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (TryGetContinuation(obj, out ClrObject continuation))
+ {
+ AddContinuation(continuation, result.Continuations);
+ }
+
+ found.Add(obj, result);
+ }
+
+ // Mark off objects that are referenced by others and thus aren't top level
+ foreach (KeyValuePair<ClrObject, AsyncObject> entry in found)
+ {
+ foreach (ClrObject continuation in entry.Value.Continuations)
+ {
+ if (found.TryGetValue(continuation, out AsyncObject asyncContinuation))
+ {
+ asyncContinuation.TopLevel = false;
+ }
+ }
+ }
+
+ return found;
+ }
+
+ // <summary>Adds the continuation into the list of continuations.</summary>
+ // <remarks>
+ // If the continuation is actually a List{object}, enumerate the list to add
+ // each of the individual continuations to the continuations list.
+ // </remarks>
+ void AddContinuation(ClrObject continuation, List<ClrObject> continuations)
+ {
+ if (continuation.Type.Name.StartsWith("System.Collections.Generic.List<", StringComparison.Ordinal))
+ {
+ if (continuation.Type.GetFieldByName("_items") is ClrInstanceField itemsField)
+ {
+ ClrObject itemsObj = itemsField.ReadObject(continuation.Address, interior: false);
+ if (!itemsObj.IsNull)
+ {
+ ClrArray items = itemsObj.AsArray();
+ if (items.Rank == 1)
+ {
+ for (int i = 0; i < items.Length; i++)
+ {
+ if (items.GetObjectValue(i) is ClrObject { IsValid: true } c)
+ {
+ continuations.Add(ResolveContinuation(c));
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ continuations.Add(continuation);
+ }
+ }
+
+ // <summary>Tries to get the object contents of a Task's continuations field</summary>
+ bool TryGetContinuation(ClrObject obj, out ClrObject continuation)
+ {
+ if (obj.Type.GetFieldByName("m_continuationObject") is ClrInstanceField continuationObjectField &&
+ continuationObjectField.ReadObject(obj.Address, interior: false) is ClrObject { IsValid: true } continuationObject)
+ {
+ continuation = ResolveContinuation(continuationObject);
+ return true;
+ }
+
+ continuation = default;
+ return false;
+ }
+
+ // <summary>Analyzes a continuation object to try to follow to the actual continuation target.</summary>
+ ClrObject ResolveContinuation(ClrObject continuation)
+ {
+ ClrObject tmp;
+
+ // If the continuation is an async method box, there's nothing more to resolve.
+ if (IsTask(continuation.Type) && IsStateMachineBox(continuation.Type))
+ {
+ return continuation;
+ }
+
+ // If it's a standard task continuation, get its task field.
+ if (TryGetValidObjectField(continuation, "m_task", out tmp))
+ {
+ return tmp;
+ }
+
+ // If it's storing an action wrapper, try to follow to that action's target.
+ if (TryGetValidObjectField(continuation, "m_action", out tmp))
+ {
+ continuation = tmp;
+ }
+
+ // If we now have an Action, try to follow through to the delegate's target.
+ if (TryGetValidObjectField(continuation, "_target", out tmp))
+ {
+ continuation = tmp;
+
+ // In some cases, the delegate's target might be a ContinuationWrapper, in which case we want to unwrap that as well.
+ if (continuation.Type?.Name == "System.Runtime.CompilerServices.AsyncMethodBuilderCore+ContinuationWrapper" &&
+ TryGetValidObjectField(continuation, "_continuation", out tmp))
+ {
+ continuation = tmp;
+ if (TryGetValidObjectField(continuation, "_target", out tmp))
+ {
+ continuation = tmp;
+ }
+ }
+ }
+
+ // Use whatever we ended with.
+ return continuation;
+ }
+
+ // <summary>Determines if a type is or is derived from Task.</summary>
+ bool IsTask(ClrType? type)
+ {
+ while (type is not null)
+ {
+ if (type.MetadataToken == taskType.MetadataToken &&
+ type.Module == taskType.Module)
+ {
+ return true;
+ }
+
+ type = type.BaseType;
+ }
+
+ return false;
+ }
+ }
+
+ /// <summary>Writes out a header line. If DML is supported, this will be bolded.</summary>
+ private void WriteHeaderLine(string text)
+ {
+ if (Console.SupportsDml)
+ {
+ Console.WriteDml($"<b>{text}</b>{Environment.NewLine}");
+ }
+ else
+ {
+ WriteLine(text);
+ }
+ }
+
+ /// <summary>Writes out a method table address. If DML is supported, this will be linked.</summary>
+ /// <param name="mt">The method table address.</param>
+ /// <param name="asyncObject">
+ /// true if this is an async-related object; otherwise, false. If true and if DML is supported,
+ /// a link to dumpasync will be generated. If false and if DML is supported, a link to dumpmt
+ /// will be generated.
+ /// </param>
+ private void WriteMethodTable(ulong mt, bool asyncObject)
+ {
+ string completed = IncludeCompleted ? "--completed" : "";
+ string tasks = IncludeTasks ? "--tasks" : "";
+
+ switch ((Console.SupportsDml, asyncObject, IntPtr.Size))
+ {
+ case (false, _, 4):
+ Console.Write($"{mt,16:x8}");
+ break;
+
+ case (false, _, 8):
+ Console.Write($"{mt:x16}");
+ break;
+
+ case (true, true, 4):
+ Console.WriteDml($"<exec cmd=\"{DmlCommandInvoke} --methodtable 0x{mt:x8} {tasks} {completed}\">{mt,16:x8}</exec>");
+ break;
+
+ case (true, true, 8):
+ Console.WriteDml($"<exec cmd=\"{DmlCommandInvoke} --methodtable 0x{mt:x16} {tasks} {completed}\">{mt:x16}</exec>");
+ break;
+
+ case (true, false, 4):
+ Console.WriteDml($"<exec cmd=\"!DumpMT /d 0x{mt:x8}\">{mt,16:x8}</exec>");
+ break;
+
+ case (true, false, 8):
+ Console.WriteDml($"<exec cmd=\"!DumpMT /d 0x{mt:x16}\">{mt:x16}</exec>");
+ break;
+ }
+ }
+
+ /// <summary>Writes out an object address. If DML is supported, this will be linked.</summary>
+ /// <param name="addr">The object address.</param>
+ /// <param name="asyncObject">
+ /// true if this is an async-related object; otherwise, false. If true and if DML is supported,
+ /// a link to dumpasync will be generated. If false and if DML is supported, a link to dumpobj
+ /// will be generated.
+ /// </param>
+ private void WriteAddress(ulong addr, bool asyncObject)
+ {
+ string completed = IncludeCompleted ? "--completed" : "";
+ string tasks = IncludeTasks ? "--tasks" : "";
+
+ switch ((Console.SupportsDml, asyncObject, IntPtr.Size))
+ {
+ case (false, _, 4):
+ Console.Write($"{addr,16:x8}");
+ break;
+
+ case (false, _, 8):
+ Console.Write($"{addr:x16}");
+ break;
+
+ case (true, true, 4):
+ Console.WriteDml($"<exec cmd=\"{DmlCommandInvoke} --address 0x{addr:x8} {tasks} {completed} --fields\">{addr,16:x8}</exec>");
+ break;
+
+ case (true, true, 8):
+ Console.WriteDml($"<exec cmd=\"{DmlCommandInvoke} --address 0x{addr:x16} {tasks} {completed} --fields\">{addr:x16}</exec>");
+ break;
+
+ case (true, false, 4):
+ Console.WriteDml($"<exec cmd=\"!DumpObj /d 0x{addr:x8}\">{addr,16:x8}</exec>");
+ break;
+
+ case (true, false, 8):
+ Console.WriteDml($"<exec cmd=\"!DumpObj /d 0x{addr:x16}\">{addr:x16}</exec>");
+ break;
+ }
+ }
+
+ /// <summary>Writes out a value type address. If DML is supported, this will be linked.</summary>
+ /// <param name="addr">The value type's address.</param>
+ /// <param name="mt">The value type's method table address.</param>
+ private void WriteValueTypeAddress(ulong addr, ulong mt)
+ {
+ switch ((Console.SupportsDml, IntPtr.Size))
+ {
+ case (false, 4):
+ Console.Write($"{addr,16:x8}");
+ break;
+
+ case (false, 8):
+ Console.Write($"{addr:x16}");
+ break;
+
+ case (true, 4):
+ Console.WriteDml($"<exec cmd=\"!DumpVC /d 0x{mt:x8} 0x{addr:x8}\">{addr,16:x8}</exec>");
+ break;
+
+ case (true, 8):
+ Console.WriteDml($"<exec cmd=\"!DumpVC /d 0x{mt:x16} 0x{addr:x16}\">{addr:x16}</exec>");
+ break;
+ }
+ }
+
+ /// <summary>Writes out a link that should open the source code for an address, if available.</summary>
+ /// <remarks>If DML is not supported, this is a nop.</remarks>
+ private void WriteCodeLink(ulong address)
+ {
+ if (address != 0 && address != ulong.MaxValue &&
+ Console.SupportsDml)
+ {
+ Console.WriteDml($" <link cmd=\".open -a 0x{address:x}\" alt=\"Source link\">@ {address:x}</link>");
+ }
+ }
+
+ /// <summary>Gets whether the specified type is an AsyncStateMachineBox{T}.</summary>
+ private static bool IsStateMachineBox(ClrType type)
+ {
+ // Ideally we would compare the metadata token and module for the generic template for the type,
+ // but that information isn't fully available via ClrMd, nor can it currently find DebugFinalizableAsyncStateMachineBox
+ // due to various limitations. So we're left with string comparison.
+ const string Prefix = "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<";
+ return
+ type?.Name is string name &&
+ name.StartsWith(Prefix, StringComparison.Ordinal) &&
+ name.IndexOf("AsyncStateMachineBox", Prefix.Length, StringComparison.Ordinal) >= 0;
+ }
+
+ /// <summary>Tries to get the compiler-generated state machine instance from a state machine box.</summary>
+ private static bool TryGetStateMachine(ClrObject obj, out IAddressableTypedEntity? stateMachine)
+ {
+ // AsyncStateMachineBox<T> has a StateMachine field storing the compiler-generated instance.
+ if (obj.Type?.GetFieldByName("StateMachine") is ClrInstanceField field)
+ {
+ if (field.IsValueType)
+ {
+ if (obj.ReadValueTypeField("StateMachine") is ClrValueType { IsValid: true } t)
+ {
+ stateMachine = t;
+ return true;
+ }
+ }
+ else if (field.ReadObject(obj.Address, interior: false) is ClrObject { IsValid: true } t)
+ {
+ stateMachine = t;
+ return true;
+ }
+ }
+
+ stateMachine = null;
+ return false;
+ }
+
+ /// <summary>Extract from the specified field of the specified object something that can be ToString'd.</summary>
+ private static object GetDisplay(IAddressableTypedEntity obj, ClrInstanceField field)
+ {
+ if (field.Name is string fieldName)
+ {
+ switch (field.ElementType)
+ {
+ case ClrElementType.Boolean:
+ return obj.ReadField<bool>(fieldName) ? "true" : "false";
+
+ case ClrElementType.Char:
+ char c = obj.ReadField<char>(fieldName);
+ return c >= 32 && c < 127 ? $"'{c}'" : $"'\\u{(int)c:X4}'";
+
+ case ClrElementType.Int8:
+ return obj.ReadField<sbyte>(fieldName);
+
+ case ClrElementType.UInt8:
+ return obj.ReadField<byte>(fieldName);
+
+ case ClrElementType.Int16:
+ return obj.ReadField<short>(fieldName);
+
+ case ClrElementType.UInt16:
+ return obj.ReadField<ushort>(fieldName);
+
+ case ClrElementType.Int32:
+ return obj.ReadField<int>(fieldName);
+
+ case ClrElementType.UInt32:
+ return obj.ReadField<uint>(fieldName);
+
+ case ClrElementType.Int64:
+ return obj.ReadField<long>(fieldName);
+
+ case ClrElementType.UInt64:
+ return obj.ReadField<ulong>(fieldName);
+
+ case ClrElementType.Float:
+ return obj.ReadField<float>(fieldName);
+
+ case ClrElementType.Double:
+ return obj.ReadField<double>(fieldName);
+
+ case ClrElementType.String:
+ return $"\"{obj.ReadStringField(fieldName)}\"";
+
+ case ClrElementType.Pointer:
+ case ClrElementType.NativeInt:
+ case ClrElementType.NativeUInt:
+ case ClrElementType.FunctionPointer:
+ return obj.ReadField<ulong>(fieldName).ToString(IntPtr.Size == 8 ? "x16" : "x8");
+
+ case ClrElementType.SZArray:
+ ClrObject arrayObj = obj.ReadObjectField(fieldName);
+ if (!arrayObj.IsNull)
+ {
+ ClrArray arrayObjAsArray = arrayObj.AsArray();
+ return $"{arrayObj.Type?.ComponentType?.ToString() ?? "unknown"}[{arrayObjAsArray.Length}]";
+ }
+ return "null";
+
+ case ClrElementType.Struct:
+ return field.GetAddress(obj.Address).ToString(IntPtr.Size == 8 ? "x16" : "x8");
+
+ case ClrElementType.Array:
+ case ClrElementType.Object:
+ case ClrElementType.Class:
+ ClrObject classObj = obj.ReadObjectField(fieldName);
+ return classObj.IsNull ? "null" : classObj.Address.ToString(IntPtr.Size == 8 ? "x16" : "x8");
+
+ case ClrElementType.Var:
+ return "(var)";
+
+ case ClrElementType.GenericInstantiation:
+ return "(generic instantiation)";
+
+ case ClrElementType.MVar:
+ return "(mvar)";
+
+ case ClrElementType.Void:
+ return "(void)";
+ }
+ }
+
+ return "(unknown)";
+ }
+
+ /// <summary>Tries to get a ClrMethod for the method wrapped by a delegate object.</summary>
+ private static bool TryGetMethodFromDelegate(ClrRuntime runtime, ClrObject delegateObject, out ClrMethod? method)
+ {
+ ClrInstanceField? methodPtrField = delegateObject.Type?.GetFieldByName("_methodPtr");
+ ClrInstanceField? methodPtrAuxField = delegateObject.Type?.GetFieldByName("_methodPtrAux");
+
+ if (methodPtrField is not null && methodPtrAuxField is not null)
+ {
+ ulong methodPtr = methodPtrField.Read<UIntPtr>(delegateObject.Address, interior: false).ToUInt64();
+ if (methodPtr != 0)
+ {
+ method = runtime.GetMethodByInstructionPointer(methodPtr);
+ if (method is null)
+ {
+ methodPtr = methodPtrAuxField.Read<UIntPtr>(delegateObject.Address, interior: false).ToUInt64();
+ if (methodPtr != 0)
+ {
+ method = runtime.GetMethodByInstructionPointer(methodPtr);
+ }
+ }
+
+ return method is not null;
+ }
+ }
+
+ method = null;
+ return false;
+ }
+
+ /// <summary>Creates an indenting string.</summary>
+ /// <param name="count">The number of tabs.</param>
+ private static string Tabs(int count) => new string(' ', count * TabWidth);
+
+ /// <summary>Shortens a string to a maximum length by eliding part of the string with ...</summary>
+ private static string? Truncate(string? value, int maxLength)
+ {
+ if (value is not null && value.Length > maxLength)
+ {
+ value = $"...{value.Substring(value.Length - maxLength + 3)}";
+ }
+
+ return value;
+ }
+
+ /// <summary>Tries to get the state flags from a task.</summary>
+ private static bool TryGetTaskStateFlags(ClrObject obj, out int flags)
+ {
+ if (obj.Type?.GetFieldByName("m_stateFlags") is ClrInstanceField field)
+ {
+ flags = field.Read<int>(obj.Address, interior: false);
+ return true;
+ }
+
+ flags = 0;
+ return false;
+ }
+
+ /// <summary>Tries to read the specified value from the field of an entity.</summary>
+ private static bool TryRead<T>(IAddressableTypedEntity entity, string fieldName, out T result) where T : unmanaged
+ {
+ if (entity.Type?.GetFieldByName(fieldName) is not null)
+ {
+ result = entity.ReadField<T>(fieldName);
+ return true;
+ }
+
+ result = default;
+ return false;
+ }
+
+ /// <summary>Tries to read an object from a field of another object.</summary>
+ private static bool TryGetValidObjectField(ClrObject obj, string fieldName, out ClrObject result)
+ {
+ if (obj.Type?.GetFieldByName(fieldName) is ClrInstanceField field &&
+ field.ReadObject(obj.Address, interior: false) is { IsValid: true } validObject)
+ {
+ result = validObject;
+ return true;
+ }
+
+ result = default;
+ return false;
+ }
+
+ /// <summary>Gets whether a task has completed, based on its state flags.</summary>
+ private static bool IsCompleted(int taskStateFlags)
+ {
+ const int TASK_STATE_COMPLETED_MASK = 0x1600000;
+ return (taskStateFlags & TASK_STATE_COMPLETED_MASK) != 0;
+ }
+
+ /// <summary>Determines whether a span contains all zeros.</summary>
+ private static bool AllZero(ReadOnlySpan<byte> bytes)
+ {
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ if (bytes[i] != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>Gets a string representing interesting aspects of the specified task state flags.</summary>
+ /// <remarks>
+ /// The goal of this method isn't to detail every flag value (there are a lot).
+ /// Rather, we only want to render flags that are likely to be valuable, e.g.
+ /// we don't render WaitingForActivation, as that's the expected state for any
+ /// task that's showing up in a stack.
+ /// </remarks>
+ private static string DescribeTaskFlags(int stateFlags)
+ {
+ if (stateFlags != 0)
+ {
+ StringBuilder? sb = null;
+ void Append(string s)
+ {
+ sb ??= new StringBuilder();
+ if (sb.Length != 0) sb.Append("|");
+ sb.Append(s);
+ }
+
+ if ((stateFlags & 0x10000) != 0) Append("Started");
+ if ((stateFlags & 0x20000) != 0) Append("DelegateInvoked");
+ if ((stateFlags & 0x40000) != 0) Append("Disposed");
+ if ((stateFlags & 0x80000) != 0) Append("ExceptionObservedByParent");
+ if ((stateFlags & 0x100000) != 0) Append("CancellationAcknowledged");
+ if ((stateFlags & 0x200000) != 0) Append("Faulted");
+ if ((stateFlags & 0x400000) != 0) Append("Canceled");
+ if ((stateFlags & 0x800000) != 0) Append("WaitingOnChildren");
+ if ((stateFlags & 0x1000000) != 0) Append("RanToCompletion");
+ if ((stateFlags & 0x4000000) != 0) Append("CompletionReserved");
+
+ if (sb is not null)
+ {
+ return sb.ToString();
+ }
+ }
+
+ return " ";
+ }
+
+ /// <summary>Gets detailed help for the command.</summary>
+ protected override string GetDetailedHelp() => s_detailedHelpText;
+
+ /// <summary>Represents an async object to be used as a frame in an async "stack".</summary>
+ private sealed class AsyncObject
+ {
+ /// <summary>The actual object on the heap.</summary>
+ public ClrObject Object;
+ /// <summary>true if <see cref="Object"/> is an AsyncStateMachineBox.</summary>
+ public bool IsStateMachine;
+ /// <summary>A compiler-generated state machine extracted from the object, if one exists.</summary>
+ public IAddressableTypedEntity? StateMachine;
+ /// <summary>The state of the state machine, if the object contains a state machine.</summary>
+ public int AwaitState;
+ /// <summary>The <see cref="Object"/>'s Task state flags, if it's a task.</summary>
+ public int TaskStateFlags;
+ /// <summary>Whether this object meets the user-specified criteria for inclusion.</summary>
+ public bool IncludeInOutput;
+ /// <summary>true if this is a top-level instance that nothing else continues to.</summary>
+ /// <remarks>This starts off as true and then is flipped to false when we find a continuation to this object.</remarks>
+ public bool TopLevel = true;
+ /// <summary>The address of the native code for a method on the object (typically MoveNext for a state machine).</summary>
+ public ulong NativeCode;
+ /// <summary>This object's continuations.</summary>
+ public readonly List<ClrObject> Continuations = new();
+ }
+ }
+}
WriteLine(" IsFileLayout: {0}", module.IsFileLayout?.ToString() ?? "<unknown>");
WriteLine(" IndexFileSize: {0}", module.IndexFileSize?.ToString("X8") ?? "<none>");
WriteLine(" IndexTimeStamp: {0}", module.IndexTimeStamp?.ToString("X8") ?? "<none>");
- WriteLine(" Version: {0}", module.VersionData?.ToString() ?? "<none>");
- string versionString = module.VersionString;
+ WriteLine(" Version: {0}", module.GetVersionData()?.ToString() ?? "<none>");
+ string versionString = module.GetVersionString();
if (!string.IsNullOrEmpty(versionString))
{
WriteLine(" {0}", versionString);
}
- foreach (PdbFileInfo pdbFileInfo in module.PdbFileInfos)
+ foreach (PdbFileInfo pdbFileInfo in module.GetPdbFileInfos())
{
WriteLine(" PdbInfo: {0}", pdbFileInfo);
}
}
if (Segment)
{
- DisplaySegments(module.ImageBase);
+ DisplaySegments(module);
}
}
}
public IMemoryService MemoryService { get; set; }
- void DisplaySegments(ulong address)
+ void DisplaySegments(IModule module)
{
try
{
- if (Target.OperatingSystem == OSPlatform.Linux)
+ ELFFile elfFile = module.Services.GetService<ELFFile>();
+ if (elfFile is not null)
{
- Stream stream = MemoryService.CreateMemoryStream();
- var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
- if (elfFile.IsValid())
+ foreach (ELFProgramHeader programHeader in elfFile.Segments.Select((segment) => segment.Header))
{
- foreach (ELFProgramHeader programHeader in elfFile.Segments.Select((segment) => segment.Header))
- {
- uint flags = MemoryService.PointerSize == 8 ? programHeader.Flags : programHeader.Flags32;
- ulong loadAddress = programHeader.VirtualAddress;
- ulong loadSize = programHeader.VirtualSize;
- ulong fileOffset = programHeader.FileOffset;
- string type = programHeader.Type.ToString();
- WriteLine($" Segment: {loadAddress:X16} {loadSize:X16} {fileOffset:X16} {flags:x2} {type}");
- }
+ uint flags = MemoryService.PointerSize == 8 ? programHeader.Flags : programHeader.Flags32;
+ ulong loadAddress = programHeader.VirtualAddress;
+ ulong loadSize = programHeader.VirtualSize;
+ ulong fileOffset = programHeader.FileOffset;
+ string type = programHeader.Type.ToString();
+ WriteLine($" Segment: {loadAddress:X16} {loadSize:X16} {fileOffset:X16} {flags:x2} {type}");
}
}
- else if (Target.OperatingSystem == OSPlatform.OSX)
+ else
{
- Stream stream = MemoryService.CreateMemoryStream();
- MachOFile machOFile = new(new StreamAddressSpace(stream), address, true);
- if (machOFile.IsValid())
+ MachOFile machOFile = module.Services.GetService<MachOFile>();
+ if (machOFile is not null)
{
WriteLine(" LoadAddress: {0:X16}", machOFile.LoadAddress);
WriteLine(" LoadBias: {0:X16}", machOFile.PreferredVMBaseAddress);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.DebugServices;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+ [Command(
+ Name = "setsymbolserver",
+ Aliases = new string[] { "SetSymbolServer" },
+ Help = "Enable and set symbol server support for symbols and module download",
+ Platform = CommandPlatform.Global)]
+ public class SetSymbolServerCommand : CommandBase
+ {
+ public ISymbolService SymbolService { get; set; }
+
+ public IModuleService ModuleService { get; set; }
+
+ [Option(Name = "--ms", Aliases = new string[] { "-ms" }, Help = "Use the public Microsoft symbol server.")]
+ public bool MicrosoftSymbolServer { get; set; }
+
+ [Option(Name = "--mi", Aliases = new string[] { "-mi" }, Help = "Use the internal symweb symbol server.")]
+ public bool InternalSymbolServer { get; set; }
+
+ [Option(Name = "--disable", Aliases = new string[] { "-disable" }, Help = "Clear or disable symbol download support.")]
+ public bool Disable { get; set; }
+
+ [Option(Name = "--reset", Aliases = new string[] { "-reset" }, Help = "Reset the HTTP symbol servers clearing any cached failures.")]
+ public bool Reset { get; set; }
+
+ [Option(Name = "--cache", Aliases = new string[] { "-cache" }, Help = "Specify a symbol cache directory.")]
+ public string Cache { get; set; }
+
+ [Option(Name = "--directory", Aliases = new string[] { "-directory" }, Help = "Specify a directory to search for symbols.")]
+ public string Directory { get; set; }
+
+ [Option(Name = "--pat", Aliases = new string[] { "-pat" }, Help = "Access token to the authenticated server.")]
+ public string AccessToken { get; set; }
+
+ [Option(Name = "--timeout", Aliases = new string[] { "-timeout" }, Help = "Specify the symbol server timeout in minutes.")]
+ public int? Timeout { get; set; }
+
+ [Option(Name = "--retrycount", Aliases = new string[] { "-retrycount" }, Help = "Specify the symbol server timeout retry count.")]
+ public int? RetryCount { get; set; }
+
+ [Option(Name = "--loadsymbols", Aliases = new string[] { "-loadsymbols" }, Help = "Attempt to load native symbols for all modules.")]
+ public bool LoadSymbols { get; set; }
+
+ [Argument(Name = "url", Help = "Symbol server URL.")]
+ public string SymbolServerUrl { get; set; }
+
+ public override void Invoke()
+ {
+ if (MicrosoftSymbolServer && InternalSymbolServer)
+ {
+ throw new DiagnosticsException("Cannot have both -ms and -mi options");
+ }
+ if ((MicrosoftSymbolServer || InternalSymbolServer) && !string.IsNullOrEmpty(SymbolServerUrl))
+ {
+ throw new DiagnosticsException("Cannot have -ms or -mi option and a symbol server path");
+ }
+ if (Disable)
+ {
+ SymbolService.DisableSymbolStore();
+ }
+ if (Reset)
+ {
+ SymbolService.Reset();
+ }
+ if (MicrosoftSymbolServer || InternalSymbolServer || !string.IsNullOrEmpty(SymbolServerUrl))
+ {
+ if (string.IsNullOrEmpty(Cache))
+ {
+ Cache = SymbolService.DefaultSymbolCache;
+ }
+ SymbolService.AddSymbolServer(MicrosoftSymbolServer, InternalSymbolServer, SymbolServerUrl, AccessToken, Timeout, RetryCount);
+ }
+ if (!string.IsNullOrEmpty(Cache))
+ {
+ SymbolService.AddCachePath(Cache);
+ }
+ if (!string.IsNullOrEmpty(Directory))
+ {
+ SymbolService.AddDirectoryPath(Directory);
+ }
+ if (LoadSymbols)
+ {
+ foreach (IModule module in ModuleService.EnumerateModules())
+ {
+ if (!module.IsManaged)
+ {
+ Write($"Downloading symbol file for {module.FileName}");
+ string downloadedModulePath = module.LoadSymbols();
+ WriteLine(" {0}", downloadedModulePath != null ? "SUCCEEDED" : "FAILED");
+ }
+ }
+ }
+ else
+ {
+ Write(SymbolService.ToString());
+ }
+ }
+ }
+}
void IConsoleService.WriteError(string text) => WriteOutput(OutputType.Error, text);
+ bool IConsoleService.SupportsDml => false;
+
+ void IConsoleService.WriteDml(string text) => throw new NotSupportedException();
+
CancellationToken IConsoleService.CancellationToken { get; set; }
int IConsoleService.WindowWidth
private void AddModuleMembers(XElement element, IModule module, string symbolModuleName)
{
- AddMembers(element, typeof(IModule), module, nameof(IModule.ModuleIndex), nameof(IModule.PdbFileInfos), nameof(IModule.VersionString));
+ AddMembers(element, typeof(IModule), module, nameof(IModule.ModuleIndex), nameof(IModule.GetPdbFileInfos), nameof(IModule.GetVersionString));
if (symbolModuleName != null && IsModuleEqual(module, symbolModuleName))
{
_serviceProvider.AddService<ISymbolService>(_symbolService);
// Automatically enable symbol server support
- _symbolService.AddSymbolServer(msdl: true, symweb: false, symbolServerPath: null, authToken: null, timeoutInMinutes: 0);
+ _symbolService.AddSymbolServer(msdl: true, symweb: false, timeoutInMinutes: 6, retryCount: 5);
_symbolService.AddCachePath(_symbolService.DefaultSymbolCache);
}
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Runtime.Interop;
-using System;
using System.Threading;
namespace SOS.Extensions
internal class ConsoleServiceFromDebuggerServices : IConsoleService
{
private readonly DebuggerServices _debuggerServices;
+ private bool? _supportsDml;
public ConsoleServiceFromDebuggerServices(DebuggerServices debuggerServices)
{
public void WriteError(string text) => _debuggerServices.OutputString(DEBUG_OUTPUT.ERROR, text);
+ public void WriteDml(string text) => _debuggerServices.OutputDmlString(DEBUG_OUTPUT.NORMAL, text);
+
+ public bool SupportsDml => _supportsDml ??= _debuggerServices.SupportsDml;
+
public CancellationToken CancellationToken { get; set; }
int IConsoleService.WindowWidth => _debuggerServices.GetOutputWidth();
public int GetOutputWidth() => (int)VTable.GetOutputWidth(Self);
+ public bool SupportsDml
+ {
+ get
+ {
+ uint supported = 0;
+ VTable.SupportsDml(Self, &supported);
+ return supported != 0;
+ }
+ }
+
+ public void OutputDmlString(DEBUG_OUTPUT mask, string message)
+ {
+ if (message == null) throw new ArgumentNullException(nameof(message));
+
+ byte[] messageBytes = Encoding.ASCII.GetBytes(message + "\0");
+ fixed (byte* messagePtr = messageBytes)
+ {
+ VTable.OutputDmlString(Self, mask, messagePtr);
+ }
+ }
+
+ public HResult AddModuleSymbol(string symbolFileName)
+ {
+ if (symbolFileName == null)
+ {
+ throw new ArgumentNullException(nameof(symbolFileName));
+ }
+ byte[] symbolFileNameBytes = Encoding.ASCII.GetBytes(symbolFileName + "\0");
+ fixed (byte* ptr = symbolFileNameBytes)
+ {
+ return VTable.AddModuleSymbol(Self, IntPtr.Zero, ptr);
+ }
+ }
+
[StructLayout(LayoutKind.Sequential)]
private readonly unsafe struct IDebuggerServicesVTable
{
public readonly delegate* unmanaged[Stdcall]<IntPtr, int, ulong, byte*, int, out uint, out ulong, HResult> GetSymbolByOffset;
public readonly delegate* unmanaged[Stdcall]<IntPtr, int, byte*, out ulong, HResult> GetOffsetBySymbol;
public readonly delegate* unmanaged[Stdcall]<IntPtr, uint> GetOutputWidth;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint*, HResult> SupportsDml;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, DEBUG_OUTPUT, byte*, void> OutputDmlString;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, IntPtr, byte*, HResult> AddModuleSymbol;
}
}
}
/// </summary>
public static HostServices Instance { get; private set; }
+ /// <summary>
+ /// The time out in minutes passed to the HTTP symbol store
+ /// </summary>
+ public static int DefaultTimeout { get; set; } = 4;
+
+ /// <summary>
+ /// The retry count passed to the HTTP symbol store
+ /// </summary>
+ public static int DefaultRetryCount { get; set; } = 0;
+
/// <summary>
/// This is the main managed entry point that the native hosting code calls. It needs to be a single function
/// and is restricted to just a string parameter because host APIs (i.e. desktop clr) have this narrow interface.
{
_serviceProvider = new ServiceProvider();
_symbolService = new SymbolService(this);
+ _symbolService.DefaultTimeout = DefaultTimeout;
+ _symbolService.DefaultRetryCount = DefaultRetryCount;
_commandService = new CommandService(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ">!ext" : null);
_commandService.AddCommands(new Assembly[] { typeof(HostServices).Assembly });
_commandService.AddCommands(new Assembly[] { typeof(ClrMDHelper).Assembly });
public override uint? IndexTimeStamp { get; }
- public override VersionData VersionData
+ public override VersionData GetVersionData()
{
- get
+ if (InitializeValue(Module.Flags.InitializeVersion))
{
- if (InitializeValue(Module.Flags.InitializeVersion))
+ HResult hr = _moduleService._debuggerServices.GetModuleVersionInformation(ModuleIndex, out VS_FIXEDFILEINFO fileInfo);
+ if (hr.IsOK)
{
- int hr = _moduleService._debuggerServices.GetModuleVersionInformation(ModuleIndex, out VS_FIXEDFILEINFO fileInfo);
- if (hr == HResult.S_OK)
- {
- int major = (int)(fileInfo.dwFileVersionMS >> 16);
- int minor = (int)(fileInfo.dwFileVersionMS & 0xffff);
- int revision = (int)(fileInfo.dwFileVersionLS >> 16);
- int patch = (int)(fileInfo.dwFileVersionLS & 0xffff);
- _versionData = new VersionData(major, minor, revision, patch);
- }
- else
+ int major = (int)(fileInfo.dwFileVersionMS >> 16);
+ int minor = (int)(fileInfo.dwFileVersionMS & 0xffff);
+ int revision = (int)(fileInfo.dwFileVersionLS >> 16);
+ int patch = (int)(fileInfo.dwFileVersionLS & 0xffff);
+ _versionData = new VersionData(major, minor, revision, patch);
+ }
+ else
+ {
+ if (_moduleService.Target.OperatingSystem != OSPlatform.Windows)
{
- if (_moduleService.Target.OperatingSystem != OSPlatform.Windows)
- {
- _versionData = GetVersion();
- }
+ _versionData = GetVersion();
}
}
- return _versionData;
}
+ return _versionData;
}
- public override string VersionString
+ public override string GetVersionString()
{
- get
+ if (InitializeValue(Module.Flags.InitializeProductVersion))
{
- if (InitializeValue(Module.Flags.InitializeProductVersion))
+ HResult hr = _moduleService._debuggerServices.GetModuleVersionString(ModuleIndex, out _versionString);
+ if (!hr.IsOK)
{
- int hr = _moduleService._debuggerServices.GetModuleVersionString(ModuleIndex, out _versionString);
- if (hr != HResult.S_OK)
+ if (_moduleService.Target.OperatingSystem != OSPlatform.Windows && !IsPEImage)
{
- if (_moduleService.Target.OperatingSystem != OSPlatform.Windows && !IsPEImage)
- {
- _versionString = _moduleService.GetVersionString(ImageBase);
- }
+ _versionString = _moduleService.GetVersionString(this);
}
}
- return _versionString;
}
+ return _versionString;
+ }
+
+ public override string LoadSymbols()
+ {
+ string symbolFile = _moduleService.SymbolService.DownloadSymbolFile(this);
+ if (symbolFile is not null)
+ {
+ _moduleService._debuggerServices.AddModuleSymbol(symbolFile);
+ }
+ return symbolFile;
}
#endregion
bool IModuleSymbols.TryGetSymbolName(ulong address, out string symbol, out ulong displacement)
{
- return _moduleService._debuggerServices.GetSymbolByOffset(ModuleIndex, address, out symbol, out displacement) == HResult.S_OK;
+ return _moduleService._debuggerServices.GetSymbolByOffset(ModuleIndex, address, out symbol, out displacement).IsOK;
}
bool IModuleSymbols.TryGetSymbolAddress(string name, out ulong address)
{
- return _moduleService._debuggerServices.GetOffsetBySymbol(ModuleIndex, name, out address) == HResult.S_OK;
+ return _moduleService._debuggerServices.GetOffsetBySymbol(ModuleIndex, name, out address).IsOK;
}
#endregion
protected override bool TryGetSymbolAddressInner(string name, out ulong address)
{
- return _moduleService._debuggerServices.GetOffsetBySymbol(ModuleIndex, name, out address) == HResult.S_OK;
+ return _moduleService._debuggerServices.GetOffsetBySymbol(ModuleIndex, name, out address).IsOK;
}
protected override ModuleService ModuleService => _moduleService;
var modules = new Dictionary<ulong, IModule>();
HResult hr = _debuggerServices.GetNumberModules(out uint loadedModules, out uint unloadedModules);
- if (hr == HResult.S_OK)
+ if (hr.IsOK)
{
for (int moduleIndex = 0; moduleIndex < loadedModules; moduleIndex++)
{
hr = _debuggerServices.GetModuleInfo(moduleIndex, out ulong imageBase, out ulong imageSize, out uint timestamp, out uint checksum);
- if (hr == HResult.S_OK)
+ if (hr.IsOK)
{
hr = _debuggerServices.GetModuleName(moduleIndex, out string imageName);
if (hr < HResult.S_OK)
[Command(Name = "dbgout", DefaultOptions = "dbgout", Help = "Enable/disable (-off) internal SOS logging.")]
[Command(Name = "dumpalc", DefaultOptions = "DumpALC", Help = "Displays details about a collectible AssemblyLoadContext into which the specified object is loaded.")]
[Command(Name = "dumparray", DefaultOptions = "DumpArray", Help = "Displays details about a managed array.")]
- [Command(Name = "dumpasync", DefaultOptions = "DumpAsync", Help = "Displays info about async state machines on the garbage-collected heap.")]
[Command(Name = "dumpassembly", DefaultOptions = "DumpAssembly", Help = "Displays details about an assembly.")]
[Command(Name = "dumpclass", DefaultOptions = "DumpClass", Help = "Displays information about a EE class structure at the specified address.")]
[Command(Name = "dumpdelegate", DefaultOptions = "DumpDelegate", Help = "Displays information about a delegate.")]
[Command(Name = "histobj", DefaultOptions = "HistObj", Help = "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.")]
[Command(Name = "histobjfind", DefaultOptions = "HistObjFind", Help = "Displays all the log entries that reference an object at the specified address.")]
[Command(Name = "histroot", DefaultOptions = "HistRoot", Help = "Displays information related to both promotions and relocations of the specified root.")]
- [Command(Name = "setsymbolserver", DefaultOptions = "SetSymbolServer", Help = "Enables the symbol server support.")]
[Command(Name = "verifyheap", DefaultOptions = "VerifyHeap", Help = "Checks the GC heap for signs of corruption.")]
[Command(Name = "threadpool", DefaultOptions = "ThreadPool", Help = "Lists basic information about the thread pool.")]
[Command(Name = "soshelp", DefaultOptions = "Help", Help = "Displays help for a specific SOS command.")]
{
return HResult.E_FAIL;
}
- if (module.VersionData is null)
+ VersionData versionData = module.GetVersionData();
+ if (versionData is null)
{
return HResult.E_FAIL;
}
pFileInfo->dwStrucVersion = 0;
pFileInfo->dwFileFlagsMask = 0;
pFileInfo->dwFileFlags = 0;
-
- VersionData versionData = module.VersionData;
pFileInfo->dwFileVersionMS = (uint)versionData.Minor | (uint)versionData.Major << 16;
pFileInfo->dwFileVersionLS = (uint)versionData.Patch | (uint)versionData.Revision << 16;
if (fileVersionBufferSizeInBytes > 0) {
*fileVersionBuffer = 0;
}
- string versionString = module.VersionString;
+ string versionString = module.GetVersionString();
if (versionString != null)
{
try
{
return HResult.E_INVALIDARG;
}
- if (module.VersionData is null)
+ VersionData versionData = module.GetVersionData();
+ if (versionData is null)
{
return HResult.E_FAIL;
}
fileInfo->dwStrucVersion = 0;
fileInfo->dwFileFlagsMask = 0;
fileInfo->dwFileFlags = 0;
-
- VersionData versionData = module.VersionData;
fileInfo->dwFileVersionMS = (uint)versionData.Minor | (uint)versionData.Major << 16;
fileInfo->dwFileVersionLS = (uint)versionData.Patch | (uint)versionData.Revision << 16;
}
else if (item == "\\StringFileInfo\\040904B0\\FileVersion")
{
*buffer = 0;
- string versionString = module.VersionString;
+ string versionString = module.GetVersionString();
if (versionString == null)
{
return HResult.E_FAIL;
private readonly ISymbolService _symbolService;
private readonly IMemoryService _memoryService;
+ private readonly ulong _ignoreAddressBitsMask;
public SymbolServiceWrapper(ISymbolService symbolService, IMemoryService memoryService)
{
Debug.Assert(memoryService != null);
_symbolService = symbolService;
_memoryService = memoryService;
+ _ignoreAddressBitsMask = memoryService.SignExtensionMask();
Debug.Assert(_symbolService != null);
VTableBuilder builder = AddInterface(IID_ISymbolService, validate: false);
- builder.AddMethod(new IsSymbolStoreEnabledDelegate((IntPtr self) => _symbolService.IsSymbolStoreEnabled));
- builder.AddMethod(new InitializeSymbolStoreDelegate(InitializeSymbolStore));
builder.AddMethod(new ParseSymbolPathDelegate(ParseSymbolPath));
- builder.AddMethod(new DisplaySymbolStoreDelegate(DisplaySymbolStore));
- builder.AddMethod(new DisableSymbolStoreDelegate((IntPtr self) => _symbolService.DisableSymbolStore()));
- builder.AddMethod(new LoadNativeSymbolsDelegate(LoadNativeSymbols));
- builder.AddMethod(new LoadNativeSymbolsFromIndexDelegate(LoadNativeSymbolsFromIndex));
builder.AddMethod(new LoadSymbolsForModuleDelegate(LoadSymbolsForModule));
builder.AddMethod(new DisposeDelegate(Dispose));
builder.AddMethod(new ResolveSequencePointDelegate(ResolveSequencePoint));
Trace.TraceInformation("SymbolServiceWrapper.Destroy");
}
- /// <summary>
- /// Initializes symbol loading. Adds the symbol server and/or the cache path (if not null) to the list of
- /// symbol servers. This API can be called more than once to add more servers to search.
- /// </summary>
- /// <param name="msdl">if true, use the public Microsoft server</param>
- /// <param name="symweb">if true, use symweb internal server and protocol (file.ptr)</param>
- /// <param name="symbolServerPath">symbol server url (optional)</param>
- /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional)</param>
- /// <param name="symbolCachePath">symbol cache directory path (optional)</param>
- /// <param name="symbolDirectoryPath">symbol directory path to search (optional)</param>
- /// <returns>if false, failure</returns>
- private bool InitializeSymbolStore(
- IntPtr self,
- bool msdl,
- bool symweb,
- string symbolServerPath,
- string authToken,
- int timeoutInMinutes,
- string symbolCachePath,
- string symbolDirectoryPath)
- {
- if (msdl || symweb || symbolServerPath != null)
- {
- // Add the default symbol cache if no cache specified and adding server
- if (symbolCachePath == null)
- {
- symbolCachePath = _symbolService.DefaultSymbolCache;
- }
- if (!_symbolService.AddSymbolServer(msdl, symweb, symbolServerPath, authToken, timeoutInMinutes))
- {
- return false;
- }
- }
- if (symbolCachePath != null)
- {
- _symbolService.AddCachePath(symbolCachePath);
- }
- if (symbolDirectoryPath != null)
- {
- _symbolService.AddDirectoryPath(symbolDirectoryPath);
- }
- return true;
- }
-
/// <summary>
/// Parse the Windows sympath format
/// </summary>
if (string.IsNullOrWhiteSpace(symbolPath)) {
return false;
}
+ _symbolService.DisableSymbolStore();
return _symbolService.ParseSymbolPathFixDefault(symbolPath);
}
- /// <summary>
- /// Displays the symbol server and cache configuration
- /// </summary>
- private void DisplaySymbolStore(IntPtr self, WriteLine writeLine)
- {
- writeLine(_symbolService.ToString());
- }
-
- /// <summary>
- /// Load native symbols and modules (i.e. DAC, DBI).
- /// </summary>
- /// <param name="callback">called back for each symbol file loaded</param>
- /// <param name="parameter">callback parameter</param>
- /// <param name="config">Target configuration: Windows, Linux or OSX</param>
- /// <param name="moduleFilePath">module path</param>
- /// <param name="address">module base address</param>
- /// <param name="size">module size</param>
- /// <param name="readMemory">read memory callback delegate</param>
- private void LoadNativeSymbols(
- IntPtr self,
- SymbolFileCallback callback,
- IntPtr parameter,
- RuntimeConfiguration config,
- string moduleFilePath,
- ulong address,
- uint size)
- {
- if (_symbolService.IsSymbolStoreEnabled)
- {
- try
- {
- KeyGenerator generator = null;
- if (config == RuntimeConfiguration.UnixCore)
- {
- Stream stream = _memoryService.CreateMemoryStream();
- var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
- generator = new ELFFileKeyGenerator(Tracer.Instance, elfFile, moduleFilePath);
- }
- else if (config == RuntimeConfiguration.OSXCore)
- {
- Stream stream = _memoryService.CreateMemoryStream();
- var machOFile = new MachOFile(new StreamAddressSpace(stream), address, true);
- generator = new MachOFileKeyGenerator(Tracer.Instance, machOFile, moduleFilePath);
- }
- else if (config == RuntimeConfiguration.WindowsCore || config == RuntimeConfiguration.WindowsDesktop)
- {
- Stream stream = _memoryService.CreateMemoryStream(address, size);
- var peFile = new PEFile(new StreamAddressSpace(stream), true);
- generator = new PEFileKeyGenerator(Tracer.Instance, peFile, moduleFilePath);
- }
- else
- {
- Trace.TraceError("LoadNativeSymbols: unsupported config {0}", config);
- }
- if (generator != null)
- {
- IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.SymbolKey | KeyTypeFlags.DacDbiKeys);
- foreach (SymbolStoreKey key in keys)
- {
- string moduleFileName = Path.GetFileName(key.FullPathName);
- Trace.TraceInformation("{0} {1}", key.FullPathName, key.Index);
-
- string downloadFilePath = _symbolService.DownloadFile(key);
- if (downloadFilePath != null)
- {
- Trace.TraceInformation("{0}: {1}", moduleFileName, downloadFilePath);
- callback(parameter, moduleFileName, downloadFilePath);
- }
- }
- }
- }
- catch (Exception ex) when
- (ex is DiagnosticsException ||
- ex is BadInputFormatException ||
- ex is InvalidVirtualAddressException ||
- ex is ArgumentOutOfRangeException ||
- ex is IndexOutOfRangeException ||
- ex is TaskCanceledException)
- {
- Trace.TraceError("{0} address {1:X16}: {2}", moduleFilePath, address, ex.Message);
- }
- }
- }
-
- /// <summary>
- /// Load native modules (i.e. DAC, DBI) from the runtime build id.
- /// </summary>
- /// <param name="callback">called back for each symbol file loaded</param>
- /// <param name="parameter">callback parameter</param>
- /// <param name="config">Target configuration: Windows, Linux or OSX</param>
- /// <param name="moduleFilePath">module path</param>
- /// <param name="specialKeys">if true, returns the DBI/DAC keys, otherwise the identity key</param>
- /// <param name="moduleIndexSize">build id size</param>
- /// <param name="moduleIndex">pointer to build id</param>
- private void LoadNativeSymbolsFromIndex(
- IntPtr self,
- SymbolFileCallback callback,
- IntPtr parameter,
- RuntimeConfiguration config,
- string moduleFilePath,
- bool specialKeys,
- int moduleIndexSize,
- IntPtr moduleIndex)
- {
- if (_symbolService.IsSymbolStoreEnabled)
- {
- try
- {
- KeyTypeFlags flags = specialKeys ? KeyTypeFlags.DacDbiKeys : KeyTypeFlags.IdentityKey;
- byte[] id = new byte[moduleIndexSize];
- Marshal.Copy(moduleIndex, id, 0, moduleIndexSize);
-
- IEnumerable<SymbolStoreKey> keys = null;
- switch (config)
- {
- case RuntimeConfiguration.UnixCore:
- keys = ELFFileKeyGenerator.GetKeys(flags, moduleFilePath, id, symbolFile: false, symbolFileName: null);
- break;
-
- case RuntimeConfiguration.OSXCore:
- keys = MachOFileKeyGenerator.GetKeys(flags, moduleFilePath, id, symbolFile: false, symbolFileName: null);
- break;
-
- case RuntimeConfiguration.WindowsCore:
- case RuntimeConfiguration.WindowsDesktop:
- uint timeStamp = BitConverter.ToUInt32(id, 0);
- uint fileSize = BitConverter.ToUInt32(id, 4);
- SymbolStoreKey key = PEFileKeyGenerator.GetKey(moduleFilePath, timeStamp, fileSize);
- keys = new SymbolStoreKey[] { key };
- break;
-
- default:
- Trace.TraceError("LoadNativeSymbolsFromIndex: unsupported platform {0}", config);
- return;
- }
- foreach (SymbolStoreKey key in keys)
- {
- string moduleFileName = Path.GetFileName(key.FullPathName);
- Trace.TraceInformation("{0} {1}", key.FullPathName, key.Index);
-
- string downloadFilePath = _symbolService.DownloadFile(key);
- if (downloadFilePath != null)
- {
- Trace.TraceInformation("{0}: {1}", moduleFileName, downloadFilePath);
- callback(parameter, moduleFileName, downloadFilePath);
- }
- }
- }
- catch (Exception ex) when (ex is BadInputFormatException || ex is InvalidVirtualAddressException || ex is TaskCanceledException)
- {
- Trace.TraceError("{0} - {1}", ex.Message, moduleFilePath);
- }
- }
- }
-
/// <summary>
/// Get expression helper for native SOS.
/// </summary>
ISymbolFile symbolFile = null;
if (loadedPeAddress != 0)
{
+ loadedPeAddress &= _ignoreAddressBitsMask;
Stream peStream = _memoryService.CreateMemoryStream(loadedPeAddress, loadedPeSize);
symbolFile = _symbolService.OpenSymbolFile(assemblyPath, isFileLayout, peStream);
}
if (inMemoryPdbAddress != 0)
{
+ inMemoryPdbAddress &= _ignoreAddressBitsMask;
Stream pdbStream = _memoryService.CreateMemoryStream(inMemoryPdbAddress, inMemoryPdbSize);
symbolFile = _symbolService.OpenSymbolFile(pdbStream);
}
VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
!VERIFY:.*UNKNOWN.*
-SOSCOMMAND:DumpAsync
-VERIFY:\s*Statistics:\s+
-VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
-VERIFY:\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*
-VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
+SOSCOMMAND:DumpAsync --completed
+VERIFY:\s*STACK <DECVAL>\s*
+VERIFY?:\s*<< Awaiting: <HEXVAL>\s+<HEXVAL>\s+.* >>\s+
+VERIFY:\s*<HEXVAL>\s+<HEXVAL>\s+\((<DECVAL>)?\)\s+.*
+
+SOSCOMMAND:DumpAsync --stats
+VERIFY:\s+MT\s+Count\s+Type\s+
+VERIFY:\s*<HEXVAL>\s+<DECVAL>\s+.*
+!VERIFY:.*UNKNOWN.*
-SOSCOMMAND:DumpAsync -mt <POUT>\s+MT\s+Count\s+TotalSize\s+Class Name\s+(<HEXVAL>)\s+<DECVAL>\s+<DECVAL>\s+.*<POUT>
-VERIFY:\s*Statistics:\s+
-VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
-VERIFY:\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*
-VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
+SOSCOMMAND:DumpAsync --methodtable 0x<POUT>\s+MT\s+Count\s+Type\s+(<HEXVAL>)\s+<DECVAL>\s+.*<POUT>
+VERIFY:\s*STACK <DECVAL>\s*
+VERIFY?:\s*<< Awaiting: <HEXVAL>\s+<HEXVAL>\s+.* >>\s+
+VERIFY:\s*<HEXVAL>\s+<HEXVAL>\s+\((<DECVAL>)?\)\s+.*
+
+SOSCOMMAND:DumpMT --stats <PREVPOUT>
+!VERIFY:\s*<HEXVAL> is not a MethodTable
-SOSCOMMAND:DumpAsync -mt <PREVOUT> -fields
+SOSCOMMAND:DumpAsync --coalesce
+VERIFY:\s*STACKS <DECVAL>\s*
+VERIFY:\s*\[<DECVAL>\]\s+<HEXVAL>\s+\((<DECVAL>)?\)\s+.*
\ No newline at end of file
if(varToExpand[0] == L'(' && pEndCast != NULL)
{
- int cchCastTypeName = ((int)(pEndCast-1-varToExpand))/2;
+ size_t cchCastTypeName = ((size_t)(pEndCast-1-varToExpand))/2;
PopulateType();
if(_wcslen(pTypeName) != (cchCastTypeName) ||
_wcsncmp(varToExpand+1, pTypeName, cchCastTypeName) != 0)
BOOL isNull = TRUE;
ToRelease<ICorDebugValue> pInnerValue;
- CorElementType corElemType;
+ CorElementType corElemType = ELEMENT_TYPE_MAX;
ULONG32 cbSize = 0;
if(pValue != NULL)
{
// one piece of data ICorDebugType will tell us if needed.
if(FAILED(GetCanonicalElementTypeForTypeName(GetTypeName(), &corElemType)))
{
- pTypeCast->GetType(&corElemType);
+ IfFailRet(pTypeCast->GetType(&corElemType));
}
switch(corElemType)
{
ULONG chTypeDef;
pMetadata->GetTypeRefProps(token, NULL, NULL, 0, &chTypeDef);
- if(chTypeDef > _countof(nameBuffer))
+ if(chTypeDef > ARRAY_SIZE(nameBuffer))
{
*pResult = FALSE;
return Status;
}
- IfFailRet(pMetadata->GetTypeRefProps(token, NULL, nameBuffer, _countof(nameBuffer), &chTypeDef));
+ IfFailRet(pMetadata->GetTypeRefProps(token, NULL, nameBuffer, ARRAY_SIZE(nameBuffer), &chTypeDef));
}
else if(type == mdtTypeDef)
{
ULONG chTypeDef;
pMetadata->GetTypeDefProps(token, NULL, 0, &chTypeDef, NULL, NULL);
- if(chTypeDef > _countof(nameBuffer))
+ if(chTypeDef > ARRAY_SIZE(nameBuffer))
{
*pResult = FALSE;
return Status;
}
- IfFailRet(pMetadata->GetTypeDefProps(token, nameBuffer, _countof(nameBuffer), &chTypeDef, NULL, NULL));
+ IfFailRet(pMetadata->GetTypeDefProps(token, nameBuffer, ARRAY_SIZE(nameBuffer), &chTypeDef, NULL, NULL));
}
if(_wcscmp(nameBuffer, L"System.ValueType") == 0 ||
return INT_MAX;
}
+HRESULT
+DbgEngServices::SupportsDml(PULONG supported)
+{
+ ULONG opts = 0;
+ HRESULT hr = m_control->GetEngineOptions(&opts);
+ *supported = (SUCCEEDED(hr) && (opts & DEBUG_ENGOPT_PREFER_DML) == DEBUG_ENGOPT_PREFER_DML) ? 1 : 0;
+ return hr;
+}
+
+void
+DbgEngServices::OutputDmlString(
+ ULONG mask,
+ PCSTR message)
+{
+ m_control->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, mask, "%s", message);
+}
+
+HRESULT
+DbgEngServices::AddModuleSymbol(
+ void* param,
+ const char* symbolFileName)
+{
+ return S_OK;
+}
+
//----------------------------------------------------------------------------
// IRemoteMemoryService
//----------------------------------------------------------------------------
{
if (strlen(symbolPath) > 0)
{
- symbolService->DisableSymbolStore();
-
if (!symbolService->ParseSymbolPath(symbolPath))
{
m_control->Output(DEBUG_OUTPUT_ERROR, "Windows symbol path parsing FAILED %s\n", symbolPath.GetPtr());
ULONG STDMETHODCALLTYPE GetOutputWidth();
+ HRESULT STDMETHODCALLTYPE SupportsDml(
+ PULONG supported);
+
+ void STDMETHODCALLTYPE OutputDmlString(
+ ULONG mask,
+ PCSTR message);
+
+ HRESULT STDMETHODCALLTYPE AddModuleSymbol(
+ void* param,
+ const char* symbolFileName);
+
//----------------------------------------------------------------------------
// IRemoteMemoryService
//----------------------------------------------------------------------------
if (n > 127) {
n = 127;
}
- strncpy_s (line,_countof(line), begin, n);
+ strncpy_s (line,ARRAY_SIZE(line), begin, n);
line[n] = '\0';
ExtOut ("%s", line);
begin += n;
char symbol[1024];
ULONG64 displacement;
- HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, ARRAY_SIZE(symbol), NULL, &displacement);
if (SUCCEEDED(hr) && symbol[0] != '\0')
{
ExtOut("%s", symbol);
if (!bSuppressLines)
{
ULONG line;
- hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
+ hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, ARRAY_SIZE(filename), NULL, NULL);
if (SUCCEEDED(hr))
{
ExtOut(" [%s:%d]", filename, line);
virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
virtual LPCSTR GetSPName() const { return s_SPName; }
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
- { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs); }
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
virtual LPCSTR GetSPName() const { return s_SPName; }
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
- { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs); }
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
virtual LPCSTR GetSPName() const { return s_SPName; }
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
- { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs); }
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
virtual LPCSTR GetSPName() const { return s_SPName; }
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
- { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs);}
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs);}
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
}
ULONG_PTR prevPC = PC;
- DisasmAndClean (PC, line, _countof(line));
+ DisasmAndClean (PC, line, ARRAY_SIZE(line));
// look at the disassembled bytes
ptr = line;
ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - PCBegin);
ULONG_PTR OrigPC = OrigInstrAddr;
- DisasmAndClean(OrigPC, line, _countof(line));
+ DisasmAndClean(OrigPC, line, ARRAY_SIZE(line));
//
// Increment the real PC based on the size of the unmodifed
while(PC < PCEnd)
{
ULONG_PTR currentPC = PC;
- DisasmAndClean (PC, line, _countof(line));
+ DisasmAndClean (PC, line, ARRAY_SIZE(line));
// This is the closing of the previous run.
// Check the next instruction. if it's not a the last movk, handle the accumulated value
ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - PCBegin);
ULONG_PTR OrigPC = OrigInstrAddr;
- DisasmAndClean(OrigPC, line, _countof(line));
+ DisasmAndClean(OrigPC, line, ARRAY_SIZE(line));
//
// Increment the real PC based on the size of the unmodifed
};
- for (size_t i = 0; i < sizeof(rgRegNames)/sizeof(rgRegNames[0]); i++)
+ for (size_t i = 0; i < ARRAY_SIZE(rgRegNames); i++)
{
if (!strncmp(ptr, rgRegNames[i].pszName, rgRegNames[i].cchName))
{
ULONG_PTR InstrAddr = IP;
- DisasmAndClean (IP, line, _countof(line));
+ DisasmAndClean (IP, line, ARRAY_SIZE(line));
// look at key word
ptr = line;
ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - IPBegin);
ULONG_PTR OrigIP = OrigInstrAddr;
- DisasmAndClean(OrigIP, line, _countof(line));
+ DisasmAndClean(OrigIP, line, ARRAY_SIZE(line));
//
// Increment the real IP based on the size of the unmodifed
}
}
-void GCPrintPinnedHeapSegmentInfo(const GCHeapDetails &heap, DWORD_PTR &total_allocated_size, DWORD_PTR total_committed_size)
+void GCPrintPinnedHeapSegmentInfo(const GCHeapDetails &heap, DWORD_PTR &total_allocated_size, DWORD_PTR& total_committed_size)
{
DWORD_PTR dwAddrSeg;
DacpHeapSegmentData segment;
// this loop will get information for all the heap segments in this heap. The outer loop iterates once
// for the "normal" generation segments and once for the large object heap. The inner loop follows the chain
// of segments rooted at AddrSegs[i]
- for (unsigned int i = 0; i < sizeof(AddrSegs)/sizeof(AddrSegs[0]); ++i)
+ for (unsigned int i = 0; i < ARRAY_SIZE(AddrSegs); ++i)
{
if (AddrSegs[i] == NULL)
{
*outWasted += wasted;
return toReturn;
-}
\ No newline at end of file
+}
{&rootstr.data, COSTRING},
};
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
return Status;
if (nArg != 1)
{&objstr.data, COSTRING},
};
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
return Status;
if (nArg != 1)
{&objstr.data, COSTRING},
};
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
return Status;
if (nArg != 1)
do
{
- hr = handles->Next(_countof(data), data, &fetched);
+ hr = handles->Next(ARRAY_SIZE(data), data, &fetched);
if (FAILED(hr))
{
map[obj].push_back(target);
}
}
- } while (fetched == _countof(data));
+ } while (fetched == ARRAY_SIZE(data));
}
///////////////////////////////////////////////////////////////////////////////
do
{
// Fetch more handles
- hr = pEnum->Next(_countof(handles), handles, &fetched);
+ hr = pEnum->Next(ARRAY_SIZE(handles), handles, &fetched);
if (FAILED(hr))
{
ExtOut("Failed to request more handles.\n");
}
}
}
- while (_countof(handles) == fetched);
+ while (ARRAY_SIZE(handles) == fetched);
return total;
}
do
{
- hr = handles->Next(_countof(data), data, &fetched);
+ hr = handles->Next(ARRAY_SIZE(data), data, &fetched);
if (FAILED(hr))
{
handlearray[pos++] = (DWORD_PTR)data[i].Handle;
}
}
- } while (fetched == _countof(data));
+ } while (fetched == ARRAY_SIZE(data));
return pos;
}
do
{
- hr = handles->Next(_countof(data), data, &fetched);
+ hr = handles->Next(ARRAY_SIZE(data), data, &fetched);
if (FAILED(hr))
break;
for (unsigned int i = 0; i < fetched; ++i)
PrintRoot(W("handle"), (size_t)data[i].Handle);
- } while (fetched == _countof(data));
+ } while (fetched == ARRAY_SIZE(data));
}
/* static */ void HeapTraverser::GatherTypes(DWORD_PTR objAddr,size_t Size,
WCHAR szFunctionName[1024];
hr = m_pImport->GetMethodProps(token, &memTypeDef,
- szFunctionName, _countof(szFunctionName), &nameLen,
+ szFunctionName, ARRAY_SIZE(szFunctionName), &nameLen,
&flags, &pbSigBlob, &ulSigBlob, &ulCodeRVA, &ulImplFlags);
if (FAILED (hr))
{
m_szName[0] = L'\0';
if (memTypeDef != mdTypeDefNil)
{
- hr = NameForTypeDef_s (memTypeDef, m_pImport, m_szName, _countof(m_szName));
+ hr = NameForTypeDef_s (memTypeDef, m_pImport, m_szName, ARRAY_SIZE(m_szName));
if (SUCCEEDED (hr)) {
- wcscat_s (m_szName, _countof(m_szName), W("."));
+ wcscat_s (m_szName, ARRAY_SIZE(m_szName), W("."));
}
}
- wcscat_s (m_szName, _countof(m_szName), szFunctionName);
+ wcscat_s (m_szName, ARRAY_SIZE(m_szName), szFunctionName);
LONG lSigBlobRemaining;
hr = GetFullNameForMD(pbSigBlob, ulSigBlob, &lSigBlobRemaining);
m_dacFilePath = _strdup(dacModulePath.c_str());
}
}
-
- if (m_dacFilePath == nullptr)
- {
- // Attempt to only load the DAC/DBI modules
- LoadRuntimeModules();
- }
}
return m_dacFilePath;
}
m_dbiFilePath = _strdup(dbiModulePath.c_str());
}
}
-
- if (m_dbiFilePath == nullptr)
- {
- // Attempt to only load the DAC/DBI modules
- LoadRuntimeModules();
- }
}
return m_dbiFilePath;
}
_ASSERTE(pFileInfo);
_ASSERTE(g_ExtSymbols2 != nullptr);
-#ifdef FEATURE_PAL
- // Load the symbols for runtime. On Linux we are looking for the "sccsid"
- // global so "libcoreclr.so/.dylib" symbols need to be loaded.
- LoadNativeSymbols(true);
-#endif
-
HRESULT hr = g_ExtSymbols2->GetModuleVersionInformation(
m_index, 0, "\\", pFileInfo, sizeof(VS_FIXEDFILEINFO), NULL);
ExtOut(" DBI file path: %s\n", m_dbiFilePath);
}
}
-
-/**********************************************************************\
- * Attempt to download the runtime modules (runtime, DAC and DBI)
-\**********************************************************************/
-void Runtime::LoadRuntimeModules()
-{
- ISymbolService* symbolService = GetSymbolService();
- if (symbolService != nullptr)
- {
- if (m_runtimeInfo != nullptr)
- {
- symbolService->LoadNativeSymbolsFromIndex(
- SymbolFileCallback,
- this,
- GetRuntimeConfiguration(),
- GetRuntimeDllName(),
- true, // special keys (runtime, DAC and DBI)
- m_runtimeInfo->RuntimeModuleIndex[0], // size of module index
- &m_runtimeInfo->RuntimeModuleIndex[1]); // beginning of index
- }
- else
- {
- symbolService->LoadNativeSymbols(
- SymbolFileCallback,
- this,
- GetRuntimeConfiguration(),
- m_name,
- m_address,
- (int)m_size);
- }
- }
-}
-
-/**********************************************************************\
- * Called by LoadRuntimeModules to set the DAC and DBI file paths
-\**********************************************************************/
-void Runtime::SymbolFileCallback(const char* moduleFileName, const char* symbolFilePath)
-{
- if (strcmp(moduleFileName, GetRuntimeDllName()) == 0) {
- return;
- }
- if (strcmp(moduleFileName, GetDacDllName()) == 0) {
- SetDacFilePath(symbolFilePath);
- return;
- }
- if (strcmp(moduleFileName, NET_DBI_DLL_NAME_A) == 0) {
- SetDbiFilePath(symbolFilePath);
- return;
- }
-}
virtual ~Runtime();
- void LoadRuntimeModules();
-
- void SymbolFileCallback(const char* moduleFileName, const char* symbolFilePath);
-
- static void SymbolFileCallback(void* param, const char* moduleFileName, const char* symbolFilePath)
- {
- ((Runtime*)param)->SymbolFileCallback(moduleFileName, symbolFilePath);
- }
-
void SetDacFilePath(LPCSTR dacFilePath)
{
if (m_dacFilePath == nullptr && dacFilePath != nullptr) {
pid = ::GetCurrentProcessId();
}
char pidstr[128];
- sprintf_s(pidstr, _countof(pidstr), "sos%d", pid);
+ sprintf_s(pidstr, ARRAY_SIZE(pidstr), "sos%d", pid);
strcat_s(tmpPath, MAX_LONGPATH, pidstr);
strcat_s(tmpPath, MAX_LONGPATH, DIRECTORY_SEPARATOR_STR_A);
hr = i->GetTypeDefProps(token, szName, 49, &cLen, NULL, NULL);
if (FAILED(hr))
- StringCchCopyW(szName, COUNTOF(szName), W("<unknown type def>"));
+ StringCchCopyW(szName, ARRAY_SIZE(szName), W("<unknown type def>"));
printf("%S", szName);
}
hr = i->GetTypeRefProps(token, NULL, szName, 49, &cLen);
if (FAILED(hr))
- StringCchCopyW(szName, COUNTOF(szName), W("<unknown type ref>"));
+ StringCchCopyW(szName, ARRAY_SIZE(szName), W("<unknown type ref>"));
printf("%S", szName);
}
NULL, NULL, NULL, NULL, NULL, NULL);
if (FAILED(hr))
- StringCchCopyW(szFieldName, COUNTOF(szFieldName), W("<unknown field def>"));
+ StringCchCopyW(szFieldName, ARRAY_SIZE(szFieldName), W("<unknown field def>"));
hr = i->GetTypeDefProps(mdClass, szClassName, 49, &cLen,
NULL, NULL);
if (FAILED(hr))
- StringCchCopyW(szClassName, COUNTOF(szClassName), W("<unknown type def>"));
+ StringCchCopyW(szClassName, ARRAY_SIZE(szClassName), W("<unknown type def>"));
printf("%S::%S", szClassName, szFieldName);
}
MethodSigArgPrettyPrinter methodPrettyPrinter(sig, cbSigBlob, i);
if (FAILED(hr))
- StringCchCopyW(szFieldName, COUNTOF(szFieldName), W("<unknown method def>"));
+ StringCchCopyW(szFieldName, ARRAY_SIZE(szFieldName), W("<unknown method def>"));
else
methodPrettyPrinter.HandleReturnType();
NULL, NULL);
if (FAILED(hr))
- StringCchCopyW(szClassName, COUNTOF(szClassName), W("<unknown type def>"));
+ StringCchCopyW(szClassName, ARRAY_SIZE(szClassName), W("<unknown type def>"));
printf("%S::%S", szClassName, szFieldName);
methodPrettyPrinter.HandleArguments(); // Safe to call in all cases if HandleReturnType hasn't been called. Will do nothing.
{
if (FAILED(i->GetTypeRefProps(cr, NULL, szName, 50, &cLen)))
{
- StringCchCopyW(szName, COUNTOF(szName), W("<unknown type ref>"));
+ StringCchCopyW(szName, ARRAY_SIZE(szName), W("<unknown type ref>"));
}
}
else if (TypeFromToken(cr) == mdtTypeDef)
if (FAILED(i->GetTypeDefProps(cr, szName, 49, &cLen,
NULL, NULL)))
{
- StringCchCopyW(szName, COUNTOF(szName), W("<unknown type def>"));
+ StringCchCopyW(szName, ARRAY_SIZE(szName), W("<unknown type def>"));
}
}
else if (TypeFromToken(cr) == mdtTypeSpec)
PCCOR_SIGNATURE sig;
if (FAILED(i->GetTypeSpecFromToken(cr, &sig, &cSig)))
{
- StringCchCopyW(szName, COUNTOF(szName), W("<Invalid record>"));
+ StringCchCopyW(szName, ARRAY_SIZE(szName), W("<Invalid record>"));
}
else
{
}
else
{
- StringCchCopyW(szName, COUNTOF(szName), W("<unknown type token>"));
+ StringCchCopyW(szName, ARRAY_SIZE(szName), W("<unknown type token>"));
}
printf("%S::%S", szName, pMemberName);
static void appendStrNum(CQuickBytes *out, int num) {
char buff[16];
- sprintf_s(buff, COUNTOF(buff), "%d", num);
+ sprintf_s(buff, ARRAY_SIZE(buff), "%d", num);
appendStr(out, buff);
}
if(typ)
{
char sz[64];
- sprintf_s(sz,COUNTOF(sz),"/* UNKNOWN TYPE (0x%X)*/",typ);
+ sprintf_s(sz,ARRAY_SIZE(sz),"/* UNKNOWN TYPE (0x%X)*/",typ);
appendStr(out, sz);
}
break;
if (!pIMD->IsValidToken(tk))
{
char str[1024];
- sprintf_s(str,COUNTOF(str)," [ERROR: INVALID TOKEN 0x%8.8X] ",tk);
+ sprintf_s(str,ARRAY_SIZE(str)," [ERROR: INVALID TOKEN 0x%8.8X] ",tk);
appendStr(out, str);
return(asString(out));
}
if (((formatFlags & FormatAssembly) && FAILED(pIMD->GetTypeRefProps(tk, &tkEncloser, nameComplete, MAX_TYPE_NAME_LEN, &unused))))
{
char str[1024];
- sprintf_s(str, COUNTOF(str), " [ERROR: Invalid TypeRef record 0x%8.8X] ", tk);
+ sprintf_s(str, ARRAY_SIZE(str), " [ERROR: Invalid TypeRef record 0x%8.8X] ", tk);
appendStr(out, str);
return asString(out);
}
if (FAILED(pIMD->GetTypeDefProps(tk, nameComplete, MAX_TYPE_NAME_LEN, &unused, &typeDefFlags, &tkExtends)))
{
char str[1024];
- sprintf_s(str, COUNTOF(str), " [ERROR: Invalid TypeDef record 0x%8.8X] ", tk);
+ sprintf_s(str, ARRAY_SIZE(str), " [ERROR: Invalid TypeDef record 0x%8.8X] ", tk);
appendStr(out, str);
return asString(out);
}
if (FAILED(pIMD->GetTypeSpecFromToken(tk, &sig, &cSig)))
{
char str[128];
- sprintf_s(str, COUNTOF(str), " [ERROR: Invalid token 0x%8.8X] ", tk);
+ sprintf_s(str, ARRAY_SIZE(str), " [ERROR: Invalid token 0x%8.8X] ", tk);
appendStr(out, str);
}
else
default:
{
char str[128];
- sprintf_s(str,COUNTOF(str)," [ERROR: INVALID TOKEN TYPE 0x%8.8X] ",tk);
+ sprintf_s(str,ARRAY_SIZE(str)," [ERROR: INVALID TOKEN TYPE 0x%8.8X] ",tk);
appendStr(out, str);
}
}
else if (isString)
{
WCHAR str[32];
- obj.GetStringData(str, _countof(str));
+ obj.GetStringData(str, ARRAY_SIZE(str));
_snwprintf_s(buffer, size, _TRUNCATE, W("%s: \"%s\""), mt.GetName(), str);
}
public:
Exception(const char *format, va_list args)
{
- vsprintf_s(mMsg, _countof(mMsg), format, args);
+ vsprintf_s(mMsg, ARRAY_SIZE(mMsg), format, args);
va_end(args);
}
}
PPFormatFlags;
-char* asString(CQuickBytes *out);
+static char* asString(CQuickBytes *out);
PCCOR_SIGNATURE PrettyPrintType(
PCCOR_SIGNATURE typePtr, // type to convert,
DWORD formatFlags = FormatCSharp); // the format flags for the types
#endif
-
DumpObj (do) Threads (clrthreads)
DumpALC (dumpalc) ThreadState
DumpArray (da) IP2MD
-DumpAsync U
-DumpDelegate DumpStack
-DumpStackObjects (dso) EEStack
-DumpHeap ClrStack
-DumpVC GCInfo
-GCRoot EHInfo
-ObjSize BPMD (bpmd)
-FinalizeQueue COMState
-PrintException (pe)
+DumpDelegate U
+DumpStackObjects (dso) DumpStack
+DumpHeap EEStack
+DumpVC ClrStack
+GCRoot GCInfo
+ObjSize EHInfo
+FinalizeQueue BPMD (bpmd)
+PrintException (pe) COMState
TraverseHeap
Examining CLR data structures Diagnostic Utilities
\\
-COMMAND: dumpasync.
-!DumpAsync [-addr <Object Address>]
- [-mt <MethodTable address>]
- [-type <partial type name>]
- [-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>". It also optionally supports
-any other tasks.
-\\
-
COMMAND: dso.
COMMAND: dumpstackobjects.
!DumpStackObjects [-verify] [top stack [bottom stack]]
DumpObj (dumpobj) Threads (clrthreads)
DumpALC (dumpalc) ThreadState
DumpArray IP2MD (ip2md)
-DumpAsync (dumpasync) u (clru)
-DumpDelegate (dumpdelegate) DumpStack (dumpstack)
-DumpStackObjects (dso) EEStack (eestack)
-DumpHeap (dumpheap) ClrStack (clrstack)
-DumpVC GCInfo
-FinalizeQueue (finalizequeue) EHInfo
-GCRoot (gcroot) bpmd (bpmd)
-PrintException (pe)
+DumpDelegate (dumpdelegate) u (clru)
+DumpStackObjects (dso) DumpStack (dumpstack)
+DumpHeap (dumpheap) EEStack (eestack)
+DumpVC ClrStack (clrstack)
+FinalizeQueue (finalizequeue) GCInfo
+GCRoot (gcroot) EHInfo
+PrintException (pe) bpmd (bpmd)
Examining CLR data structures Diagnostic Utilities
----------------------------- -----------------------------
\\
-COMMAND: dumpasync.
-DumpAsync [-addr <Object Address>]
- [-mt <MethodTable address>]
- [-type <partial type name>]
- [-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>". It also optionally supports
-any other tasks.
-\\
-
COMMAND: dso.
COMMAND: dumpstackobjects.
DumpStackObjects [-verify] [top stack [bottom stack]]
{
if ( lf & 0x1 )
{
- strcat_s ( buff, _countof(buff), &(facilities[i].lfName[3]) );
- strcat_s ( buff, _countof(buff), "`" );
+ strcat_s ( buff, ARRAY_SIZE(buff), &(facilities[i].lfName[3]) );
+ strcat_s ( buff, ARRAY_SIZE(buff), "`" );
}
lf >>= 1;
}
int iArgCount = 0;
- strcpy_s(formatCopy, _countof(formatCopy), format);
+ strcpy_s(formatCopy, ARRAY_SIZE(formatCopy), format);
for(;;)
{
char c = *ptr++;
static WCHAR wszNameBuffer[1024]; // should be large enough
if (g_sos->GetMethodDescName(arg, 1024, wszNameBuffer, NULL) != S_OK)
{
- wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN METHODDESC"));
+ wcscpy_s(wszNameBuffer, ARRAY_SIZE(wszNameBuffer), W("UNKNOWN METHODDESC"));
}
wcscpy_s(buff, capacity_buff, wszNameBuffer);
TADDR taFmt = GetFormatAddr(inProcLog, latestMsg->formatOffset, bHasModuleTable);
hr = memCallBack->ReadVirtual(TO_CDADDR(taFmt), format, 256, 0);
if (hr != S_OK)
- strcpy_s(format, _countof(format), "Could not read address of format string");
+ strcpy_s(format, ARRAY_SIZE(format), "Could not read address of format string");
double deltaTime = ((double) (latestMsg->timeStamp - inProcLog.startTimeStamp)) / inProcLog.tickFrequency;
if (bDoGcHist)
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
}
if (symlines != 0 &&
- SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename))))
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, ARRAY_SIZE(filename))))
{
ExtOut("Source file: %S @ %d\n", filename, linenum);
}
{&DSFlag.end, COHEX}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
return Status;
// symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
{"/d", &dml, COBOOL, FALSE}
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&moduleExpr.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&moduleExpr.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&dwStartAddr, COHEX}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&flags.strObject, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&str_Object.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&str_Object.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&dwAddr, COHEX}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
xcode = EXCEPTION_COMPLUS;
goto Done;
}
- for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
+ for (size_t idx = 0; idx < ARRAY_SIZE(AsyncHResultValues); ++idx)
{
if (ehr == AsyncHResultValues[idx])
{
return TRUE;
HRESULT ehr = excData.HResult;
- for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
+ for (size_t idx = 0; idx < ARRAY_SIZE(AsyncHResultValues); ++idx)
{
if (ehr == AsyncHResultValues[idx])
{
// The unmodified IP is displayed (above by DumpMDInfoBuffer) which points after the exception in most
// cases. This means that the printed IP and the printed line number often will not map to one another
// and this is intentional.
- SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename), !bAsync || i > 0)))
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, ARRAY_SIZE(filename), !bAsync || i > 0)))
{
- swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
+ swprintf_s(wszLineBuffer, ARRAY_SIZE(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
}
else
{
- swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s\n"), so.String());
+ swprintf_s(wszLineBuffer, ARRAY_SIZE(wszLineBuffer), W(" %s\n"), so.String());
}
Length += _wcslen(wszLineBuffer);
{&strObject, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&p_Object, COHEX},
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&strObject, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&strObject, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&p_Object, COHEX}
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{"/d", &dml, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
break;
char domain[16];
- sprintf_s(domain, _countof(domain), "Domain %d", n+1);
+ sprintf_s(domain, ARRAY_SIZE(domain), "Domain %d", n+1);
IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
{&Filename.data, COSTRING},
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{"/d", &dml, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
return Status;
EnableDMLHolder dmlHolder(dml);
};
size_t nArgs = 0;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArgs))
sos::Throw<sos::Exception>("Failed to parse command line arguments.");
if (mStart == 0)
if (mVerify)
{
char buffer[1024];
- if (!itr.Verify(buffer, _countof(buffer)))
+ if (!itr.Verify(buffer, ARRAY_SIZE(buffer)))
{
ExtOut(buffer);
return false;
// Don't bother calculating the size of the string, just read the full 64 characters of the buffer. The null
// terminator we read will terminate the string.
- HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(_countof(tmp.str)-1), &fetched);
+ HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(ARRAY_SIZE(tmp.str)-1), &fetched);
if (SUCCEEDED(hr))
{
// Ensure we null terminate the string. Note that this will not overrun the buffer as we only
}
};
-/**********************************************************************\
-* Routine Description: *
-* *
-* 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 we now have an Action, try to follow through to the delegate'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);
-
- // In some cases, the delegate's target might be a ContinuationWrapper, in which case we want to unwrap that as well.
- if (_wcsncmp(contObj.GetTypeName(), W("System.Runtime.CompilerServices.AsyncMethodBuilderCore+ContinuationWrapper"), 74) == 0 &&
- (offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_continuation"))) != 0)
- {
- MOVE(*contAddr, contObj.GetAddress() + offset);
- if (sos::IsObject(*contAddr, false))
- {
- contObj = TO_TADDR(*contAddr);
- 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");
-}
-
-void ExtOutStateMachineFields(AsyncRecord& ar)
-{
- DacpMethodTableData mtabledata;
- DacpMethodTableFieldData vMethodTableFields;
- if (mtabledata.Request(g_sos, ar.StateMachineMT) == S_OK &&
- vMethodTableFields.Request(g_sos, ar.StateMachineMT) == S_OK &&
- vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
- {
- DisplayFields(ar.StateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)ar.StateMachineAddr, TRUE, ar.IsValueType);
- }
-}
-
-void FindStateMachineTypes(DWORD_PTR* corelibModule, mdTypeDef* stateMachineBox, mdTypeDef* debugStateMachineBox, mdTypeDef* task)
-{
- int numModule;
- ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(const_cast<LPSTR>("System.Private.CoreLib.dll"), &numModule);
- if (moduleList != NULL && numModule == 1)
- {
- *corelibModule = moduleList[0];
- GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1", stateMachineBox);
- GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1", debugStateMachineBox);
- GetInfoFromName(*corelibModule, "System.Threading.Tasks.Task", task);
- }
- else
- {
- *corelibModule = 0;
- *stateMachineBox = 0;
- *debugStateMachineBox = 0;
- }
-}
-
-DECLARE_API(DumpAsync)
-{
- INIT_API();
- MINIDUMP_NOT_SUPPORTED();
- if (!g_snapshot.Build())
- {
- ExtOut("Unable to build snapshot of the garbage collector state\n");
- return E_FAIL;
- }
-
- try
- {
- // Process command-line arguments.
- size_t nArg = 0;
- TADDR mt = NULL, addr = NULL;
- ArrayHolder<char> ansiType = NULL;
- ArrayHolder<WCHAR> type = NULL;
- BOOL dml = FALSE, includeCompleted = FALSE, includeStacks = FALSE, includeRoots = FALSE, includeAllTasks = FALSE, dumpFields = FALSE;
- CMDOption option[] =
- { // name, vptr, type, hasValue
- { "-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
- { "/d", &dml, COBOOL, FALSE }, // Debugger Markup Language
- };
- 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)
- {
- 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;
- if (!gcheap.AreGCStructuresValid())
- {
- DisplayInvalidStructuresMessage();
- }
-
- // Find the state machine types
- DWORD_PTR corelibModule;
- mdTypeDef stateMachineBoxMd, debugStateMachineBoxMd, taskMd;
- FindStateMachineTypes(&corelibModule, &stateMachineBoxMd, &debugStateMachineBoxMd, &taskMd);
-
- // 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 too small to be state machines or tasks, avoiding some compiler-generated caching data structures.
- if (itr->GetSize() <= 24)
- {
- continue;
- }
-
- // Match only async objects.
- if (includeAllTasks)
- {
- // 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(), corelibModule, taskMd))
- {
- continue;
- }
- }
- else
- {
- // Otherwise, we only care about AsyncStateMachineBox`1 as well as the DebugFinalizableAsyncStateMachineBox`1
- // that's used when certain ETW events are set.
- DacpMethodTableData mtdata;
- if (mtdata.Request(g_sos, TO_TADDR(itr->GetMT())) != S_OK ||
- mtdata.Module != corelibModule ||
- (mtdata.cl != stateMachineBoxMd && mtdata.cl != debugStateMachineBoxMd))
- {
- continue;
- }
- }
-
- // 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)
- {
- 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);
- }
- }
-
- // If we only want to include incomplete async objects, skip this one if it's completed.
- if (!includeCompleted && AsyncRecordIsCompleted(ar))
- {
- continue;
- }
-
- // 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))
- {
- sos::Object contObj = TO_TADDR(nextAddr);
- if (_wcsncmp(contObj.GetTypeName(), W("System.Collections.Generic.List`1"), 33) == 0)
- {
- // 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());
- }
- }
-
- // 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)
- {
- if (!hasTypeFilter || arIt->second.FilteredByOptions)
- {
- stats.Add((DWORD_PTR)arIt->second.MT, (DWORD)arIt->second.Size);
- }
- }
- 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)
- {
- for (std::vector<CLRDATA_ADDRESS>::iterator contIt = arIt->second.Continuations.begin(); contIt != arIt->second.Continuations.end(); ++contIt)
- {
- std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator found = asyncRecords.find(*contIt);
- if (found != asyncRecords.end())
- {
- if (found->second.IsTopLevel)
- {
- found->second.IsTopLevel = false;
- uniqueChains--;
- }
- }
- }
- }
-
- ExtOut("In %d chains.\n", uniqueChains);
- }
-
- // 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 details as a single line.
- sos::Object obj = TO_TADDR(arIt->second.Address);
- if (arIt->second.IsStateMachine)
- {
- // 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) ExtOutStateMachineFields(arIt->second);
- }
- 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 we gathered any continuations for this record, output the chains now.
- if (includeStacks && arIt->second.Continuations.size() > 0)
- {
- 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)
- {
- // 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());
- if (contAsyncRecord->second.IsStateMachine && dumpFields) ExtOutStateMachineFields(contAsyncRecord->second);
- }
- 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));
- }
- }
- }
-
- // 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(obj.GetAddress(), FALSE, FALSE);
- DecrementIndent();
- if (numRoots == 0 && !AsyncRecordIsCompleted(arIt->second))
- {
- ExtOut("Incomplete state machine or task with 0 roots.\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");
- }
- }
-
- return S_OK;
- }
- catch (const sos::Exception &e)
- {
- ExtOut("%s\n", e.what());
- return E_FAIL;
- }
-}
-
/**********************************************************************\
* Routine Description: *
* *
while (itr)
{
- if (itr.Verify(buffer, _countof(buffer)))
+ if (itr.Verify(buffer, ARRAY_SIZE(buffer)))
{
++itr;
}
{&taddrObj, COHEX}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&taddrArg, COHEX}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg) || nArg != 1)
{
ExtOut("Usage: !ListNearObj <obj_address>\n");
return Status;
{"/d", &dml, COBOOL, FALSE}
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
{&nbAsked, COSIZE_T}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{"-mt", &taddrMT, COHEX, TRUE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
if (state)
{
- for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
+ for (unsigned int i = 0; i < ARRAY_SIZE(ThreadStates); ++i)
if (state & ThreadStates[i].State)
{
ExtOut(" %s\n", ThreadStates[i].Name);
{"-managedexception", &bSwitchToManagedExceptionThread, COBOOL, FALSE},
{"/d", &dml, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
|| g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
{
- wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
+ wcscpy_s(wszNameBuffer, ARRAY_SIZE(wszNameBuffer), W("UNKNOWN"));
}
#ifndef FEATURE_PAL
- sprintf_s(buffer, _countof(buffer), "bp %p", SOS_PTR(addr));
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "bp %p", SOS_PTR(addr));
#else
- sprintf_s(buffer, _countof(buffer), "breakpoint set --address 0x%p", SOS_PTR(addr));
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "breakpoint set --address 0x%p", SOS_PTR(addr));
#endif
ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
{
CHAR buffer[100];
#ifndef FEATURE_PAL
- sprintf_s(buffer, _countof(buffer), "bp /1 %p", SOS_PTR(startAddr+catcherNativeOffset));
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "bp /1 %p", SOS_PTR(startAddr+catcherNativeOffset));
#else
- sprintf_s(buffer, _countof(buffer), "breakpoint set --one-shot --address 0x%p", SOS_PTR(startAddr+catcherNativeOffset));
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "breakpoint set --one-shot --address 0x%p", SOS_PTR(startAddr+catcherNativeOffset));
#endif
g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
}
if (SUCCEEDED(Status = g_sos->QueryInterface(__uuidof(ISOSDacInterface4), (void**) &psos4)))
{
- int count = _countof(arguments);
+ int count = ARRAY_SIZE(arguments);
int countNeeded = 0;
Status = psos4->GetClrNotification(arguments, count, &countNeeded);
{&Offset, COSIZE_T},
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
TypeName.data = new NOTHROW char[buffSize];
if (TypeName.data != NULL)
{
- int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
+ INDEBUG(int bytesWritten =) WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
_ASSERTE(bytesWritten == buffSize);
}
}
ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
SOS_PTR(MethodDescData.AddressOfNativeCodeSlot));
- sprintf_s(buffer, _countof(buffer),
+ sprintf_s(buffer, ARRAY_SIZE(buffer),
#ifdef _TARGET_WIN64_
"ba w8"
#else
{"/d", &dml, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return E_FAIL;
}
TO_CDADDR(vPortableTpHcLogArray.ArrayDataPtr + (index * sizeof(HillClimbingLogEntry)));
INT32 i32Value = 0;
float f32Value = 0;
- int fieldOffset = 0;
if (FAILED(Status = MOVE(i32Value, entryPtr + portableTpHcLogEntry_tickCountOffset)))
{
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg) || (0 == nArg))
{
return Status;
}
{&taStartAddr, COHEX},
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg) || (0 == nArg))
{
return Status;
}
{ // vptr, type
{&dwStartAddr, COHEX},
};
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg) || (nArg < 1))
{
return Status;
}
{&sFileName.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{"--reset", &bReset, COBOOL, FALSE},
{"-r", &bReset, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
return S_OK;
}
Target::DisplayStatus();
- DisplaySymbolStore();
}
return Status;
}
end = _wcschr (pt, L'\0');
if (end == NULL) {
char format[20];
- sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
+ sprintf_s (format, ARRAY_SIZE(format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
ExtOut(format, pt);
break;
}
};
size_t nArg;
- if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args,option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&root, COHEX},
{&target, COHEX},
};
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{ // vptr, type
{&obj, COHEX}
};
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{ // vptr, type
{&taddrObj, COHEX}
};
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{ // vptr, type
{&taObj, COHEX}
};
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{"/d", &mDML, COBOOL, FALSE},
};
- if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
+ if (!GetCMDOption(args,option,ARRAY_SIZE(option),NULL,0,NULL))
sos::Throw<sos::Exception>("Failed to parse command line arguments.");
if (type != NULL)
HRESULT hr = S_OK;
do
{
- if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
+ if (FAILED(hr = handles->Next(ARRAY_SIZE(data), data, &fetched)))
{
ExtOut("Error %x while walking the handle table.\n", hr);
break;
}
WalkHandles(data, fetched);
- } while (_countof(data) == fetched);
+ } while (ARRAY_SIZE(data) == fetched);
}
void WalkHandles(SOSHandleData data[], unsigned int count)
{&PReg.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
}
}
- sprintf_s(buffer,_countof (buffer),
+ sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=0",
preg);
Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
codeType = 16;
}
- sprintf_s(buffer,_countof (buffer),
+ sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=%x",
preg, codeType);
Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
{&PReg.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
}
}
- sprintf_s(buffer,_countof (buffer),
+ sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=0",
preg);
Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
if (fCreate1 || fCreate2)
{
- sprintf_s(buffer,_countof (buffer),
+ sprintf_s(buffer, ARRAY_SIZE(buffer),
"sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
fCreate1 ? "-c" : "-c2",
fDerived ? "-derived" : "",
if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
(fDerived && IsDerivedFrom(taMT, typeNameWide)))
{
- sprintf_s(buffer,_countof (buffer),
+ sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=1",
preg);
Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
{&str_Object.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{"/d", &dml, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
ExtOut("\nLOCALS:\n");
for (ULONG i=0; i < cLocals; i++)
{
- ULONG paramNameLen = 0;
WCHAR paramName[mdNameLen] = W("\0");
ToRelease<ICorDebugValue> pValue;
WCHAR wszModuleName[MAX_LONGPATH];
ULONG32 cchModuleNameActual;
- IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
+ IfFailRet(pModule->GetName(ARRAY_SIZE(wszModuleName), &cchModuleNameActual, wszModuleName));
ToRelease<IUnknown> pMDUnknown;
ToRelease<IMetaDataImport> pMD;
if (ref.HasRegisterInformation)
{
WCHAR reg[32];
- HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
+ HRESULT hr = g_sos->GetRegisterName(ref.Register, ARRAY_SIZE(reg), reg, NULL);
if (SUCCEEDED(hr))
res = reg;
else
if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
{
WCHAR type[128];
- sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
+ sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), ARRAY_SIZE(type), type);
res += WString(W(" - ")) + type;
}
out.WriteColumn(0, frame->StackOffset);
out.WriteColumn(1, NativePtr(ip));
- HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, ARRAY_SIZE(symbol), NULL, &displacement);
if (SUCCEEDED(hr) && symbol[0] != '\0')
{
String frameOutput;
if (!bSuppressLines)
{
ULONG line;
- hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
+ hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, ARRAY_SIZE(filename), NULL, NULL);
if (SUCCEEDED(hr))
{
frameOutput += " at ";
DECLARE_API(Watch)
{
INIT_API_NOEE();
- BOOL bExpression = FALSE;
StringHolder addExpression;
StringHolder aExpression;
StringHolder saveName;
{ // vptr, type
{&expression.data, COSTRING}
};
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&cvariableName.data, COSTRING},
{&frameToDumpVariablesFor, COSIZE_T},
};
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&Location.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{"-off", &bOff, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
UINT Length = 0;
while(1)
{
- if (_wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
+ if (_wcsncmp(pwszPointer, PSZSEP, ARRAY_SIZE(PSZSEP)-1) != 0)
{
delete [] pwszBuf;
return bRet;
WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
// Note that we don't add a newline because we have this embedded in wszLineBuffer
- swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %p %p %s"), SOS_PTR(-1), SOS_PTR(-1), pwszPointer);
+ swprintf_s(wszLineBuffer, ARRAY_SIZE(wszLineBuffer), W(" %p %p %s"), SOS_PTR(-1), SOS_PTR(-1), pwszPointer);
Length += (UINT)_wcslen(wszLineBuffer);
if (wszBuffer)
{"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL,0,NULL))
{
return Status;
}
{&filePath.data, COSTRING},
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return E_FAIL;
}
{&onOff.data, COSTRING},
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return E_FAIL;
}
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
{&hostRuntimeDirectory.data, COSTRING},
};
size_t narg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &narg))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &narg))
{
return E_FAIL;
}
#endif // FEATURE_PAL
-//
-// Sets the symbol server path.
-//
-DECLARE_API(SetSymbolServer)
-{
- INIT_API_EXT();
-
- StringHolder symbolCache;
- StringHolder searchDirectory;
- StringHolder windowsSymbolPath;
- StringHolder authToken;
- size_t timeoutInMinutes = 0;
- std::string resolvedSearchDirectory;
- BOOL disable = FALSE;
- BOOL loadNative = FALSE;
- BOOL msdl = FALSE;
- BOOL symweb = FALSE;
- CMDOption option[] =
- { // name, vptr, type, hasValue
- {"-disable", &disable, COBOOL, FALSE},
- {"-cache", &symbolCache.data, COSTRING, TRUE},
- {"-directory", &searchDirectory.data, COSTRING, TRUE},
- {"-pat", &authToken.data, COSTRING, TRUE},
- {"-timeout", &timeoutInMinutes, COSIZE_T, TRUE},
- {"-ms", &msdl, COBOOL, FALSE},
-#ifdef FEATURE_PAL
- {"-loadsymbols", &loadNative, COBOOL, FALSE},
- {"-sympath", &windowsSymbolPath.data, COSTRING, TRUE},
-#else
- {"-mi", &symweb, COBOOL, FALSE},
-#endif
- };
- StringHolder symbolServer;
- CMDValue arg[] =
- {
- {&symbolServer.data, COSTRING},
- };
- size_t narg;
- if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &narg))
- {
- return E_FAIL;
- }
-
- if (msdl && symweb)
- {
- ExtErr("Cannot have both -ms and -mi options\n");
- return E_FAIL;
- }
-
- if ((msdl || symweb) && symbolServer.data != nullptr)
- {
- ExtErr("Cannot have -ms or -mi option and a symbol server path\n");
- return E_FAIL;
- }
-
- if (disable) {
- DisableSymbolStore();
- }
-
- if (searchDirectory.data != nullptr) {
- if (!GetAbsolutePath(searchDirectory.data, resolvedSearchDirectory))
- {
- ExtErr("Invalid runtime directory %s\n", resolvedSearchDirectory.c_str());
- return E_FAIL;
- }
- }
-
- if (msdl || symweb || symbolServer.data != nullptr || symbolCache.data != nullptr || !resolvedSearchDirectory.empty() || windowsSymbolPath.data != nullptr)
- {
- Status = InitializeSymbolStore(msdl, symweb, symbolServer.data, authToken.data, (int)timeoutInMinutes, symbolCache.data, (resolvedSearchDirectory.empty() ? nullptr : resolvedSearchDirectory.c_str()), windowsSymbolPath.data);
- if (FAILED(Status))
- {
- return Status;
- }
- if (msdl)
- {
- ExtOut("Added Microsoft public symbol server\n");
- }
- if (symweb)
- {
- ExtOut("Added internal symweb symbol server\n");
- }
- if (symbolServer.data != nullptr)
- {
- ExtOut("Added symbol server: %s\n", symbolServer.data);
- }
- if (symbolCache.data != nullptr)
- {
- ExtOut("Added symbol cache path: %s\n", symbolCache.data);
- }
- if (!resolvedSearchDirectory.empty())
- {
- ExtOut("Added symbol directory path: %s\n", resolvedSearchDirectory.c_str());
- }
- if (windowsSymbolPath.data != nullptr)
- {
- ExtOut("Added Windows symbol path: %s\n", windowsSymbolPath.data);
- }
- }
-#ifdef FEATURE_PAL
- else if (loadNative)
- {
- Status = LoadNativeSymbols();
- if (FAILED(Status))
- {
- ExtErr("Symbol server not set\n");
- }
- }
-#endif
- else
- {
- DisplaySymbolStore();
- }
-
- return Status;
-}
-
//
// Sets the runtime module path
//
{&runtimeModulePath.data, COSTRING},
};
size_t narg;
- if (!GetCMDOption(args, nullptr, 0, arg, _countof(arg), &narg))
+ if (!GetCMDOption(args, nullptr, 0, arg, ARRAY_SIZE(arg), &narg))
{
return E_FAIL;
}
{"-netfx", &bNetFx, COBOOL, FALSE},
{"-netcore", &bNetCore, COBOOL, FALSE},
};
- if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
{
return Status;
}
return S_OK;
}
+//
+// Sets the symbol server path.
+//
+DECLARE_API(SetSymbolServer)
+{
+ INIT_API_EXT();
+ return ExecuteCommand("setsymbolserver", args);
+}
+
//
// Dumps the managed assemblies
//
return ExecuteCommand("clrmodules", args);
}
+//
+// Dumps async stacks
+//
+DECLARE_API(DumpAsync)
+{
+ INIT_API_EXT();
+ return ExecuteCommand("dumpasync", args);
+}
+
//
// Enables and disables managed extension logging
//
}
char lpFilename[MAX_LONGPATH + 12]; // + 12 to make enough room for strcat function.
- strcpy_s(lpFilename, _countof(lpFilename), szSOSModulePath);
- strcat_s(lpFilename, _countof(lpFilename), "sosdocsunix.txt");
+ strcpy_s(lpFilename, ARRAY_SIZE(lpFilename), szSOSModulePath);
+ strcat_s(lpFilename, ARRAY_SIZE(lpFilename), "sosdocsunix.txt");
HANDLE hSosDocFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hSosDocFile == INVALID_HANDLE_VALUE) {
// Find our line in the text file
char searchString[MAX_LONGPATH];
- sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
+ sprintf_s(searchString, ARRAY_SIZE(searchString), "COMMAND: %s.", pszCmdName);
LPSTR pStart = strstr(pText, searchString);
LPSTR pEnd = NULL;
{&commandName.data, COSTRING}
};
size_t nArg;
- if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg))
{
return Status;
}
#ifndef __strike_h__
#define __strike_h__
-#ifndef _countof
-#define _countof(x) (sizeof(x)/sizeof(x[0]))
-#endif
+#include <minipal/utils.h>
#if defined(_MSC_VER)
#pragma warning(disable:4245) // signed/unsigned mismatch
OnUnloadTask::Run();
}
-/**********************************************************************\
- * Setup and initialize the symbol server support.
-\**********************************************************************/
-HRESULT InitializeSymbolStore(
- BOOL msdl,
- BOOL symweb,
- const char* symbolServer,
- const char* authToken,
- int timeoutInMinutes,
- const char* cacheDirectory,
- const char* searchDirectory,
- const char* windowsSymbolPath)
-{
- HRESULT Status = S_OK;
- ISymbolService* symbolService = GetSymbolService();
- if (symbolService == nullptr)
- {
- return E_NOINTERFACE;
- }
- if (!symbolService->InitializeSymbolStore(
- msdl,
- symweb,
- symbolServer,
- authToken,
- timeoutInMinutes,
- cacheDirectory,
- searchDirectory))
- {
- ExtErr("Error initializing symbol server support\n");
- return E_FAIL;
- }
- if (windowsSymbolPath != nullptr)
- {
- if (!symbolService->ParseSymbolPath(windowsSymbolPath))
- {
- ExtErr("Error parsing symbol path %s\n", windowsSymbolPath);
- return E_FAIL;
- }
- }
- return S_OK;
-}
-
-#ifdef FEATURE_PAL
-
-//
-// Symbol downloader callback
-//
-static void SymbolFileCallback(void* param, const char* moduleFileName, const char* symbolFilePath)
-{
- if (strcmp(moduleFileName, GetRuntimeDllName(IRuntime::Core)) == 0) {
- return;
- }
- if (strcmp(moduleFileName, NETCORE_DAC_DLL_NAME_A) == 0) {
- return;
- }
- if (strcmp(moduleFileName, NET_DBI_DLL_NAME_A) == 0) {
- return;
- }
- g_ExtServices2->AddModuleSymbol(param, symbolFilePath);
-}
-
-//
-// Enumerate native module callback
-//
-static void LoadNativeSymbolsCallback(void* param, const char* moduleFilePath, ULONG64 moduleAddress, int moduleSize)
-{
- GetSymbolService()->LoadNativeSymbols(SymbolFileCallback, param, IRuntime::Core, moduleFilePath, moduleAddress, moduleSize);
-}
-
-/**********************************************************************\
- * Enumerate the native modules and attempt to download the symbols
- * for them. Depends on the lldb callback to enumerate modules. Not
- * necessary on dbgeng because it already downloads native symbols.
-\**********************************************************************/
-HRESULT LoadNativeSymbols(bool runtimeOnly)
-{
- ISymbolService* symbolService = GetSymbolService();
- if (symbolService != nullptr)
- {
- if (symbolService->IsSymbolStoreEnabled())
- {
- return g_ExtServices2->LoadNativeSymbols(runtimeOnly, LoadNativeSymbolsCallback);
- }
- }
- return E_FAIL;
-}
-
-#endif
-
-/**********************************************************************\
- * Displays the symbol server and cache status.
-\**********************************************************************/
-void DisplaySymbolStore()
-{
- ISymbolService* symbolService = GetSymbolService();
- if (symbolService != nullptr)
- {
- symbolService->DisplaySymbolStore([] (const char* message) {
- ExtOut(message);
- ExtOut("\n");
- });
- }
-}
-
-/**********************************************************************\
- * Turns off the symbol server support.
-\**********************************************************************/
-void DisableSymbolStore()
-{
- ISymbolService* symbolService = GetSymbolService();
- if (symbolService != nullptr)
- {
- symbolService->DisableSymbolStore();
- }
-}
-
/**********************************************************************\
* Returns the metadata from a local or downloaded assembly
\**********************************************************************/
extern HMODULE g_hInstance;
-#ifdef FEATURE_PAL
-extern HRESULT LoadNativeSymbols(bool runtimeOnly = false);
-#endif
-
-extern HRESULT InitializeSymbolStore(
- BOOL msdl,
- BOOL symweb,
- const char* symbolServer,
- const char* authToken,
- int timeoutInMinutes,
- const char* cacheDirectory,
- const char* searchDirectory,
- const char* windowsSymbolPath);
-
-extern void DisplaySymbolStore();
-extern void DisableSymbolStore();
-
extern HRESULT GetMetadataLocator(
LPCWSTR imagePath,
ULONG32 imageTimestamp,
else if (hr == S_FALSE && dwAddr)
return (DWORD_PTR)dwAddr;
- strcpy_s (name, _countof(name), str);
+ strcpy_s (name, ARRAY_SIZE(name), str);
char *ptr;
if ((ptr = strstr (name, "__")) != NULL)
{
return "MVAR";
break;
default:
- if ((type >= _countof(CorElementTypeName)) || (CorElementTypeName[type] == NULL))
+ if ((type >= ARRAY_SIZE(CorElementTypeName)) || (CorElementTypeName[type] == NULL))
{
return "";
}
const char * ElementTypeNamespace(unsigned type)
{
- if ((type >= _countof(CorElementTypeName)) || (CorElementTypeNamespace[type] == NULL))
+ if ((type >= ARRAY_SIZE(CorElementTypeName)) || (CorElementTypeNamespace[type] == NULL))
{
return "";
}
// If ET type from signature is different from fielddesc, then the signature one is more descriptive.
// For example, E_T_STRING in field desc will be E_T_CLASS. In minidump's case, we won't have
// the method table for it.
- ComposeName_s(vFieldDesc.Type != vFieldDesc.sigType ? vFieldDesc.sigType : vFieldDesc.Type, ElementName, sizeof(ElementName)/sizeof(ElementName[0]));
+ ComposeName_s(vFieldDesc.Type != vFieldDesc.sigType ? vFieldDesc.sigType : vFieldDesc.Type, ElementName, ARRAY_SIZE(ElementName));
ExtOut("%20.20s ", ElementName);
}
}
BOOL bFailed = FALSE;
if (g_sos->GetMethodDescName(pMethodDescData->MethodDescPtr, 1024, wszNameBuffer, NULL) != S_OK)
{
- wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
+ wcscpy_s(wszNameBuffer, ARRAY_SIZE(wszNameBuffer), W("UNKNOWN"));
bFailed = TRUE;
}
TO_CDADDR(dwMethodDescAddr),
dwRequestedIP,
&MethodDescData,
- _countof(revertedRejitData),
+ ARRAY_SIZE(revertedRejitData),
revertedRejitData,
&cNeededRevertedRejitData) != S_OK)
{
_ASSERTE(pFileInfo);
WCHAR wszFullPath[MAX_LONGPATH];
- DWORD cchFullPath = GetModuleFileNameW(g_hInstance, wszFullPath, _countof(wszFullPath));
+ DWORD cchFullPath = GetModuleFileNameW(g_hInstance, wszFullPath, ARRAY_SIZE(wszFullPath));
DWORD dwHandle = 0;
DWORD infoSize = GetFileVersionInfoSizeW(wszFullPath, &dwHandle);
toRead = count;
ULONG bytesRead;
- wcsncpy_s(buffer,_countof(buffer),(LPWSTR) dwAddr, toRead);
+ wcsncpy_s(buffer,ARRAY_SIZE(buffer),(LPWSTR) dwAddr, toRead);
bytesRead = toRead*sizeof(WCHAR);
DWORD wcharsRead = bytesRead/2;
buffer[wcharsRead] = L'\0';
ExtOut ("Invalid option %s\n", ptr);
return FALSE;
}
- strncpy_s (buffer,_countof(buffer), ptr, end-ptr);
+ strncpy_s (buffer,ARRAY_SIZE(buffer), ptr, end-ptr);
size_t n;
for (n = 0; n < nOption; n ++)
"<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
"<exec cmd=\"!DumpIL /i %s\">%s</exec>", // DML_IL
"<exec cmd=\"!DumpRCW -cw /d %s\">%s</exec>", // DML_ComWrapperRCW
"<exec cmd=\"!DumpCCW -cw /d %s\">%s</exec>", // DML_ComWrapperCCW
char hex2[POINTERSIZE_BYTES*2 + 1];
char* d = hex1;
char* a = hex1;
- GetHex(addr, hex1, _countof(hex1), fill);
+ GetHex(addr, hex1, ARRAY_SIZE(hex1), fill);
if (disp != addr)
{
- GetHex(disp, hex2, _countof(hex2), fill);
+ GetHex(disp, hex2, ARRAY_SIZE(hex2), fill);
d = hex2;
}
if (IsDMLEnabled())
{
char hex[POINTERSIZE_BYTES*2 + 1];
- GetHex(addr, hex, _countof(hex), fill);
+ GetHex(addr, hex, ARRAY_SIZE(hex), fill);
sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hex, len, hex);
}
else
char* d = hexaddr1;
char* a = hexaddr1;
- GetHex(addr, hexaddr1, _countof(hexaddr1), fill);
+ GetHex(addr, hexaddr1, ARRAY_SIZE(hexaddr1), fill);
if (disp != addr)
{
- GetHex(disp, hexaddr2, _countof(hexaddr2), fill);
+ GetHex(disp, hexaddr2, ARRAY_SIZE(hexaddr2), fill);
d = hexaddr2;
}
- GetHex(mt, hexmt, _countof(hexmt), fill);
+ GetHex(mt, hexmt, ARRAY_SIZE(hexmt), fill);
sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hexmt, a, d);
}
CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type)
{
WCHAR indexString[24];
- swprintf_s(indexString, _countof(indexString), W("[%d]"), indexInArray);
+ swprintf_s(indexString, ARRAY_SIZE(indexString), W("[%d]"), indexInArray);
return BuildManagedVarValue(expansionName, frame, indexString, type);
}
HRESULT Status;
CLRDATA_IL_ADDRESS_MAP MapLocal[16];
CLRDATA_IL_ADDRESS_MAP* Map = MapLocal;
- ULONG32 MapCount = _countof(MapLocal);
+ ULONG32 MapCount = ARRAY_SIZE(MapLocal);
ULONG32 MapNeeded;
ULONG32 HighestOffset;
if (count == 0)
{
- count = _countof(WhiteSpace);
+ count = ARRAY_SIZE(WhiteSpace);
for (int i = 0; i < count-1; ++i)
WhiteSpace[i] = ' ';
WhiteSpace[count-1] = 0;
if (SUCCEEDED(g_ExtSymbols->GetModuleNames(Index, moduleBase, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL)))
{
- MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, g_mdName, _countof(g_mdName));
+ MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, g_mdName, ARRAY_SIZE(g_mdName));
methodOutput += g_mdName;
methodOutput += W("!");
}
_ASSERTE(pThread3 != NULL);
return pThread3->GetActiveInternalFrames(
- _countof(m_rgpInternalFrame2),
+ ARRAY_SIZE(m_rgpInternalFrame2),
&m_cInternalFramesActual,
&(m_rgpInternalFrame2[0]));
}
#include "runtimeimpl.h"
#include "symbols.h"
-#ifndef COUNTOF
-#define COUNTOF(a) (sizeof(a) / sizeof(*a))
-#endif
-
typedef LPCSTR LPCUTF8;
typedef LPSTR LPUTF8;
char buffer[64];
if (mFormat == Formats::Default || mFormat == Formats::Pointer)
{
- sprintf_s(buffer, _countof(buffer), "%p", (int *)(SIZE_T)mValue);
- ConvertToLower(buffer, _countof(buffer));
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "%p", (int *)(SIZE_T)mValue);
+ ConvertToLower(buffer, ARRAY_SIZE(buffer));
}
else
{
else if (mFormat == Formats::Decimal)
format = "%d";
- sprintf_s(buffer, _countof(buffer), format, (__int32)mValue);
- ConvertToLower(buffer, _countof(buffer));
+ sprintf_s(buffer, ARRAY_SIZE(buffer), format, (__int32)mValue);
+ ConvertToLower(buffer, ARRAY_SIZE(buffer));
}
return buffer;
static void BuildDMLCol(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType, bool leftAlign, int width)
{
char hex[64];
- int count = GetHex(value, hex, _countof(hex), format != Formats::Hex);
+ int count = GetHex(value, hex, ARRAY_SIZE(hex), format != Formats::Hex);
int i = 0;
if (!leftAlign)
va_list list;
va_start(list, fmt);
- vsprintf_s(result, _countof(result), fmt, list);
+ vsprintf_s(result, ARRAY_SIZE(result), fmt, list);
va_end(list);
WriteColumn(col, result);
va_list list;
va_start(list, fmt);
- vswprintf_s(result, _countof(result), fmt, list);
+ vswprintf_s(result, ARRAY_SIZE(result), fmt, list);
va_end(list);
WriteColumn(col, result);
{
#ifdef _DEBUG
char buffer[1024];
- sprintf_s(buffer, _countof(buffer), "Cache (%s): %d reads (%2.1f%% hits), %d misses (%2.1f%%), %d misaligned (%2.1f%%).\n",
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "Cache (%s): %d reads (%2.1f%% hits), %d misses (%2.1f%%), %d misaligned (%2.1f%%).\n",
func, mReads, 100*(mReads-mMisses)/(float)(mReads+mMisaligned), mMisses,
100*mMisses/(float)(mReads+mMisaligned), mMisaligned, 100*mMisaligned/(float)(mReads+mMisaligned));
OutputDebugStringA(buffer);
#define PRINTF_FORMAT_HEAD "%-7s %*s %*s %*s %*s %*s\n"
#define PRINTF_FORMAT "%-7s %*sK %*sK %*sK %*s %*sK\n"
-#define CCH_ULONGLONG_COMMAS _countof("18,446,744,073,709,551,616")
+#define CCH_ULONGLONG_COMMAS ARRAY_SIZE("18,446,744,073,709,551,616")
#define CCH_ULONGLONG_MINIMUM_COMMAS (CCH_ULONGLONG_COMMAS - 3)
#define CCH_ULONGLONG_BLOCKCOUNT_COMMAS sizeof("1,000,000")
}
};
-#define NUM_PROTECT_MASKS (sizeof(ProtectMasks) / sizeof(ProtectMasks[0]))
-
//
// Private functions.
//
-
PSTR
ULongLongToString(
IN ULONGLONG Value,
__out_ecount (CCH_ULONGLONG_COMMAS) OUT PSTR Buffer
)
{
-
PSTR p1;
PSTR p2;
CHAR ch;
Buffer[0] = '\0';
for( i = 0, mask = &ProtectMasks[0] ;
- (i < NUM_PROTECT_MASKS) && (Protect != 0) ;
+ (i < ARRAY_SIZE(ProtectMasks)) && (Protect != 0) ;
i++, mask++ ) {
if( mask->Bit & Protect ) {
Protect &= ~mask->Bit;
break;
default:
- sprintf_s(invalidStr,_countof(invalidStr), "%08lx", State );
+ sprintf_s(invalidStr,ARRAY_SIZE(invalidStr), "%08lx", State );
result = invalidStr;
break;
}
break;
default:
- sprintf_s(invalidStr,_countof(invalidStr), "%08lx", Type );
+ sprintf_s(invalidStr,ARRAY_SIZE(invalidStr), "%08lx", Type );
result = invalidStr;
break;
}
SOS_PTR(memInfo.BaseAddress),
SOS_PTR(((ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize - 1)),
SOS_PTR(memInfo.RegionSize),
- VmProtectToString( memInfo.AllocationProtect, aprotectStr, _countof(aprotectStr) ),
- VmProtectToString( memInfo.Protect, protectStr, _countof(protectStr) ),
- VmStateToString( memInfo.State, stateStr, _countof(stateStr) ),
- VmTypeToString( memInfo.Type, typeStr , _countof(typeStr))
+ VmProtectToString( memInfo.AllocationProtect, aprotectStr, ARRAY_SIZE(aprotectStr) ),
+ VmProtectToString( memInfo.Protect, protectStr, ARRAY_SIZE(protectStr) ),
+ VmStateToString( memInfo.State, stateStr, ARRAY_SIZE(stateStr) ),
+ VmTypeToString( memInfo.Type, typeStr , ARRAY_SIZE(typeStr))
);
//
#define __CONFIG_H__
#cmakedefine01 HAVE_DIRENT_D_TYPE
+#cmakedefine01 HAVE_GETAUXVAL
#endif // __CONFIG_H__
include(CheckStructHasMember)
+include(CheckIncludeFiles)
check_struct_has_member ("struct dirent" d_type dirent.h HAVE_DIRENT_D_TYPE)
+check_include_files("sys/auxv.h;asm/hwcap.h" HAVE_AUXV_HWCAP_H)
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
#include <string>
#include <vector>
-#if defined(__APPLE__)
-#include <mach-o/dyld.h>
-#endif
-
-#if defined(__FreeBSD__)
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#endif
-
#include "palclr.h"
#include "arrayholder.h"
#include "coreclrhost.h"
#include "extensions.h"
-#ifndef _countof
-#define _countof(x) (sizeof(x)/sizeof(x[0]))
-#endif
+#include <minipal/getexepath.h>
+#include <minipal/utils.h>
#ifndef IfFailRet
#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
"/usr/share/dotnet",
#endif
};
-#if defined(TARGET_LINUX)
- constexpr char SymlinkEntrypointExecutable[] = "/proc/self/exe";
-#elif !defined(TARGET_OSX)
- constexpr char SymlinkEntrypointExecutable[] = "/proc/curproc/exe";
-#endif
#endif
};
FileFind find;
if (find.Open(hostRuntimeDirectory.c_str()))
{
- int highestRevision = 0;
+ uint32_t highestRevision = 0;
do
{
if (find.IsDirectory())
{
- int major = 0;
- int minor = 0;
- int revision = 0;
+ uint32_t major = 0;
+ uint32_t minor = 0;
+ uint32_t revision = 0;
if (sscanf(find.FileName(), "%d.%d.%d", &major, &minor, &revision) == 3)
{
if (major == runtimeVersion.Major && minor == runtimeVersion.Minor)
return false;
}
-#ifdef HOST_WINDOWS
-
-static bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
-{
- ArrayHolder<char> hostPath = new char[MAX_LONGPATH+1];
- if (::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH) == 0)
- {
- return false;
- }
- entrypointExecutable.clear();
- entrypointExecutable.append(hostPath);
- return true;
-}
-
-#else // HOST_WINDOWS
-
-static bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
-{
- bool result = false;
-
- entrypointExecutable.clear();
-
- // Get path to the executable for the current process using
- // platform specific means.
-#if defined(TARGET_OSX)
- // On Mac, we ask the OS for the absolute path to the entrypoint executable
- uint32_t lenActualPath = 0;
- if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
- {
- // OSX has placed the actual path length in lenActualPath,
- // so re-attempt the operation
- std::string resizedPath(lenActualPath, '\0');
- char *pResizedPath = const_cast<char *>(resizedPath.c_str());
- if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
- {
- entrypointExecutable.assign(pResizedPath);
- result = true;
- }
- }
-#elif defined (TARGET_FREEBSD)
- static const int name[] = {
- CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1
- };
- char path[PATH_MAX];
- size_t len;
-
- len = sizeof(path);
- if (sysctl(name, 4, path, &len, nullptr, 0) == 0)
- {
- entrypointExecutable.assign(path);
- result = true;
- }
- else
- {
- // ENOMEM
- result = false;
- }
-#elif defined(TARGET_NETBSD) && defined(KERN_PROC_PATHNAME)
- static const int name[] = {
- CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
- };
- char path[MAXPATHLEN];
- size_t len;
-
- len = sizeof(path);
- if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1)
- {
- entrypointExecutable.assign(path);
- result = true;
- }
- else
- {
- result = false;
- }
-#else
- // On other OSs, return the symlink that will be resolved by GetAbsolutePath
- // to fetch the entrypoint EXE absolute path, inclusive of filename.
- result = GetAbsolutePath(RuntimeHostingConstants::SymlinkEntrypointExecutable, entrypointExecutable);
-#endif
-
- return result;
-}
+#ifdef HOST_UNIX
static HRESULT ProbeInstallationMarkerFile(const char* const markerName, std::string &hostRuntimeDirectory)
{
return hostRuntimeDirectory.empty() ? S_FALSE : S_OK;
}
-#endif // HOST_WINDOWS
+#endif // HOST_UNIX
static HRESULT ProbeInstallationDir(const char* const installPath, std::string& hostRuntimeDirectory)
{
};
#if defined(HOST_UNIX)
- for (int i = 0; i < _countof(RuntimeHostingConstants::UnixInstallPaths); i++)
+ for (int i = 0; i < ARRAY_SIZE(RuntimeHostingConstants::UnixInstallPaths); i++)
{
strategyList.push_back({ ProbeInstallationDir, RuntimeHostingConstants::UnixInstallPaths[i] });
}
"UseLatestBehaviorWhenTFMNotSpecified"
};
- std::string entryPointExecutablePath;
- if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
+ char* exePath = minipal_getexepath();
+ if (!exePath)
{
TraceError("Could not get full path to current executable");
return E_FAIL;
void* hostHandle;
unsigned int domainId;
- hr = initializeCoreCLR(entryPointExecutablePath.c_str(), "sos",
- sizeof(propertyKeys) / sizeof(propertyKeys[0]), propertyKeys, propertyValues, &hostHandle, &domainId);
+ hr = initializeCoreCLR(exePath, "sos", ARRAY_SIZE(propertyKeys), propertyKeys, propertyValues, &hostHandle, &domainId);
+ free(exePath);
if (FAILED(hr))
{
TraceError("Error: Fail to initialize coreclr %08x\n", hr);
PULONG64 offset) = 0;
virtual ULONG STDMETHODCALLTYPE GetOutputWidth() = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE SupportsDml(PULONG supported) = 0;
+
+ virtual void STDMETHODCALLTYPE OutputDmlString(
+ ULONG mask,
+ PCSTR message) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE AddModuleSymbol(
+ void* param,
+ const char* symbolFileName) = 0;
};
#ifdef __cplusplus
ISymbolService : public IUnknown
{
public:
- virtual bool STDMETHODCALLTYPE IsSymbolStoreEnabled() = 0;
-
- virtual bool STDMETHODCALLTYPE InitializeSymbolStore(
- bool msdl,
- bool symweb,
- const char* symbolServerPath,
- const char* authToken,
- int timeoutInMinutes,
- const char* symbolCacehPath,
- const char* symbolDirectoryPath) = 0;
-
virtual bool STDMETHODCALLTYPE ParseSymbolPath(
const char* windowsSymbolPath) = 0;
- virtual void STDMETHODCALLTYPE DisplaySymbolStore(
- WriteLineDelegate writeline) = 0;
-
- virtual void STDMETHODCALLTYPE DisableSymbolStore() = 0;
-
- virtual void STDMETHODCALLTYPE LoadNativeSymbols(
- SymbolFileCallbackDelegate,
- void*,
- int,
- const char*,
- ULONG64,
- int) = 0;
-
- virtual void STDMETHODCALLTYPE LoadNativeSymbolsFromIndex(
- SymbolFileCallbackDelegate,
- void*,
- int,
- const char*,
- BOOL,
- int,
- const unsigned char* moduleIndex) = 0;
-
virtual PVOID STDMETHODCALLTYPE LoadSymbolsForModule(
LPCWSTR,
BOOL,
return m_debugger.GetTerminalWidth();
}
+HRESULT
+LLDBServices::SupportsDml(PULONG supported)
+{
+ supported = 0;
+ return S_OK;
+}
+
+void
+LLDBServices::OutputDmlString(
+ ULONG mask,
+ PCSTR str)
+{
+ OutputString(mask, str);
+}
+
//----------------------------------------------------------------------------
// Helper functions
//----------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE GetOutputWidth();
+ HRESULT STDMETHODCALLTYPE SupportsDml(PULONG supported);
+
+ void STDMETHODCALLTYPE OutputDmlString(
+ ULONG mask,
+ PCSTR str);
+
//----------------------------------------------------------------------------
// LLDBServices (internal)
//----------------------------------------------------------------------------
g_services->AddCommand("logging", new sosCommand("logging"), "Enable/disable internal SOS logging.");
g_services->AddCommand("dumpalc", new sosCommand("DumpALC"), "Displays details about a collectible AssemblyLoadContext to which the specified object is loaded.");
g_services->AddCommand("dumparray", new sosCommand("DumpArray"), "Displays details about a managed array.");
- g_services->AddCommand("dumpasync", new sosCommand("DumpAsync"), "Displays info about async state machines on the garbage-collected heap.");
+ g_services->AddCommand("dumpasync", new sosCommand("DumpAsync"), "Displays information about async \"stacks\" on the garbage-collected heap.");
g_services->AddCommand("dumpassembly", new sosCommand("DumpAssembly"), "Displays details about an assembly.");
g_services->AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address.");
g_services->AddCommand("dumpdelegate", new sosCommand("DumpDelegate"), "Displays information about a delegate.");
using (DiagnosticsClientHolder holder = await builder.Build(ct, _processId, diagnosticPort, showChildIO: false, printLaunchCommand: false))
using (VirtualTerminalMode vTerm = VirtualTerminalMode.TryEnable())
{
- bool useAnsi = vTerm.IsEnabled && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ bool useAnsi = vTerm.IsEnabled;
if (holder == null)
{
return ReturnCode.Ok;
private int maxNameLength = 40; // Allow room for 40 character counter names by default.
private int STATUS_ROW; // Row # of where we print the status of dotnet-counters
+ private int Top_Row;
private bool paused = false;
private bool initialized = false;
private string _errorText = null;
{
if (this.useAnsi)
{
- Console.Write($"\u001b[{row + 1};{col + 1}H");
+ Console.Write($"\u001b[{row + 1 - Top_Row};{col + 1}H");
}
else
{
Clear();
int row = Console.CursorTop;
+ Top_Row = row;
Console.WriteLine("Press p to pause, r to resume, q to quit."); row++;
Console.WriteLine($" Status: {GetStatus()}"); STATUS_ROW = row++;
if (_errorText != null)
_target.ServiceProvider.AddServiceFactory<SOSHost>(() => new SOSHost(_contextService.Services));
// Automatically enable symbol server support, default cache and search for symbols in the dump directory
- _symbolService.AddSymbolServer(msdl: true, symweb: false, symbolServerPath: null, authToken: null, timeoutInMinutes: 0);
+ _symbolService.AddSymbolServer(msdl: true, symweb: false, retryCount: 3);
_symbolService.AddCachePath(_symbolService.DefaultSymbolCache);
_symbolService.AddDirectoryPath(Path.GetDirectoryName(dump_path.FullName));
/// <returns></returns>
private static async Task<int> Report(CancellationToken ct, IConsole console, int processId, string name, TimeSpan duration)
{
- string tempNetTraceFilename = Path.GetRandomFileName() + ".nettrace";
+ string tempNetTraceFilename = Path.Join(Path.GetTempPath(), Path.GetRandomFileName() + ".nettrace");
string tempEtlxFilename = "";
try
using (DiagnosticsClientHolder holder = await builder.Build(ct, processId, diagnosticPort, showChildIO: showchildio, printLaunchCommand: true))
{
- string processMainModuleFileName = "";
+ string processMainModuleFileName = $"Process{processId}";
// if builder returned null, it means we received ctrl+C while waiting for clients to connect. Exit gracefully.
if (holder == null)
{
// Reading the process MainModule filename can fail if the target process closes
// or isn't fully setup. Retry a few times to attempt to address the issue
- for (int attempts = 0; true; attempts++)
+ for (int attempts = 0; attempts < 10; attempts++)
{
try
{
processMainModuleFileName = process.MainModule.FileName;
break;
}
+
catch
{
- if (attempts > 10)
- {
- Console.Error.WriteLine("Unable to examine process.");
- return ReturnCode.SessionCreationError;
- }
Thread.Sleep(200);
}
}
+
}
if (String.Equals(output.Name, DefaultTraceName, StringComparison.OrdinalIgnoreCase))
catch (DiagnosticsClientException e)
{
Console.Error.WriteLine($"Unable to start a tracing session: {e.ToString()}");
+ return ReturnCode.SessionCreationError;
+ }
+ catch (UnauthorizedAccessException e)
+ {
+ Console.Error.WriteLine($"dotnet-trace does not have permission to access the specified app: {e.GetType()}");
+ return ReturnCode.SessionCreationError;
}
if (session == null)
Match match = nameRx.Match(name);
string functionList = match.Groups[1].Value;
string arguments = match.Groups[2].Value;
+ if (functionList == string.Empty && arguments == string.Empty)
+ {
+ return name;
+ }
string[] usingStatement = functionList.Split(".");
int length = usingStatement.Length;
CallTreeNodeBase node = nodesToReport[i];
string name = node.Name;
string formatName = FormatFunction(name);
+ if (formatName == "?!?")
+ {
+ formatName = "Missing Symbol";
+ }
List<string> nameList = SplitInto(formatName, functionColumnWidth);
if(isVerbose)
{
string inclusiveMeasure = "";
string exclusiveMeasure = "";
- string number = new string(' ', maxDigit + 2); //+2 lines 130 and 137 account for '. '
+ string number = new string(' ', maxDigit + 2); //+2 to account for '. '
if(j == 0)
{
ClrRuntimeInfo()
{
ModuleHandle = NULL;
+#ifdef TARGET_UNIX
+ ContinueStartupEvent = NULL;
+#else
ContinueStartupEvent = INVALID_HANDLE_VALUE;
+#endif
EngineMetrics.cbSize = sizeof(EngineMetrics);
EngineMetrics.dwDbiVersion = CorDebugLatestVersion;
}
};
+static
HRESULT
GetRuntime(
DWORD debuggeePID,
ClrRuntimeInfo& clrRuntimeInfo);
+static
HRESULT
GetTargetCLRMetrics(
LPCWSTR wszModulePath,
ClrInfo* pClrInfoOut = NULL,
DWORD *pdwRVAContinueStartupEvent = NULL);
+static
void
AppendDbiDllName(
SString & szFullDbiPath);
+static
bool
CheckDbiAndRuntimeVersion(
SString & szFullDbiPath,
exit:
if (FAILED(hr))
{
- _ASSERTE(pCordb == NULL);
-
+ if (pCordb != NULL)
+ {
+ pCordb->Release();
+ }
// Invoke the callback on error
m_callback(NULL, m_parameter, hr);
}
// Don't need to wake up and wait for the worker thread if called on it
if (m_threadId != GetCurrentThreadId())
{
- // Wait for work thread to exit
- WaitForSingleObject(m_threadHandle, INFINITE);
+ // Wait for work thread to exit for 60 seconds
+ WaitForSingleObject(m_threadHandle, 60 * 1000);
}
}
HRESULT InvokeStartupCallback(bool *pCoreClrExists)
{
ClrRuntimeInfo clrRuntimeInfo;
+ IUnknown *pCordb = NULL;
HRESULT hr = S_OK;
PAL_CPP_TRY
{
- IUnknown *pCordb = NULL;
*pCoreClrExists = FALSE;
SetEvent(clrRuntimeInfo.ContinueStartupEvent);
}
}
-
+ if (FAILED(hr) && (pCordb != NULL))
+ {
+ pCordb->Release();
+ }
return hr;
}
// Return Value:
// true if the versions match
//
-static
bool
CheckDbiAndRuntimeVersion(
SString & szFullDbiPath,
HMODULE hDbi = NULL;
HMODULE hDac = NULL;
ICorDebugDataTarget * pDt = NULL;
- CLR_DEBUGGING_VERSION version;
+ CLR_DEBUGGING_VERSION version = {};
// argument checking
if ((ppProcess != NULL || pFlags != NULL) && pLibraryProvider == NULL)
}
}
- CLR_DEBUG_RESOURCE debugResource;
+ CLR_DEBUG_RESOURCE debugResource = {};
if (SUCCEEDED(hr) && debugResourceSize != sizeof(debugResource))
{
hr = CORDBG_E_NOT_CLR;
<Project>
<Import Project="$(MSBuildThisFileDirectory)..\..\Directory.Build.props"/>
<PropertyGroup>
- <IsShipping>false</IsShipping>
+ <IsShipping>true</IsShipping>
<NoPackageAnalysis>true</NoPackageAnalysis>
<PackageDescription>Internal implementation package not meant for direct consumption. Please do not reference directly.</PackageDescription>
</PropertyGroup>
// after the signature is a 20 byte image file header
// we need to parse this to figure out the target architecture
- IMAGE_FILE_HEADER imageFileHeader;
+ IMAGE_FILE_HEADER imageFileHeader = {};
if (SUCCEEDED(hr))
{
hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + peSigFilePointer + 4, (BYTE*)&imageFileHeader, IMAGE_SIZEOF_FILE_HEADER);
}
-
-
WORD optHeaderMagic = 0;
DWORD peOptImageHeaderFilePointer = 0;
if (SUCCEEDED(hr))
}
return hr;
-}
+}
\ No newline at end of file
};
- const UINT nCONTEXTRegisters = sizeof(rgRegisters)/sizeof(rgRegisters[0]);
-
UINT iFirstRegister;
UINT iSPRegister;
- UINT nRegisters;
iFirstRegister = 0;
- nRegisters = nCONTEXTRegisters;
#ifdef TARGET_AMD64
iSPRegister = (FIELD_OFFSET(CONTEXT, Rsp) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONGLONG);
#elif defined(TARGET_ARM64)
{
SIZE_T *pReg = NULL;
- for (UINT iReg = 0; iReg < nRegisters; iReg++)
+ for (UINT iReg = 0; iReg < ARRAY_SIZE(rgRegisters); iReg++)
{
UINT iEncodedReg = iFirstRegister + iReg;
#ifdef TARGET_ARM
}
#endif
{
- _ASSERTE(iReg < nCONTEXTRegisters);
+ _ASSERTE(iReg < ARRAY_SIZE(rgRegisters));
#ifdef TARGET_ARM
pReg = *(SIZE_T**)(pContext + rgRegisters[iReg].cbContextOffset);
if (iEncodedReg == 12)
"EDI"
};
- _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
+ _ASSERTE(reg < ARRAY_SIZE(regNames));
return regNames[reg];
}
"EBP"
};
- _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
+ _ASSERTE(reg < ARRAY_SIZE(regNames));
return regNames[reg];
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef HAVE_MINIPAL_GETEXEPATH_H
+#define HAVE_MINIPAL_GETEXEPATH_H
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#elif defined(__FreeBSD__)
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#elif HAVE_GETAUXVAL
+#include <sys/auxv.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns the full path to the executable for the current process, resolving symbolic links.
+// The caller is responsible for releasing the buffer. Returns null on error.
+static inline char* minipal_getexepath(void)
+{
+#if defined(__APPLE__)
+ uint32_t path_length = 0;
+ if (_NSGetExecutablePath(NULL, &path_length) != -1)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ char path_buf[path_length];
+ if (_NSGetExecutablePath(path_buf, &path_length) != 0)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return realpath(path_buf, NULL);
+#elif defined(__FreeBSD__)
+ static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ char path[PATH_MAX];
+ size_t len = sizeof(path);
+ if (sysctl(name, 4, path, &len, NULL, 0) != 0)
+ {
+ return NULL;
+ }
+
+ return strdup(path);
+#elif defined(__sun)
+ const char* path = getexecname();
+ if (path == NULL)
+ {
+ return NULL;
+ }
+
+ return realpath(path, NULL);
+#elif defined(_WIN32)
+ char path[MAX_PATH];
+ if (GetModuleFileNameA(NULL, path, MAX_PATH) == 0)
+ {
+ return NULL;
+ }
+
+ return _strdup(path);
+#elif defined(TARGET_WASM)
+ // This is a packaging convention that our tooling should enforce.
+ return strdup("/managed");
+#else
+#if HAVE_GETAUXVAL && defined(AT_EXECFN)
+ const char* path = (const char *)(getauxval(AT_EXECFN));
+ if (path && !errno)
+ {
+ return realpath(path, NULL);
+ }
+#endif // HAVE_GETAUXVAL && defined(AT_EXECFN)
+#ifdef __linux__
+ const char* symlinkEntrypointExecutable = "/proc/self/exe";
+#else
+ const char* symlinkEntrypointExecutable = "/proc/curproc/exe";
+#endif
+
+ // Resolve the symlink to the executable from /proc
+ return realpath(symlinkEntrypointExecutable, NULL);
+#endif // defined(__APPLE__)
+}
+
+#ifdef __cplusplus
+}
+#endif // extern "C"
+
+#endif // HAVE_MINIPAL_GETEXEPATH_H
/* Include our configuration information so it's always present when
compiling PAL implementation files. */
#include "config.h"
+#include <minipal/utils.h>
#ifdef DEBUG
#define _ENABLE_DEBUG_MESSAGES_ 1
ErrorString *stringEntry = (ErrorString *)bsearch(
&searchEntry,
palErrorStrings,
- sizeof(palErrorStrings) / sizeof(palErrorStrings[0]),
+ ARRAY_SIZE(palErrorStrings),
sizeof(ErrorString),
CompareErrorStrings);
#endif /* _UNICODE */
{
_TCHAR floatstring[_CVTBUFSIZE + 1];
- _TCHAR *pFloatStr=floatstring;
- size_t nFloatStrUsed=0;
- size_t nFloatStrSz=sizeof(floatstring)/sizeof(floatstring[0]);
- int malloc_FloatStrFlag=0;
+ _TCHAR *pFloatStr = floatstring;
+ size_t nFloatStrUsed = 0;
+ size_t nFloatStrSz = ARRAY_SIZE(floatstring);
+ int malloc_FloatStrFlag = 0;
unsigned long number; /* temp hold-value */
#if ALLOC_TABLE
using namespace CorUnix;
-static PalObjectTypeId sg_rgWaitObjectsIds[] =
- {
- otiProcess,
- otiThread
- };
-static CAllowedObjectTypes sg_aotWaitObject(sg_rgWaitObjectsIds,
- sizeof(sg_rgWaitObjectsIds)/sizeof(sg_rgWaitObjectsIds[0]));
+static PalObjectTypeId sg_rgWaitObjectsIds[] = { otiProcess, otiThread };
+static CAllowedObjectTypes sg_aotWaitObject(sg_rgWaitObjectsIds, ARRAY_SIZE(sg_rgWaitObjectsIds));
/*++
Function:
// Don't need to wait for the worker thread if unregister called on it
if (m_threadId != (DWORD)THREADSilentGetCurrentThreadId())
{
- // Wait for work thread to exit
- if (WaitForSingleObject(m_threadHandle, INFINITE) != WAIT_OBJECT_0)
- {
- ASSERT("WaitForSingleObject\n");
- }
+ // Wait for work thread to exit for 60 seconds
+ WaitForSingleObject(m_threadHandle, 60 * 1000);
}
}
HRESULT hr = S_OK;
HMODULE ret = NULL;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
CONTRACTL_END;
HRESULT hr = S_OK;
- DWORD lastError;
+ DWORD lastError = 0;
HANDLE ret = INVALID_HANDLE_VALUE;
EX_TRY
HRESULT hr = S_OK;
DWORD ret = INVALID_FILE_ATTRIBUTES;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
BOOL ret = FALSE;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
BOOL ret = FALSE;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
BOOL ret = FALSE;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
DWORD ret = 0;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
DWORD ret = 0;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
UINT ret = 0;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
DWORD ret = 0;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
DWORD ret = 0;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
DWORD ret = 0;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
BOOL ret = FALSE;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
HRESULT hr = S_OK;
HANDLE ret = INVALID_HANDLE_VALUE;
- DWORD lastError;
+ DWORD lastError = 0;
EX_TRY
{
{
return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
}
-
-
-
if (dwFlags == LCMAP_UPPERCASE)
{
- wTmp =
+ wTmp = (WCHAR)
#ifdef SELF_NO_HOST
toupper(wc);
#else
else
{
_ASSERTE(dwFlags == LCMAP_LOWERCASE);
- wTmp =
+ wTmp = (WCHAR)
#ifdef SELF_NO_HOST
tolower(wc);
#else
return false;
}
-#endif //DACCESS_COMPILE
+#endif //DACCESS_COMPILE
\ No newline at end of file
public delegate void RuntimeStartupCallbackDelegate(ICorDebug cordbg, object parameter, HResult hresult);
- public static HResult RegisterForRuntimeStartup(int pid, object parameter, out IntPtr unregisterToken, RuntimeStartupCallbackDelegate callback)
+ public static HResult RegisterForRuntimeStartup(int pid, object parameter, out (IntPtr, GCHandle) unregister, RuntimeStartupCallbackDelegate callback)
{
- IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out IntPtr nativeParameter);
- return _registerForRuntimeStartup((uint)pid, nativeCallback, nativeParameter, out unregisterToken);
+ IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out GCHandle gchNativeCallback, out IntPtr nativeParameter);
+ HResult hr = _registerForRuntimeStartup((uint)pid, nativeCallback, nativeParameter, out IntPtr unregisterToken);
+ unregister = (unregisterToken, gchNativeCallback);
+ return hr;
}
- public static HResult RegisterForRuntimeStartupEx(int pid, string applicationGroupId, object parameter, out IntPtr unregisterToken, RuntimeStartupCallbackDelegate callback)
+ public static HResult RegisterForRuntimeStartupEx(int pid, string applicationGroupId, object parameter, out (IntPtr, GCHandle) unregister, RuntimeStartupCallbackDelegate callback)
{
- IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out IntPtr nativeParameter);
- return _registerForRuntimeStartupEx((uint)pid, applicationGroupId, nativeCallback, nativeParameter, out unregisterToken);
+ IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out GCHandle nativeCallbackHandle, out IntPtr nativeParameter);
+ HResult hr = _registerForRuntimeStartupEx((uint)pid, applicationGroupId, nativeCallback, nativeParameter, out IntPtr unregisterToken);
+ unregister = (unregisterToken, nativeCallbackHandle);
+ return hr;
}
- public static HResult RegisterForRuntimeStartup3(int pid, string applicationGroupId, object parameter, IntPtr libraryProvider, out IntPtr unregisterToken, RuntimeStartupCallbackDelegate callback)
+ public static HResult RegisterForRuntimeStartup3(int pid, string applicationGroupId, object parameter, IntPtr libraryProvider, out (IntPtr, GCHandle) unregister, RuntimeStartupCallbackDelegate callback)
{
if (_registerForRuntimeStartup3 == default)
{
throw new NotSupportedException("RegisterForRuntimeStartup3 not supported");
}
- IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out IntPtr nativeParameter);
- return _registerForRuntimeStartup3((uint)pid, applicationGroupId, libraryProvider, nativeCallback, nativeParameter, out unregisterToken);
+ IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out GCHandle nativeCallbackHandle, out IntPtr nativeParameter);
+ HResult hr = _registerForRuntimeStartup3((uint)pid, applicationGroupId, libraryProvider, nativeCallback, nativeParameter, out IntPtr unregisterToken);
+ unregister = (unregisterToken, nativeCallbackHandle);
+ return hr;
}
private delegate void NativeRuntimeStartupCallbackDelegate(IntPtr cordbg, IntPtr parameter, HResult hresult);
- private static IntPtr RuntimeStartupCallback(object parameter, RuntimeStartupCallbackDelegate callback, out IntPtr nativeParameter)
+ private static IntPtr RuntimeStartupCallback(object parameter, RuntimeStartupCallbackDelegate callback, out GCHandle nativeCallbackHandle, out IntPtr nativeParameter)
{
NativeRuntimeStartupCallbackDelegate native = (IntPtr cordbg, IntPtr param, HResult hresult) => {
GCHandle gch = GCHandle.FromIntPtr(param);
callback(ICorDebug.Create(cordbg), gch.Target, hresult);
gch.Free();
};
+ // Need to keep native callback delegate alive until UnregisterForRuntimeStartup
+ nativeCallbackHandle = GCHandle.Alloc(native);
+
+ // Need to keep parameter alive until the callback is invoked
GCHandle gchParameter = GCHandle.Alloc(parameter);
nativeParameter = GCHandle.ToIntPtr(gchParameter);
+
+ // Return the function pointer for the native callback
return Marshal.GetFunctionPointerForDelegate(native);
}
- public static HResult UnregisterForRuntimeStartup(IntPtr unregisterToken) => _unregisterForRuntimeStartup(unregisterToken);
+ public static HResult UnregisterForRuntimeStartup((IntPtr unregisterToken, GCHandle nativeCallbackHandle) unregister)
+ {
+ HResult hr = _unregisterForRuntimeStartup(unregister.unregisterToken);
+ unregister.nativeCallbackHandle.Free();
+ return hr;
+ }
private const int HRESULT_ERROR_PARTIAL_COPY = unchecked((int)0x8007012b);
private const int HRESULT_ERROR_BAD_LENGTH = unchecked((int)0x80070018);
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Assert.True(await debuggeeInfo.WaitForDebuggee());
}
Trace.TraceInformation("CreateProcessForLaunch pid {0} DONE", processId);
- return debuggeeInfo;
+ return debuggeeInfo;
}
private static void TestRegisterForRuntimeStartup(DebuggeeInfo debuggeeInfo, int api)
{
TestConfiguration config = debuggeeInfo.TestConfiguration;
AutoResetEvent wait = new AutoResetEvent(false);
+ (IntPtr, GCHandle) unregister = (IntPtr.Zero, default);
string applicationGroupId = null;
- IntPtr unregisterToken = IntPtr.Zero;
HResult result = HResult.S_OK;
HResult callbackResult = HResult.S_OK;
Exception callbackException = null;
switch (api)
{
case 1:
- result = DbgShimAPI.RegisterForRuntimeStartup(debuggeeInfo.ProcessId, parameter: IntPtr.Zero, out unregisterToken, callback);
+ result = DbgShimAPI.RegisterForRuntimeStartup(debuggeeInfo.ProcessId, parameter: IntPtr.Zero, out unregister, callback);
break;
case 2:
- result = DbgShimAPI.RegisterForRuntimeStartupEx(debuggeeInfo.ProcessId, applicationGroupId, parameter: IntPtr.Zero, out unregisterToken, callback);
+ result = DbgShimAPI.RegisterForRuntimeStartupEx(debuggeeInfo.ProcessId, applicationGroupId, parameter: IntPtr.Zero, out unregister, callback);
break;
case 3:
LibraryProviderWrapper libraryProvider = new(config.RuntimeModulePath(), config.DbiModulePath(), config.DacModulePath());
- result = DbgShimAPI.RegisterForRuntimeStartup3(debuggeeInfo.ProcessId, applicationGroupId, parameter: IntPtr.Zero, libraryProvider.ILibraryProvider, out unregisterToken, callback);
+ result = DbgShimAPI.RegisterForRuntimeStartup3(debuggeeInfo.ProcessId, applicationGroupId, parameter: IntPtr.Zero, libraryProvider.ILibraryProvider, out unregister, callback);
break;
default:
throw new ArgumentException(nameof(api));
Assert.True(wait.WaitOne());
Trace.TraceInformation("RegisterForRuntimeStartup pid {0} after callback wait", debuggeeInfo.ProcessId);
- AssertResult(DbgShimAPI.UnregisterForRuntimeStartup(unregisterToken));
+ AssertResult(DbgShimAPI.UnregisterForRuntimeStartup(unregister));
Assert.Null(callbackException);
switch (api)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
- using Utilities.ELFModule elfModule = Utilities.OpenELFFile(filePath);
+ using ELFModule elfModule = ELFModule.OpenFile(filePath);
if (elfModule is not null)
{
return elfModule.BuildID.ToImmutableArray();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
- using Utilities.MachOModule machOModule = Utilities.OpenMachOFile(filePath);
+ using MachOModule machOModule = MachOModule.OpenFile(filePath);
if (machOModule is not null)
{
return machOModule.Uuid.ToImmutableArray();
if (_symbolService is null)
{
_symbolService = new SymbolService(this);
- _symbolService.AddSymbolServer(msdl: true, symweb: false, symbolServerPath: null, authToken: null, timeoutInMinutes: 0);
+ _symbolService.AddSymbolServer(msdl: true, symweb: false, timeoutInMinutes: 6, retryCount: 5);
_symbolService.AddCachePath(SymbolService.DefaultSymbolCache);
}
return _symbolService;
<PropertyGroup>
<OutputType>Exe</OutputType>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
using Microsoft.Diagnostics.Tracing;
using Process = System.Diagnostics.Process;
+using System.Runtime.Versioning;
namespace Orchestrator
{
};
}
+ [SupportedOSPlatformGuard("Windows")]
+ [SupportedOSPlatformGuard("Linux")]
+ static bool IsWindowsOrLinux => OperatingSystem.IsLinux() || OperatingSystem.IsWindows();
+
static async Task<int> Orchestrate(
IConsole console,
CancellationToken ct,
eventWritingProc.Start();
Console.WriteLine($"Executing: {eventWritingProc.StartInfo.FileName} {eventWritingProc.StartInfo.Arguments}");
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+
+ if (IsWindowsOrLinux)
{
// Set affinity and priority
ulong affinityMask = 0;
affinityMask |= ((ulong)1 << j);
}
eventWritingProc.ProcessorAffinity = (IntPtr)((ulong)eventWritingProc.ProcessorAffinity & affinityMask);
- eventWritingProc.PriorityClass = ProcessPriorityClass.RealTime; // Set the process priority to highest possible
}
// Start listening to the event.
<PropertyGroup>
<OutputType>Exe</OutputType>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
// See the LICENSE file in the project root for more information.
using Microsoft.Diagnostics.DebugServices.Implementation;
+using Microsoft.SymbolStore.SymbolStores;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
+using System.Text;
using Xunit;
namespace Microsoft.Diagnostics.DebugServices.UnitTests
#endregion
}
+
public static class SymbolServiceExtensions
{
- public static string FormatSymbolStores(
- this ISymbolService symbolService)
+ public static string FormatSymbolStores(this SymbolService symbolService)
{
- return symbolService.ToString().Replace(Environment.NewLine, " ").TrimEnd();
+ StringBuilder sb = new StringBuilder();
+ symbolService.ForEachSymbolStore<Microsoft.SymbolStore.SymbolStores.SymbolStore>((symbolStore) => sb.AppendLine(symbolStore.ToString()));
+ return sb.ToString().Replace(Environment.NewLine, " ").TrimEnd();
}
}
}
throw new DiagnosticsException($"Loading {sosPath} FAILED {hr:X8}");
}
+ // Set the HTTP symbol store timeout and retry count before the symbol path is added to the symbol service
+ HostServices.DefaultTimeout = 6;
+ HostServices.DefaultRetryCount = 5;
+
// Initialize the extension host
hr = HostServices.Initialize(sosPath);
if (hr != HResult.S_OK) {
throw new DiagnosticsException($"HostServices.Initialize({sosPath}) FAILED {hr:X8}");
}
- var symbolService = Host.Services.GetService<ISymbolService>();
+ ISymbolService symbolService = Host.Services.GetService<ISymbolService>();
Trace.TraceInformation($"SymbolService: {symbolService}");
}