diff --git a/.github/fabricbot.json b/.github/fabricbot.json index 2e190edba0..fdc12a789c 100644 --- a/.github/fabricbot.json +++ b/.github/fabricbot.json @@ -887,149 +887,6 @@ ] } }, - { - "taskType": "scheduled", - "capabilityId": "ScheduledSearch", - "subCapability": "ScheduledSearch", - "version": "1.1", - "config": { - "frequency": [ - { - "weekDay": 0, - "hours": [ - 1, - 4, - 7, - 10, - 13, - 16, - 19, - 22 - ], - "timezoneOffset": -8 - }, - { - "weekDay": 1, - "hours": [ - 1, - 4, - 7, - 10, - 13, - 16, - 19, - 22 - ], - "timezoneOffset": -8 - }, - { - "weekDay": 2, - "hours": [ - 1, - 4, - 7, - 10, - 13, - 16, - 19, - 22 - ], - "timezoneOffset": -8 - }, - { - "weekDay": 3, - "hours": [ - 1, - 4, - 7, - 10, - 13, - 16, - 19, - 22 - ], - "timezoneOffset": -8 - }, - { - "weekDay": 4, - "hours": [ - 1, - 4, - 7, - 10, - 13, - 16, - 19, - 22 - ], - "timezoneOffset": -8 - }, - { - "weekDay": 5, - "hours": [ - 1, - 4, - 7, - 10, - 13, - 16, - 19, - 22 - ], - "timezoneOffset": -8 - }, - { - "weekDay": 6, - "hours": [ - 1, - 4, - 7, - 10, - 13, - 16, - 19, - 22 - ], - "timezoneOffset": -8 - } - ], - "searchTerms": [ - { - "name": "isClosed", - "parameters": {} - }, - { - "name": "noActivitySince", - "parameters": { - "days": 30 - } - }, - { - "name": "isUnlocked", - "parameters": {} - }, - { - "name": "isIssue", - "parameters": {} - }, - { - "name": "noLabel", - "parameters": { - "label": "Bot: Do Not Lock" - } - } - ], - "taskName": "[Closed Issue Management] Lock issues closed without activity for over 30 days", - "actions": [ - { - "name": "lockIssue", - "parameters": { - "reason": "resolved" - } - } - ] - } - } ], "userGroups": [] } diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml new file mode 100644 index 0000000000..0e7b771510 --- /dev/null +++ b/.github/workflows/locker.yml @@ -0,0 +1,36 @@ +name: Locker - Lock stale issues and PRs +on: + schedule: + - cron: '0 9 * * *' # Once per day, early morning PT + + workflow_dispatch: + # Manual triggering through the GitHub UI, API, or CLI + inputs: + daysSinceClose: + required: true + default: "30" + daysSinceUpdate: + required: true + default: "30" + +permissions: + issues: write + pull-requests: write + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v4 + with: + repository: "microsoft/vscode-github-triage-actions" + path: ./actions + ref: cd16cd2aad6ba2da74bb6c6f7293adddd579a90e # locker action commit sha + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run Locker + uses: ./actions/locker + with: + daysSinceClose: ${{ fromJson(inputs.daysSinceClose || 30) }} + daysSinceUpdate: ${{ fromJson(inputs.daysSinceUpdate || 30) }} diff --git a/diagnostics.sln b/diagnostics.sln index 23ae2ff91a..08398b86bf 100644 --- a/diagnostics.sln +++ b/diagnostics.sln @@ -219,6 +219,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.Debug EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{BE45F03E-D700-404F-A890-8ED7D8969958}" ProjectSection(SolutionItems) = preProject + src\SOS\inc\clrma.h = src\SOS\inc\clrma.h + src\SOS\inc\clrma.idl = src\SOS\inc\clrma.idl src\SOS\inc\debuggerservices.h = src\SOS\inc\debuggerservices.h src\SOS\inc\host.h = src\SOS\inc\host.h src\SOS\inc\hostservices.h = src\SOS\inc\hostservices.h diff --git a/documentation/design-docs/ipc-protocol.md b/documentation/design-docs/ipc-protocol.md index a8c2496a90..d55f815a43 100644 --- a/documentation/design-docs/ipc-protocol.md +++ b/documentation/design-docs/ipc-protocol.md @@ -370,7 +370,7 @@ enum class ProfilerCommandId : uint8_t AttachProfiler = 0x01, // future } -``` +``` See: [Profiler Commands](#Profiler-Commands) ```c++ @@ -460,7 +460,7 @@ Payload array providers } -provider_config +provider_config { ulong keywords, uint logLevel, @@ -482,7 +482,7 @@ Followed by an Optional Continuation of a `nettrace` format stream of events. Command Code: `0x0203` -The `CollectTracing2` Command is an extension of the `CollectTracing` command - its behavior is the same as `CollectTracing` command, except that it has another field that lets you specify whether rundown events should be fired by the runtime. +The `CollectTracing2` command is an extension of the `CollectTracing` command - its behavior is the same as `CollectTracing` command, except that it has another field that lets you specify whether rundown events should be fired by the runtime. #### Inputs: @@ -500,7 +500,7 @@ A `provider_config` is composed of the following data: * `string filter_data` (optional): Filter information > see ETW documentation for a more detailed explanation of Keywords, Filters, and Log Level. -> +> #### Returns (as an IPC Message Payload): Header: `{ Magic; 28; 0xFF00; 0x0000; }` @@ -520,7 +520,7 @@ Payload array providers } -provider_config +provider_config { ulong keywords, uint logLevel, @@ -538,7 +538,70 @@ Payload ``` Followed by an Optional Continuation of a `nettrace` format stream of events. -### `StopTracing` +### `CollectTracing3` + +Command Code: `0x0204` + +The `CollectTracing3` command is an extension of the `CollectTracing2` command - its behavior is the same as `CollectTracing2` command, except that it has another field that lets you specify whether the stackwalk should be made for each event. + +#### Inputs: + +Header: `{ Magic; Size; 0x0203; 0x0000 }` + +* `uint circularBufferMB`: The size of the circular buffer used for buffering event data while streaming +* `uint format`: 0 for the legacy NetPerf format and 1 for the NetTrace format +* `bool requestRundown`: Indicates whether rundown should be fired by the runtime. +* `bool requestStackwalk`: Indicates whether stacktrace information should be recorded. +* `array providers`: The providers to turn on for the streaming session + +A `provider_config` is composed of the following data: +* `ulong keywords`: The keywords to turn on with this providers +* `uint logLevel`: The level of information to turn on +* `string provider_name`: The name of the provider +* `string filter_data` (optional): Filter information + +> see ETW documentation for a more detailed explanation of Keywords, Filters, and Log Level. +> +#### Returns (as an IPC Message Payload): + +Header: `{ Magic; 28; 0xFF00; 0x0000; }` + +`CollectTracing2` returns: +* `ulong sessionId`: the ID for the stream session starting on the current connection + +##### Details: + +Input: +``` +Payload +{ + uint circularBufferMB, + uint format, + bool requestRundown, + bool requestStackwalk, + array providers +} + +provider_config +{ + ulong keywords, + uint logLevel, + string provider_name, + string filter_data (optional) +} +``` + +Returns: +```c +Payload +{ + ulong sessionId +} +``` +Followed by an Optional Continuation of a `nettrace` format stream of events. + + +### `StopTracing` Command Code: `0x0201` diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index 2a571b3e57..854ceff458 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -3,6 +3,5 @@ - - \ No newline at end of file + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 355fddd00f..d64ed09916 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,55 +1,55 @@ - + https://github.com/dotnet/symstore - 21508f9ab057e19d4060855647edeb376ac7d59d + b26b065af8647262418c1ef4609de690a003bfa3 - + https://github.com/microsoft/clrmd - b64f583396784300c7ded11d22c65053197e958a + b9b01f21c353f59908b1563e1f17831eaad41b28 - + https://github.com/microsoft/clrmd - b64f583396784300c7ded11d22c65053197e958a + b9b01f21c353f59908b1563e1f17831eaad41b28 - + https://github.com/dotnet/arcade - 6a5ca678aef84f34a1fccc46f76623f41da56553 + 3faeb9817f465151aa4bbcdb315f0a6170206760 - + https://github.com/dotnet/arcade - 6a5ca678aef84f34a1fccc46f76623f41da56553 + 3faeb9817f465151aa4bbcdb315f0a6170206760 https://github.com/dotnet/arcade ccfe6da198c5f05534863bbb1bff66e830e0c6ab - + https://github.com/dotnet/installer - 30d7d24a6592aa0c97f81ac36b563fab2b451b14 + bc60f176c86a6b1a290b3badc509ec86233f9543 - + https://github.com/dotnet/aspnetcore - c9fa5f3a34605c93bffd1459a5e39e6bc63f50cc + 815eb281ecad13eb69cf50516ac7f534b947f2b0 - + https://github.com/dotnet/aspnetcore - c9fa5f3a34605c93bffd1459a5e39e6bc63f50cc + 815eb281ecad13eb69cf50516ac7f534b947f2b0 - + https://github.com/dotnet/runtime - 11ad607efb2b31c5e1b906303fcd70341e9d5206 + 59edaad404d1b8e47080015ae8d0787f94c970df - + https://github.com/dotnet/runtime - 11ad607efb2b31c5e1b906303fcd70341e9d5206 + 59edaad404d1b8e47080015ae8d0787f94c970df - + https://github.com/dotnet/source-build-reference-packages - d3fbf3c3d4c4f142ea12efceaa6efece9ad2e6b5 + 5357f2bafb9e23858aa57136d38dbb113cdf81a2 diff --git a/eng/Versions.props b/eng/Versions.props index 6a38791823..440e78d98d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -16,26 +16,26 @@ - 1.0.451601 + 1.0.505101 - 8.0.0-rtm.23520.16 - 8.0.0-rtm.23520.16 + 8.0.0 + 8.0.0 - 8.0.0-rtm.23520.10 - 8.0.0-rtm.23520.10 + 8.0.0 + 8.0.0 - 8.0.100-rtm.23522.1 + 8.0.101-servicing.23567.12 - 6.0.19 + 6.0.24 $(MicrosoftNETCoreApp60Version) - 7.0.11 + 7.0.14 $(MicrosoftNETCoreApp70Version) $(MicrosoftNETCoreApp60Version) $(MicrosoftNETCoreApp70Version) - 8.0.0-rtm.23519.13 + 8.0.0 @@ -46,7 +46,7 @@ 6.0.0 6.0.0 - 3.1.451701 + 3.1.457001 16.11.27-beta1.23180.1 3.0.7 6.0.0 @@ -62,12 +62,12 @@ 6.0.0 6.0.8 2.0.3 - 9.0.0-beta.23518.2 + 9.0.0-beta.23607.2 1.2.0-beta.406 7.0.0-beta.22316.2 10.0.18362 13.0.1 - 9.0.0-alpha.1.23519.2 + 9.0.0-alpha.1.24052.1 3.11.0 diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake index 38e96724f5..3762640fdc 100644 --- a/eng/common/cross/toolchain.cmake +++ b/eng/common/cross/toolchain.cmake @@ -40,7 +40,7 @@ if(TARGET_ARCH_NAME STREQUAL "arm") set(TOOLCHAIN "arm-linux-gnueabihf") endif() if(TIZEN) - set(TIZEN_TOOLCHAIN "armv7hl-tizen-linux-gnueabihf/13.1.0") + set(TIZEN_TOOLCHAIN "armv7hl-tizen-linux-gnueabihf/9.2.0") endif() elseif(TARGET_ARCH_NAME STREQUAL "arm64") set(CMAKE_SYSTEM_PROCESSOR aarch64) @@ -49,7 +49,7 @@ elseif(TARGET_ARCH_NAME STREQUAL "arm64") elseif(LINUX) set(TOOLCHAIN "aarch64-linux-gnu") if(TIZEN) - set(TIZEN_TOOLCHAIN "aarch64-tizen-linux-gnu/13.1.0") + set(TIZEN_TOOLCHAIN "aarch64-tizen-linux-gnu/9.2.0") endif() elseif(FREEBSD) set(triple "aarch64-unknown-freebsd12") @@ -58,7 +58,7 @@ elseif(TARGET_ARCH_NAME STREQUAL "armel") set(CMAKE_SYSTEM_PROCESSOR armv7l) set(TOOLCHAIN "arm-linux-gnueabi") if(TIZEN) - set(TIZEN_TOOLCHAIN "armv7l-tizen-linux-gnueabi/13.1.0") + set(TIZEN_TOOLCHAIN "armv7l-tizen-linux-gnueabi/9.2.0") endif() elseif(TARGET_ARCH_NAME STREQUAL "armv6") set(CMAKE_SYSTEM_PROCESSOR armv6l) @@ -98,7 +98,7 @@ elseif(TARGET_ARCH_NAME STREQUAL "x64") elseif(LINUX) set(TOOLCHAIN "x86_64-linux-gnu") if(TIZEN) - set(TIZEN_TOOLCHAIN "x86_64-tizen-linux-gnu/13.1.0") + set(TIZEN_TOOLCHAIN "x86_64-tizen-linux-gnu/9.2.0") endif() elseif(FREEBSD) set(triple "x86_64-unknown-freebsd12") @@ -115,7 +115,7 @@ elseif(TARGET_ARCH_NAME STREQUAL "x86") set(TOOLCHAIN "i686-linux-gnu") endif() if(TIZEN) - set(TIZEN_TOOLCHAIN "i586-tizen-linux-gnu/13.1.0") + set(TIZEN_TOOLCHAIN "i586-tizen-linux-gnu/9.2.0") endif() else() message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only arm, arm64, armel, armv6, ppc64le, riscv64, s390x, x64 and x86 are supported!") @@ -288,6 +288,8 @@ elseif(TARGET_ARCH_NAME MATCHES "^(arm64|x64|riscv64)$") add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64") add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}") endif() +elseif(TARGET_ARCH_NAME STREQUAL "s390x") + add_toolchain_linker_flag("--target=${TOOLCHAIN}") elseif(TARGET_ARCH_NAME STREQUAL "x86") if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/i586-alpine-linux-musl) add_toolchain_linker_flag("--target=${TOOLCHAIN}") @@ -335,6 +337,8 @@ if(TARGET_ARCH_NAME MATCHES "^(arm|armel)$") if(TARGET_ARCH_NAME STREQUAL "armel") add_compile_options(-mfloat-abi=softfp) endif() +elseif(TARGET_ARCH_NAME STREQUAL "s390x") + add_compile_options("--target=${TOOLCHAIN}") elseif(TARGET_ARCH_NAME STREQUAL "x86") if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/i586-alpine-linux-musl) add_compile_options(--target=${TOOLCHAIN}) diff --git a/eng/common/darc-init.ps1 b/eng/common/darc-init.ps1 index 435e764134..8fda30bdce 100644 --- a/eng/common/darc-init.ps1 +++ b/eng/common/darc-init.ps1 @@ -1,6 +1,6 @@ param ( $darcVersion = $null, - $versionEndpoint = 'https://maestro-prod.westus2.cloudapp.azure.com/api/assets/darc-version?api-version=2019-01-16', + $versionEndpoint = 'https://maestro.dot.net/api/assets/darc-version?api-version=2019-01-16', $verbosity = 'minimal', $toolpath = $null ) diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh index 84c1d0cc2e..c305ae6bd7 100644 --- a/eng/common/darc-init.sh +++ b/eng/common/darc-init.sh @@ -2,7 +2,7 @@ source="${BASH_SOURCE[0]}" darcVersion='' -versionEndpoint='https://maestro-prod.westus2.cloudapp.azure.com/api/assets/darc-version?api-version=2019-01-16' +versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2019-01-16' verbosity='minimal' while [[ $# > 0 ]]; do diff --git a/eng/common/post-build/add-build-to-channel.ps1 b/eng/common/post-build/add-build-to-channel.ps1 index de2d957922..49938f0c89 100644 --- a/eng/common/post-build/add-build-to-channel.ps1 +++ b/eng/common/post-build/add-build-to-channel.ps1 @@ -2,7 +2,7 @@ param( [Parameter(Mandatory=$true)][int] $BuildId, [Parameter(Mandatory=$true)][int] $ChannelId, [Parameter(Mandatory=$true)][string] $MaestroApiAccessToken, - [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro-prod.westus2.cloudapp.azure.com', + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$false)][string] $MaestroApiVersion = '2019-01-16' ) diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 8508397d77..1e779fec4d 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -3,7 +3,7 @@ param( [Parameter(Mandatory=$true)][int] $PublishingInfraVersion, [Parameter(Mandatory=$true)][string] $AzdoToken, [Parameter(Mandatory=$true)][string] $MaestroToken, - [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro-prod.westus2.cloudapp.azure.com', + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$true)][string] $WaitPublishingFinish, [Parameter(Mandatory=$false)][string] $ArtifactsPublishingAdditionalParameters, [Parameter(Mandatory=$false)][string] $SymbolPublishingAdditionalParameters diff --git a/eng/common/post-build/redact-logs.ps1 b/eng/common/post-build/redact-logs.ps1 new file mode 100644 index 0000000000..82d91f6fd0 --- /dev/null +++ b/eng/common/post-build/redact-logs.ps1 @@ -0,0 +1,81 @@ +[CmdletBinding(PositionalBinding=$False)] +param( + [Parameter(Mandatory=$true, Position=0)][string] $InputPath, + [Parameter(Mandatory=$true)][string] $BinlogToolVersion, + [Parameter(Mandatory=$false)][string] $DotnetPath, + [Parameter(Mandatory=$false)][string] $PackageFeed = 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json', + # File with strings to redact - separated by newlines. + # For comments start the line with '# ' - such lines are ignored + [Parameter(Mandatory=$false)][string] $TokensFilePath, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$TokensToRedact +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + $packageName = 'binlogtool' + + $dotnet = $DotnetPath + + if (!$dotnet) { + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + } + + $toolList = & "$dotnet" tool list -g + + if ($toolList -like "*$packageName*") { + & "$dotnet" tool uninstall $packageName -g + } + + $toolPath = "$PSScriptRoot\..\..\..\.tools" + $verbosity = 'minimal' + + New-Item -ItemType Directory -Force -Path $toolPath + + Push-Location -Path $toolPath + + try { + Write-Host "Installing Binlog redactor CLI..." + Write-Host "'$dotnet' new tool-manifest" + & "$dotnet" new tool-manifest + Write-Host "'$dotnet' tool install $packageName --local --add-source '$PackageFeed' -v $verbosity --version $BinlogToolVersion" + & "$dotnet" tool install $packageName --local --add-source "$PackageFeed" -v $verbosity --version $BinlogToolVersion + + if (Test-Path $TokensFilePath) { + Write-Host "Adding additional sensitive data for redaction from file: " $TokensFilePath + $TokensToRedact += Get-Content -Path $TokensFilePath | Foreach {$_.Trim()} | Where { $_ -notmatch "^# " } + } + + $optionalParams = [System.Collections.ArrayList]::new() + + Foreach ($p in $TokensToRedact) + { + if($p -match '^\$\(.*\)$') + { + Write-Host ("Ignoring token {0} as it is probably unexpanded AzDO variable" -f $p) + } + elseif($p) + { + $optionalParams.Add("-p:" + $p) | Out-Null + } + } + + & $dotnet binlogtool redact --input:$InputPath --recurse --in-place ` + @optionalParams + + if ($LastExitCode -ne 0) { + Write-PipelineTelemetryError -Category 'Redactor' -Type 'warning' -Message "Problems using Redactor tool (exit code: $LastExitCode). But ignoring them now." + } + } + finally { + Pop-Location + } + + Write-Host 'done.' +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Category 'Redactor' -Message "There was an error while trying to redact logs. Error: $_" + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/trigger-subscriptions.ps1 b/eng/common/post-build/trigger-subscriptions.ps1 index 55dea518ac..ac9a95778f 100644 --- a/eng/common/post-build/trigger-subscriptions.ps1 +++ b/eng/common/post-build/trigger-subscriptions.ps1 @@ -2,7 +2,7 @@ param( [Parameter(Mandatory=$true)][string] $SourceRepo, [Parameter(Mandatory=$true)][int] $ChannelId, [Parameter(Mandatory=$true)][string] $MaestroApiAccessToken, - [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro-prod.westus2.cloudapp.azure.com', + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$false)][string] $MaestroApiVersion = '2019-01-16' ) diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 42017109f3..3b25fd9792 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -48,8 +48,8 @@ jobs: - group: AzureDevOps-Artifact-Feeds-Pats - name: runCodesignValidationInjection value: false - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates/post-build/common-variables.yml + # unconditional - needed for logs publishing (redactor tool version) + - template: /eng/common/templates/post-build/common-variables.yml pool: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) @@ -81,7 +81,7 @@ jobs: arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' /p:BuildAssetRegistryToken=$(MaestroAccessToken) - /p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com + /p:MaestroApiEndpoint=https://maestro.dot.net /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} /p:OfficialBuildId=$(Build.BuildNumber) condition: ${{ parameters.condition }} diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml index c24193acfc..4ef7bd271f 100644 --- a/eng/common/templates/post-build/common-variables.yml +++ b/eng/common/templates/post-build/common-variables.yml @@ -7,7 +7,7 @@ variables: # Default Maestro++ API Endpoint and API Version - name: MaestroApiEndPoint - value: "https://maestro-prod.westus2.cloudapp.azure.com" + value: "https://maestro.dot.net" - name: MaestroApiAccessToken value: $(MaestroAccessToken) - name: MaestroApiVersion @@ -17,6 +17,8 @@ variables: value: 3.0.0 - name: SymbolToolVersion value: 1.0.1 + - name: BinlogToolVersion + value: 1.0.8 - name: runCodesignValidationInjection value: false diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index ef720f9d78..d64236b28c 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -187,6 +187,7 @@ stages: parameters: StageLabel: 'Validation' JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) - job: displayName: SourceLink Validation diff --git a/eng/common/templates/steps/publish-logs.yml b/eng/common/templates/steps/publish-logs.yml index 88f238f36b..dadf1c464c 100644 --- a/eng/common/templates/steps/publish-logs.yml +++ b/eng/common/templates/steps/publish-logs.yml @@ -1,6 +1,9 @@ parameters: StageLabel: '' JobLabel: '' + CustomSensitiveDataList: '' + # A default - in case value from eng/common/templates/post-build/common-variables.yml is not passed + BinlogToolVersion: '1.0.8' steps: - task: Powershell@2 @@ -12,7 +15,30 @@ steps: Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ continueOnError: true condition: always() - + +- task: PowerShell@2 + displayName: Redact Logs + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml + # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # If the file exists - sensitive data for redaction will be sourced from it + # (single entry per line, lines starting with '# ' are considered comments and skipped) + arguments: -InputPath '$(Build.SourcesDirectory)/PostBuildLogs' + -BinlogToolVersion ${{parameters.BinlogToolVersion}} + -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + '$(publishing-dnceng-devdiv-code-r-build-re)' + '$(MaestroAccessToken)' + '$(dn-bot-all-orgs-artifact-feeds-rw)' + '$(akams-client-id)' + '$(akams-client-secret)' + '$(microsoft-symbol-server-pat)' + '$(symweb-symbol-server-pat)' + '$(dn-bot-all-orgs-build-rw-code-rw)' + ${{parameters.CustomSensitiveDataList}} + continueOnError: true + condition: always() + - task: PublishBuildArtifacts@1 displayName: Publish Logs inputs: diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index fdd0cbb91f..e8def7e6a8 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -158,7 +158,7 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { $env:DOTNET_MULTILEVEL_LOOKUP=0 # Disable first run since we do not need all ASP.NET packages restored. - $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 + $env:DOTNET_NOLOGO=1 # Disable telemetry on CI. if ($ci) { @@ -228,7 +228,7 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { Write-PipelinePrependPath -Path $dotnetRoot Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0' - Write-PipelineSetVariable -Name 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE' -Value '1' + Write-PipelineSetVariable -Name 'DOTNET_NOLOGO' -Value '1' return $global:_DotNetInstallDir = $dotnetRoot } diff --git a/eng/common/tools.sh b/eng/common/tools.sh index e8d4789433..e98daf50c6 100644 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -112,7 +112,7 @@ function InitializeDotNetCli { export DOTNET_MULTILEVEL_LOOKUP=0 # Disable first run since we want to control all package sources - export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 + export DOTNET_NOLOGO=1 # Disable telemetry on CI if [[ $ci == true ]]; then @@ -165,7 +165,7 @@ function InitializeDotNetCli { Write-PipelinePrependPath -path "$dotnet_root" Write-PipelineSetVariable -name "DOTNET_MULTILEVEL_LOOKUP" -value "0" - Write-PipelineSetVariable -name "DOTNET_SKIP_FIRST_TIME_EXPERIENCE" -value "1" + Write-PipelineSetVariable -name "DOTNET_NOLOGO" -value "1" # return value _InitializeDotNetCli="$dotnet_root" @@ -310,7 +310,7 @@ function GetDotNetInstallScript { curl "$install_script_url" -sSL --retry 10 --create-dirs -o "$install_script" || { if command -v openssl &> /dev/null; then echo "Curl failed; dumping some information about dotnet.microsoft.com for later investigation" - echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 + echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 || true fi echo "Will now retry the same URL with verbose logging." with_retries curl "$install_script_url" -sSL --verbose --retry 10 --create-dirs -o "$install_script" || { @@ -341,7 +341,12 @@ function InitializeBuildTool { # return values _InitializeBuildTool="$_InitializeDotNetCli/dotnet" _InitializeBuildToolCommand="msbuild" - _InitializeBuildToolFramework="net8.0" + # use override if it exists - commonly set by source-build + if [[ "${_OverrideArcadeInitializeBuildToolFramework:-x}" == "x" ]]; then + _InitializeBuildToolFramework="net8.0" + else + _InitializeBuildToolFramework="${_OverrideArcadeInitializeBuildToolFramework}" + fi } # Set RestoreNoCache as a workaround for https://github.com/NuGet/Home/issues/3116 diff --git a/global.json b/global.json index 1ec9e591ed..6fa551e305 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "tools": { - "dotnet": "8.0.100-rtm.23506.1", + "dotnet": "8.0.100", "runtimes": { "dotnet": [ "$(MicrosoftNETCoreApp60Version)", @@ -16,6 +16,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.5.0", - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.23518.2" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.23607.2" } } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs index 2f7282e0ae..cee810569c 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Linq; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -22,7 +24,7 @@ public class CrashInfoService : ICrashInfoService /// public const uint FAST_FAIL_EXCEPTION_DOTNET_AOT = 0x48; - public sealed class CrashInfoJson + private sealed class CrashInfoJson { [JsonPropertyName("version")] public string Version { get; set; } @@ -48,10 +50,10 @@ public sealed class CrashInfoJson public string Message { get; set; } [JsonPropertyName("exception")] - public CrashInfoException Exception { get; set; } + public ExceptionJson Exception { get; set; } } - public sealed class CrashInfoException : IManagedException + private sealed class ExceptionJson { [JsonPropertyName("address")] [JsonConverter(typeof(HexUInt64Converter))] @@ -68,17 +70,13 @@ public sealed class CrashInfoException : IManagedException public string Type { get; set; } [JsonPropertyName("stack")] - public CrashInfoStackFrame[] Stack { get; set; } - - IEnumerable IManagedException.Stack => Stack; + public StackFrameJson[] Stack { get; set; } [JsonPropertyName("inner")] - public CrashInfoException[] InnerExceptions { get; set; } - - IEnumerable IManagedException.InnerExceptions => InnerExceptions; + public ExceptionJson[] InnerExceptions { get; set; } } - public sealed class CrashInfoStackFrame : IStackFrame + private sealed class StackFrameJson { [JsonPropertyName("ip")] [JsonConverter(typeof(HexUInt64Converter))] @@ -100,7 +98,7 @@ public sealed class CrashInfoStackFrame : IStackFrame public string MethodName { get; set; } } - public sealed class HexUInt64Converter : JsonConverter + private sealed class HexUInt64Converter : JsonConverter { public override ulong Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -117,7 +115,7 @@ public override ulong Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe public override void Write(Utf8JsonWriter writer, ulong value, JsonSerializerOptions options) => throw new NotImplementedException(); } - public sealed class HexUInt32Converter : JsonConverter + private sealed class HexUInt32Converter : JsonConverter { public override uint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -134,7 +132,90 @@ public override uint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSer public override void Write(Utf8JsonWriter writer, uint value, JsonSerializerOptions options) => throw new NotImplementedException(); } - public static ICrashInfoService Create(uint hresult, ReadOnlySpan triageBuffer) + private sealed class CrashInfoException : IException + { + private readonly ExceptionJson _exception; + private readonly IModuleService _moduleService; + private IStack _stack; + private IEnumerable _inner; + + public CrashInfoException(ExceptionJson exception, IModuleService moduleService) + { + Debug.Assert(exception != null); + Debug.Assert(moduleService != null); + _exception = exception; + _moduleService = moduleService; + } + + public ulong Address => _exception.Address; + + public uint HResult => _exception.HResult; + + public string Message => _exception.Message; + + public string Type => _exception.Type; + + public IStack Stack => _stack ??= new CrashInfoStack(_exception.Stack, _moduleService); + + public IEnumerable InnerExceptions => _inner ??= _exception.InnerExceptions != null ? _exception.InnerExceptions.Select((inner) => new CrashInfoException(inner, _moduleService)) : Array.Empty(); + } + + private sealed class CrashInfoStack : IStack + { + private readonly IStackFrame[] _stackFrames; + + public CrashInfoStack(StackFrameJson[] stackFrames, IModuleService moduleService) + { + _stackFrames = stackFrames != null ? stackFrames.Select((frame) => new CrashInfoStackFrame(frame, moduleService)).ToArray() : Array.Empty(); + } + + public int FrameCount => _stackFrames.Length; + + public IStackFrame GetStackFrame(int index) => _stackFrames[index]; + } + + private sealed class CrashInfoStackFrame : IStackFrame + { + private readonly StackFrameJson _stackFrame; + private readonly IModuleService _moduleService; + + public CrashInfoStackFrame(StackFrameJson stackFrame, IModuleService moduleService) + { + Debug.Assert(stackFrame != null); + Debug.Assert(moduleService != null); + _stackFrame = stackFrame; + _moduleService = moduleService; + } + + public ulong InstructionPointer => _stackFrame.InstructionPointer; + + public ulong StackPointer => _stackFrame.StackPointer; + + public ulong ModuleBase => _stackFrame.ModuleBase; + + public void GetMethodName(out string moduleName, out string methodName, out ulong displacement) + { + moduleName = null; + methodName = _stackFrame.MethodName; + displacement = _stackFrame.Offset; + + if (ModuleBase != 0) + { + IModule module = _moduleService.GetModuleFromBaseAddress(_stackFrame.ModuleBase); + if (module != null) + { + moduleName = Path.GetFileNameWithoutExtension(module.FileName); + if (string.IsNullOrEmpty(methodName)) + { + IModuleSymbols moduleSymbols = module.Services.GetService(); + moduleSymbols?.TryGetSymbolName(_stackFrame.InstructionPointer, out methodName, out displacement); + } + } + } + } + } + + public static ICrashInfoService Create(uint hresult, ReadOnlySpan triageBuffer, IModuleService moduleService) { CrashInfoService crashInfoService = null; try @@ -145,7 +226,7 @@ public static ICrashInfoService Create(uint hresult, ReadOnlySpan triageBu { if (Version.TryParse(crashInfo.Version, out Version protocolVersion) && protocolVersion.Major >= 1) { - crashInfoService = new(crashInfo.Thread, hresult, crashInfo); + crashInfoService = new(crashInfo.Thread, hresult, crashInfo, moduleService); } else { @@ -157,14 +238,16 @@ public static ICrashInfoService Create(uint hresult, ReadOnlySpan triageBu Trace.TraceError($"CrashInfoService: JsonSerializer.Deserialize failed"); } } - catch (Exception ex) when (ex is JsonException or NotSupportedException or DecoderFallbackException or ArgumentException) + catch (Exception ex) when (ex is System.Text.Json.JsonException or NotSupportedException or DecoderFallbackException or ArgumentException) { Trace.TraceError($"CrashInfoService: {ex}"); } return crashInfoService; } - private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo) + private readonly IException _exception; + + private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo, IModuleService moduleService) { ThreadId = threadId; HResult = hresult; @@ -173,7 +256,7 @@ private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo) RuntimeVersion = crashInfo.RuntimeVersion; RuntimeType = (RuntimeType)crashInfo.RuntimeType; Message = crashInfo.Message; - Exception = crashInfo.Exception; + _exception = crashInfo.Exception != null ? new CrashInfoException(crashInfo.Exception, moduleService) : null; } #region ICrashInfoService @@ -192,7 +275,60 @@ private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo) public string Message { get; } - public IManagedException Exception { get; } + public IException GetException(ulong address) + { + // Only supports getting the "current" exception or the crash exception. + if (address != 0) + { + if (address != _exception.Address) + { + throw new ArgumentOutOfRangeException(nameof(address)); + } + } + return _exception; + } + + public IException GetThreadException(uint threadId) + { + // Only supports getting the "current" exception or the crash thread's exception + if (threadId != 0) + { + if (threadId != ThreadId) + { + throw new ArgumentOutOfRangeException(nameof(threadId)); + } + } + return _exception; + } + + public IEnumerable GetNestedExceptions(uint threadId) + { + // Only supports getting the "current" exception or the crash thread's exception + if (threadId != 0) + { + if (threadId != ThreadId) + { + throw new ArgumentOutOfRangeException(nameof(threadId)); + } + } + + List exceptions = new() { + _exception + }; + + AddExceptions(_exception.InnerExceptions); + + void AddExceptions(IEnumerable inner) + { + foreach (IException exception in inner) + { + exceptions.Add(exception); + AddExceptions(exception.InnerExceptions); + } + } + + return exceptions; + } #endregion } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs index e7fad8aead..075636ec9c 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs @@ -65,7 +65,7 @@ public override Version GetVersionData() { if (InitializeValue(Module.Flags.InitializeVersion)) { - if (!_moduleInfo.Version.Equals(Utilities.EmptyVersion)) + if (_moduleInfo.Version != null && !_moduleInfo.Version.Equals(Utilities.EmptyVersion)) { _version = _moduleInfo.Version; } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs index 32d7989a93..74a5093b9e 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs @@ -102,7 +102,7 @@ public static ICrashInfoService CreateCrashInfoService(IServiceProvider services Span buffer = new byte[triageBufferSize]; if (services.GetService().ReadMemory(triageBufferAddress, buffer, out int bytesRead) && bytesRead == triageBufferSize) { - return CrashInfoService.Create(hresult, buffer); + return CrashInfoService.Create(hresult, buffer, services.GetService()); } else { diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ThreadService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ThreadService.cs index 4c89c0c83a..de9149330d 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/ThreadService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/ThreadService.cs @@ -59,6 +59,12 @@ public ThreadService(IServiceProvider services) contextType = typeof(ArmContext); break; + case (Architecture)9 /* Architecture.RiscV64 */: + _contextSize = RiscV64Context.Size; + _contextFlags = RiscV64Context.ContextControl | RiscV64Context.ContextInteger | RiscV64Context.ContextFloatingPoint; + contextType = typeof(RiscV64Context); + break; + default: throw new PlatformNotSupportedException($"Unsupported architecture: {Target.Architecture}"); } diff --git a/src/Microsoft.Diagnostics.DebugServices/CommandBase.cs b/src/Microsoft.Diagnostics.DebugServices/CommandBase.cs index e843fd9879..39c0508fae 100644 --- a/src/Microsoft.Diagnostics.DebugServices/CommandBase.cs +++ b/src/Microsoft.Diagnostics.DebugServices/CommandBase.cs @@ -91,7 +91,7 @@ protected void WriteLineError(string format, params object[] args) /// protected bool TryParseAddress(string addressInHexa, out ulong address) { - if (addressInHexa == null) + if (string.IsNullOrWhiteSpace(addressInHexa)) { address = 0; return false; @@ -105,6 +105,13 @@ protected bool TryParseAddress(string addressInHexa, out ulong address) addressInHexa = addressInHexa.TrimStart('0'); + int index = addressInHexa.IndexOf('`'); + if (index >= 0 && index < addressInHexa.Length - 1) + { + // Remove up to one instance of ` since that's what WinDbg adds to its x64 addresses. + addressInHexa = addressInHexa.Substring(0, index) + addressInHexa.Substring(index + 1); + } + return ulong.TryParse(addressInHexa, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out address); } diff --git a/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs b/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs index 1d04cab520..143532cdc3 100644 --- a/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs +++ b/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; namespace Microsoft.Diagnostics.DebugServices { @@ -57,8 +58,27 @@ public interface ICrashInfoService string Message { get; } /// - /// The exception that caused the crash or null + /// The exception at the address /// - IManagedException Exception { get; } + /// address of exception object or 0 for current exception + /// exception or null if none + /// invalid exception address + IException GetException(ulong address); + + /// + /// Returns the thread's current exception. + /// + /// OS thread id + /// exception or null if none + /// invalid thread id + IException GetThreadException(uint threadId); + + /// + /// Returns all the thread's nested exception. + /// + /// OS thread id + /// array of nested exceptions + /// invalid thread id + IEnumerable GetNestedExceptions(uint threadId); } } diff --git a/src/Microsoft.Diagnostics.DebugServices/IManagedException.cs b/src/Microsoft.Diagnostics.DebugServices/IException.cs similarity index 83% rename from src/Microsoft.Diagnostics.DebugServices/IManagedException.cs rename to src/Microsoft.Diagnostics.DebugServices/IException.cs index f73b172e5c..13c3493d48 100644 --- a/src/Microsoft.Diagnostics.DebugServices/IManagedException.cs +++ b/src/Microsoft.Diagnostics.DebugServices/IException.cs @@ -8,7 +8,7 @@ namespace Microsoft.Diagnostics.DebugServices /// /// Describes a managed exception /// - public interface IManagedException + public interface IException { /// /// Exception object address @@ -31,13 +31,13 @@ public interface IManagedException uint HResult { get; } /// - /// Stack trace of exception + /// Stack trace of exception or null /// - IEnumerable Stack { get; } + IStack Stack { get; } /// /// The inner exception or exceptions in the AggregateException case /// - IEnumerable InnerExceptions { get; } + IEnumerable InnerExceptions { get; } } } diff --git a/src/Microsoft.Diagnostics.DebugServices/IStack.cs b/src/Microsoft.Diagnostics.DebugServices/IStack.cs new file mode 100644 index 0000000000..b8d0a77e3b --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/IStack.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Diagnostics.DebugServices +{ + /// + /// Native or managed stack + /// + public interface IStack + { + /// + /// Number of total stack frames + /// + int FrameCount { get; } + + /// + /// Get an individual stack frame + /// + /// stack frame index + /// frame + /// invalid index + IStackFrame GetStackFrame(int index); + } +} diff --git a/src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs b/src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs index 42f33eb7f6..2fd8a6e7d1 100644 --- a/src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs +++ b/src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs @@ -21,16 +21,14 @@ public interface IStackFrame /// /// The module base of the IP /// - public ulong ModuleBase { get; } + ulong ModuleBase { get; } /// - /// Offset from beginning of method + /// Returns the module, method name and displacement /// - uint Offset { get; } - - /// - /// The exception type name - /// - string MethodName { get; } + /// the module name of the method or null + /// the method name or null + /// the offset from the beginning of the function + void GetMethodName(out string moduleName, out string methodName, out ulong displacement); } } diff --git a/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj index e550154f29..90fc3ce2ff 100644 --- a/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj +++ b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj @@ -1,10 +1,9 @@ - + netstandard2.0 true ;1591;1701 Diagnostics debug services - 7.0.0 true Diagnostic $(Description) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs index 67c69b3aed..41ad047e6c 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -15,17 +16,12 @@ public class CrashInfoCommand : CommandBase [ServiceImport(Optional = true)] public ICrashInfoService CrashInfo { get; set; } - [ServiceImport] - public IModuleService ModuleService { get; set; } - public override void Invoke() { if (CrashInfo == null) { throw new DiagnosticsException("No crash info to display"); } - WriteLine(); - WriteLine($"CrashReason: {CrashInfo.CrashReason}"); WriteLine($"ThreadId: {CrashInfo.ThreadId:X4}"); WriteLine($"HResult: {CrashInfo.HResult:X4}"); @@ -34,47 +30,64 @@ public override void Invoke() WriteLine($"RuntimeVersion: {CrashInfo.RuntimeVersion}"); WriteLine($"Message: {CrashInfo.Message}"); - if (CrashInfo.Exception != null) + WriteLine(); + WriteLine("** Current Exception **"); + WriteLine(); + IException exception = CrashInfo.GetException(0); + if (exception != null) + { + WriteLine("-----------------------------------------------"); + PrintException(exception, string.Empty); + } + + WriteLine(); + WriteLine($"** Thread {CrashInfo.ThreadId} Exception **"); + WriteLine(); + exception = CrashInfo.GetThreadException(CrashInfo.ThreadId); + if (exception != null) + { + WriteLine("-----------------------------------------------"); + PrintException(exception, string.Empty); + } + + WriteLine(); + WriteLine("** Nested Exceptions **"); + WriteLine(); + IEnumerable exceptions = CrashInfo.GetNestedExceptions(CrashInfo.ThreadId); + foreach (IException ex in exceptions) { WriteLine("-----------------------------------------------"); - PrintException(CrashInfo.Exception, string.Empty); + PrintException(ex, string.Empty); } } - private void PrintException(IManagedException exception, string indent) + private void PrintException(IException exception, string indent) { WriteLine($"{indent}Exception object: {exception.Address:X16}"); WriteLine($"{indent}Exception type: {exception.Type}"); WriteLine($"{indent}HResult: {exception.HResult:X8}"); WriteLine($"{indent}Message: {exception.Message}"); - if (exception.Stack != null && exception.Stack.Any()) + IStack stack = exception.Stack; + if (stack.FrameCount > 0) { WriteLine($"{indent}StackTrace:"); WriteLine($"{indent} IP Function"); - foreach (IStackFrame frame in exception.Stack) + for (int index = 0; index < stack.FrameCount; index++) { - string moduleName = ""; - if (frame.ModuleBase != 0) - { - IModule module = ModuleService.GetModuleFromBaseAddress(frame.ModuleBase); - if (module != null) - { - moduleName = Path.GetFileName(module.FileName); - } - } - string methodName = frame.MethodName ?? ""; - WriteLine($"{indent} {frame.InstructionPointer:X16} {moduleName}!{methodName} + 0x{frame.Offset:X}"); + IStackFrame frame = stack.GetStackFrame(index); + frame.GetMethodName(out string moduleName, out string methodName, out ulong displacement); + WriteLine($"{indent} {frame.InstructionPointer:X16} {moduleName ?? ""}!{methodName ?? ""} + 0x{displacement:X}"); } } - if (exception.InnerExceptions != null) + if (exception.InnerExceptions.Any()) { - WriteLine("InnerExceptions:"); - foreach (IManagedException inner in exception.InnerExceptions) + WriteLine($"{indent}InnerExceptions:"); + foreach (IException inner in exception.InnerExceptions) { - WriteLine("-----------------------------------------------"); - PrintException(inner, " "); + WriteLine($"{indent}-----------------------------------------------"); + PrintException(inner, $"{indent} "); } } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs index 38aa12c773..d23e68e5b0 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs @@ -62,6 +62,9 @@ public class DumpHeapCommand : ClrRuntimeCommandBase [Option(Name = "-gen")] public string Generation { get; set; } + [Option(Name = "-ignoreGCState", Help = "Ignore the GC's marker that the heap is not walkable (will generate lots of false positive errors).")] + public bool IgnoreGCState { get; set; } + [Argument(Help = "Optional memory ranges in the form of: [Start [End]]")] public string[] MemoryRange { get; set; } @@ -165,6 +168,11 @@ public override void Invoke() private void ParseArguments() { + if (!Runtime.Heap.CanWalkHeap && !IgnoreGCState) + { + throw new DiagnosticsException("The GC heap is not in a valid state for traversal. (Use -ignoreGCState to override.)"); + } + if (Live && Dead) { Live = false; @@ -189,9 +197,13 @@ private void ParseArguments() FilteredHeap.GCHeap = GCHeap; } - if (!string.IsNullOrWhiteSpace(Segment)) + if (TryParseAddress(Segment, out ulong segment)) + { + FilteredHeap.FilterBySegmentHex(segment); + } + else if (!string.IsNullOrWhiteSpace(Segment)) { - FilteredHeap.FilterBySegmentHex(Segment); + throw new DiagnosticsException($"Failed to parse segment '{Segment}'."); } if (MemoryRange is not null && MemoryRange.Length > 0) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs index 2a211c1eb5..a6f94d055b 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs @@ -41,7 +41,7 @@ public override void Invoke() } MemoryRange range; - if (Bounds is null || Bounds.Length == 0) + if (Bounds is not null || Bounds.Length == 0) { range = GetStackRange(); } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs index 59fb9fc3ce..85daeabb62 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs @@ -54,9 +54,13 @@ public override void Invoke() HeapWithFilters.GCHeap = GCHeap; } - if (!string.IsNullOrWhiteSpace(Segment)) + if (TryParseAddress(Segment, out ulong segment)) { - HeapWithFilters.FilterBySegmentHex(Segment); + HeapWithFilters.FilterBySegmentHex(segment); + } + else if (!string.IsNullOrWhiteSpace(Segment)) + { + throw new DiagnosticsException($"Failed to parse segment '{Segment}'."); } if (MemoryRange is not null) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs index 1cf459013a..28b5ab35b6 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs @@ -38,6 +38,9 @@ public class GCRootCommand : ClrRuntimeCommandBase [Argument(Name = "target")] public string TargetAddress { get; set; } + [Option(Name = "-limit", Help = "Limits the amount of roots to find")] + public int? Limit { get; set; } + public override void Invoke() { if (!TryParseAddress(TargetAddress, out ulong address)) @@ -58,6 +61,8 @@ public override void Invoke() }); int count; + int limit = Limit ?? int.MaxValue; + if (AsGCGeneration.HasValue) { int gen = AsGCGeneration.Value; @@ -81,32 +86,32 @@ public override void Invoke() // If not gen0 or gen1, treat it as a normal !gcroot if (NoStacks) { - count = PrintNonStackRoots(gcroot); + count = PrintNonStackRoots(gcroot, limit); } else { - count = PrintAllRoots(gcroot); + count = PrintAllRoots(gcroot, limit); } } else { - count = PrintOlderGenerationRoots(gcroot, gen); - count += PrintNonStackRoots(gcroot); + count = PrintOlderGenerationRoots(gcroot, gen, limit); + count += PrintNonStackRoots(gcroot, limit); } } else if (NoStacks) { - count = PrintNonStackRoots(gcroot); + count = PrintNonStackRoots(gcroot, limit); } else { - count = PrintAllRoots(gcroot); + count = PrintAllRoots(gcroot, limit); } Console.WriteLine($"Found {count:n0} unique roots."); } - private int PrintOlderGenerationRoots(GCRoot gcroot, int gen) + private int PrintOlderGenerationRoots(GCRoot gcroot, int gen, int limit) { int count = 0; @@ -125,6 +130,11 @@ private int PrintOlderGenerationRoots(GCRoot gcroot, int gen) ulong address = internalRootArray.Start; while (internalRootArray.Contains(address)) { + if (count >= limit) + { + break; + } + Console.CancellationToken.ThrowIfCancellationRequested(); if (Memory.ReadPointer(address, out ulong objAddress)) @@ -167,11 +177,16 @@ private int PrintOlderGenerationRoots(GCRoot gcroot, int gen) return count; } - private int PrintAllRoots(GCRoot gcroot) + private int PrintAllRoots(GCRoot gcroot, int limit) { int count = 0; foreach (ClrRoot root in RootCache.EnumerateRoots()) { + if (count >= limit) + { + break; + } + Console.CancellationToken.ThrowIfCancellationRequested(); GCRoot.ChainLink item = gcroot.FindPathFrom(root.Object); if (item is not null) @@ -184,11 +199,16 @@ private int PrintAllRoots(GCRoot gcroot) return count; } - private int PrintNonStackRoots(GCRoot gcroot) + private int PrintNonStackRoots(GCRoot gcroot, int limit) { int count = 0; foreach (ClrRoot root in RootCache.GetHandleRoots()) { + if (count >= limit) + { + break; + } + Console.CancellationToken.ThrowIfCancellationRequested(); GCRoot.ChainLink item = gcroot.FindPathFrom(root.Object); if (item is not null) @@ -200,6 +220,11 @@ private int PrintNonStackRoots(GCRoot gcroot) foreach (ClrRoot root in RootCache.GetFinalizerQueueRoots()) { + if (count >= limit) + { + break; + } + Console.CancellationToken.ThrowIfCancellationRequested(); GCRoot.ChainLink item = gcroot.FindPathFrom(root.Object); if (item is not null) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/HeapWithFilters.cs b/src/Microsoft.Diagnostics.ExtensionCommands/HeapWithFilters.cs index b8abc37782..36c229157a 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/HeapWithFilters.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/HeapWithFilters.cs @@ -85,19 +85,9 @@ public HeapWithFilters(ClrHeap heap) SortSubHeaps = (heap) => heap.OrderBy(heap => heap.Index); } - public void FilterBySegmentHex(string segmentStr) + public void FilterBySegmentHex(ulong segment) { - if (!ulong.TryParse(segmentStr, NumberStyles.HexNumber, null, out ulong segment)) - { - throw new ArgumentException($"Invalid segment address: {segmentStr}"); - } - - if (ThrowIfNoMatchingGCRegions && !_heap.Segments.Any(seg => seg.Address == segment || seg.CommittedMemory.Contains(segment))) - { - throw new ArgumentException($"No segments match address: {segment:x}"); - } - - Segment = segment; + Segment = segment != 0 ? segment : null; } public void FilterByStringMemoryRange(string[] memoryRange, string commandName) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs index c65f381fa4..6906ff4f9e 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Diagnostics.DebugServices; using Microsoft.Diagnostics.Runtime; @@ -26,45 +27,88 @@ public class ObjSizeCommand : ClrRuntimeCommandBase [Argument] public string ObjectAddress { get; set; } + [Option(Name = "-mt")] + public string MethodTable { get; set; } + public override void Invoke() { - if (!TryParseAddress(ObjectAddress, out ulong objAddress)) + IEnumerable objects; + + if (!string.IsNullOrEmpty(MethodTable)) { - throw new ArgumentException($"Could not parse target object address: {objAddress:x}"); - } + if (!TryParseAddress(MethodTable, out ulong methodTable)) + { + throw new ArgumentException($"Could not parse method table: {MethodTable}"); + } + + objects = Runtime.Heap.EnumerateObjects().Where(obj => obj.Type?.MethodTable == methodTable); - ClrObject obj = Runtime.Heap.GetObject(objAddress); - if (!obj.IsValid) + Console.Write("Objects which "); + Console.WriteLine($"({Runtime.Heap.GetTypeByMethodTable(methodTable)?.Name ?? ""}) transitively keep alive:"); + Console.WriteLine(); + } + else { - Console.WriteLine($"{objAddress:x} is not a valid object"); - return; + if (!TryParseAddress(ObjectAddress, out ulong objAddress)) + { + throw new ArgumentException($"Could not parse target object address: {objAddress:x}"); + } + + ClrObject obj = Runtime.Heap.GetObject(objAddress); + if (!obj.IsValid) + { + Console.WriteLine($"{objAddress:x} is not a valid object"); + return; + } + + objects = new[] { obj }; + + Console.Write("Objects which "); + Console.WriteDmlExec(obj.Address.ToString("x"), $"!dumpobj {obj.Address:x}"); + Console.WriteLine($" ({obj.Type?.Name ?? ""}) transitively keep alive:"); + Console.WriteLine(); } - Console.Write($"Objects which "); - Console.WriteDmlExec(obj.Address.ToString("x"), $"!dumpobj {obj.Address:x}"); - Console.WriteLine($"({obj.Type?.Name ?? " "}) transitively keep alive:"); - Console.WriteLine(); + DumpHeapService.DisplayKind displayKind; + if (Strings) + { + displayKind = DumpHeapService.DisplayKind.Strings; + } + else if (Short) + { + displayKind = DumpHeapService.DisplayKind.Short; + } + else + { + displayKind = DumpHeapService.DisplayKind.Normal; + } - DumpHeapService.DisplayKind displayKind = Strings ? DumpHeapService.DisplayKind.Strings : DumpHeapService.DisplayKind.Normal; - DumpHeap.PrintHeap(GetTransitiveClosure(obj), displayKind, Stat, printFragmentation: false); + DumpHeap.PrintHeap(GetTransitiveClosure(objects), displayKind, Stat, printFragmentation: false); } - private static IEnumerable GetTransitiveClosure(ClrObject obj) + private static IEnumerable GetTransitiveClosure(IEnumerable objects) { - HashSet seen = new() { obj }; + HashSet seen = new(); Queue queue = new(); - queue.Enqueue(obj); - while (queue.Count > 0) + foreach (ClrObject obj in objects) { - ClrObject parent = queue.Dequeue(); - yield return parent; - - foreach (ClrObject child in parent.EnumerateReferences()) + if (obj.IsValid && seen.Add(obj)) { - if (child.IsValid && seen.Add(child)) + queue.Enqueue(obj); + + while (queue.Count > 0) { - queue.Enqueue(child); + ClrObject parent = queue.Dequeue(); + yield return parent; + + foreach (ClrObject child in parent.EnumerateReferences()) + { + if (child.IsValid && seen.Add(child)) + { + queue.Enqueue(child); + } + } } } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacks.Runtime/ParallelStack.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacks.Runtime/ParallelStack.cs index f2653bb8df..c3287f0cdc 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacks.Runtime/ParallelStack.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacks.Runtime/ParallelStack.cs @@ -11,7 +11,7 @@ namespace ParallelStacks.Runtime { public class ParallelStack { - public static ParallelStack Build(ClrRuntime runtime) + public static ParallelStack Build(ClrRuntime runtime, bool includeRuntimeStacks) { ParallelStack ps = new(); List stackFrames = new(64); @@ -20,7 +20,11 @@ public static ParallelStack Build(ClrRuntime runtime) stackFrames.Clear(); foreach (ClrStackFrame stackFrame in thread.EnumerateStackTrace().Reverse()) { - if ((stackFrame.Kind != ClrStackFrameKind.ManagedMethod) || (stackFrame.Method == null)) + bool shouldAdd = + stackFrame.Kind == ClrStackFrameKind.ManagedMethod + || (includeRuntimeStacks && stackFrame.Kind == ClrStackFrameKind.Runtime); + + if ((!shouldAdd) || (stackFrame.Method == null)) { continue; } @@ -39,7 +43,7 @@ public static ParallelStack Build(ClrRuntime runtime) return ps; } - public static ParallelStack Build(string dumpFile, string dacFilePath) + public static ParallelStack Build(string dumpFile, string dacFilePath, bool includeRuntimeStacks) { DataTarget dataTarget = null; ParallelStack ps = null; @@ -63,7 +67,7 @@ public static ParallelStack Build(string dumpFile, string dacFilePath) return null; } - ps = ParallelStack.Build(runtime); + ps = ParallelStack.Build(runtime, includeRuntimeStacks); } finally { @@ -73,7 +77,7 @@ public static ParallelStack Build(string dumpFile, string dacFilePath) return ps; } - public static ParallelStack Build(int pid, string dacFilePath) + public static ParallelStack Build(int pid, string dacFilePath, bool includeRuntimeStacks) { DataTarget dataTarget = null; ParallelStack ps = null; @@ -100,7 +104,7 @@ public static ParallelStack Build(int pid, string dacFilePath) return null; } - ps = ParallelStack.Build(runtime); + ps = ParallelStack.Build(runtime, includeRuntimeStacks); } finally { diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs index 3320f11d34..0b9a987e47 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs @@ -17,9 +17,12 @@ public class ParallelStacksCommand : ClrMDHelperCommandBase [Option(Name = "--allthreads", Aliases = new string[] { "-a" }, Help = "Displays all threads per group instead of at most 4 by default.")] public bool AllThreads { get; set; } + [Option(Name = "--runtime", Aliases = new string[] { "-r" }, Help = "Displays runtime stacks in addition to managed ones.")] + public bool IncludeRuntimeStacks { get; set; } + public override void Invoke() { - ParallelStack ps = ParallelStack.Build(Runtime); + ParallelStack ps = ParallelStack.Build(Runtime, IncludeRuntimeStacks); if (ps == null) { return; diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs index c7b7b19578..a7645b4d82 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs @@ -25,6 +25,9 @@ public class VerifyHeapCommand : ClrRuntimeCommandBase [Option(Name = "-segment")] public string Segment { get; set; } + [Option(Name = "-ignoreGCState", Help = "Ignore the GC's marker that the heap is not walkable (will generate lots of false positive errors).")] + public bool IgnoreGCState { get; set; } + [Argument(Help ="Optional memory ranges in the form of: [Start [End]]")] public string[] MemoryRange { get; set; } @@ -36,9 +39,13 @@ public override void Invoke() filteredHeap.GCHeap = GCHeap; } - if (!string.IsNullOrWhiteSpace(Segment)) + if (TryParseAddress(Segment, out ulong segment)) + { + filteredHeap.FilterBySegmentHex(segment); + } + else if (!string.IsNullOrWhiteSpace(Segment)) { - filteredHeap.FilterBySegmentHex(Segment); + throw new DiagnosticsException($"Failed to parse segment '{Segment}'."); } if (MemoryRange is not null) @@ -46,6 +53,11 @@ public override void Invoke() filteredHeap.FilterByStringMemoryRange(MemoryRange, CommandName); } + if (!Runtime.Heap.CanWalkHeap && !IgnoreGCState) + { + throw new DiagnosticsException("The GC heap is not in a valid state for traversal. (Use -ignoreGCState to override.)"); + } + VerifyHeap(filteredHeap.EnumerateFilteredObjects(Console.CancellationToken), verifySyncTable: filteredHeap.HasFilters); } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventMonitor.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventMonitor.cs index 147167f5c6..5e773d2394 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventMonitor.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventMonitor.cs @@ -24,6 +24,8 @@ internal sealed class EventMonitor : IAsyncDisposable private readonly bool _leaveEventStreamOpen; private EventPipeEventSource _eventSource; + private TraceEventParser _eventParser; + private readonly string _providerName; private readonly string _eventName; @@ -78,8 +80,16 @@ public Task ProcessAsync(CancellationToken token) token.ThrowIfCancellationRequested(); using IDisposable registration = token.Register(() => _eventSource.Dispose()); - _eventSource.Dynamic.AddCallbackForProviderEvent(_providerName, _eventName, TraceEventCallback); + if (string.Equals(_providerName, "Microsoft-Windows-DotNETRuntime", StringComparison.Ordinal)) + { + _eventParser = _eventSource.Clr; + } + else + { + _eventParser = _eventSource.Dynamic; + } + _eventParser.AddCallbackForProviderEvent(_providerName, _eventName, TraceEventCallback); _eventSource.Process(); token.ThrowIfCancellationRequested(); }, token); @@ -91,7 +101,7 @@ public Task ProcessAsync(CancellationToken token) /// private void StopMonitoringForEvent() { - _eventSource?.Dynamic.RemoveCallback(TraceEventCallback); + _eventParser?.RemoveCallback(TraceEventCallback); } private void TraceEventCallback(TraceEvent obj) diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj index c104003875..4558b2ed0c 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj @@ -40,6 +40,7 @@ + diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs index 8084d0af8e..564ade9990 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs @@ -19,6 +19,8 @@ namespace Microsoft.Diagnostics.NETCore.Client /// public sealed class DiagnosticsClient { + private const int DefaultCircularBufferMB = 256; + private readonly IpcEndpoint _endpoint; public DiagnosticsClient(int processId) : @@ -66,9 +68,10 @@ internal Task WaitForConnectionAsync(CancellationToken token) /// /// An EventPipeSession object representing the EventPipe session that just started. /// - public EventPipeSession StartEventPipeSession(IEnumerable providers, bool requestRundown = true, int circularBufferMB = 256) + public EventPipeSession StartEventPipeSession(IEnumerable providers, bool requestRundown = true, int circularBufferMB = DefaultCircularBufferMB) { - return EventPipeSession.Start(_endpoint, providers, requestRundown, circularBufferMB); + EventPipeSessionConfiguration config = new(providers, circularBufferMB, requestRundown: requestRundown, requestStackwalk: true); + return EventPipeSession.Start(_endpoint, config); } /// @@ -80,9 +83,10 @@ public EventPipeSession StartEventPipeSession(IEnumerable pro /// /// An EventPipeSession object representing the EventPipe session that just started. /// - public EventPipeSession StartEventPipeSession(EventPipeProvider provider, bool requestRundown = true, int circularBufferMB = 256) + public EventPipeSession StartEventPipeSession(EventPipeProvider provider, bool requestRundown = true, int circularBufferMB = DefaultCircularBufferMB) { - return EventPipeSession.Start(_endpoint, new[] { provider }, requestRundown, circularBufferMB); + EventPipeSessionConfiguration config = new(new[] {provider}, circularBufferMB, requestRundown: requestRundown, requestStackwalk: true); + return EventPipeSession.Start(_endpoint, config); } /// @@ -95,9 +99,11 @@ public EventPipeSession StartEventPipeSession(EventPipeProvider provider, bool r /// /// An EventPipeSession object representing the EventPipe session that just started. /// - internal Task StartEventPipeSessionAsync(IEnumerable providers, bool requestRundown, int circularBufferMB, CancellationToken token) + public Task StartEventPipeSessionAsync(IEnumerable providers, bool requestRundown, + int circularBufferMB = DefaultCircularBufferMB, CancellationToken token = default) { - return EventPipeSession.StartAsync(_endpoint, providers, requestRundown, circularBufferMB, token); + EventPipeSessionConfiguration config = new(providers, circularBufferMB, requestRundown: requestRundown, requestStackwalk: true); + return EventPipeSession.StartAsync(_endpoint, config, token); } /// @@ -110,9 +116,24 @@ internal Task StartEventPipeSessionAsync(IEnumerable /// An EventPipeSession object representing the EventPipe session that just started. /// - internal Task StartEventPipeSessionAsync(EventPipeProvider provider, bool requestRundown, int circularBufferMB, CancellationToken token) + public Task StartEventPipeSessionAsync(EventPipeProvider provider, bool requestRundown, + int circularBufferMB = DefaultCircularBufferMB, CancellationToken token = default) + { + EventPipeSessionConfiguration config = new(new[] {provider}, circularBufferMB, requestRundown: requestRundown, requestStackwalk: true); + return EventPipeSession.StartAsync(_endpoint, config, token); + } + + /// + /// Start tracing the application and return an EventPipeSession object + /// + /// Configuration of this EventPipeSession + /// The token to monitor for cancellation requests. + /// + /// An EventPipeSession object representing the EventPipe session that just started. + /// + public Task StartEventPipeSessionAsync(EventPipeSessionConfiguration configuration, CancellationToken token) { - return EventPipeSession.StartAsync(_endpoint, new[] { provider }, requestRundown, circularBufferMB, token); + return EventPipeSession.StartAsync(_endpoint, configuration, token); } /// diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs index d61aace345..355a815fe5 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs @@ -28,16 +28,16 @@ private EventPipeSession(IpcEndpoint endpoint, IpcResponse response, ulong sessi public Stream EventStream => _response.Continuation; - internal static EventPipeSession Start(IpcEndpoint endpoint, IEnumerable providers, bool requestRundown, int circularBufferMB) + internal static EventPipeSession Start(IpcEndpoint endpoint, EventPipeSessionConfiguration config) { - IpcMessage requestMessage = CreateStartMessage(providers, requestRundown, circularBufferMB); + IpcMessage requestMessage = CreateStartMessage(config); IpcResponse? response = IpcClient.SendMessageGetContinuation(endpoint, requestMessage); return CreateSessionFromResponse(endpoint, ref response, nameof(Start)); } - internal static async Task StartAsync(IpcEndpoint endpoint, IEnumerable providers, bool requestRundown, int circularBufferMB, CancellationToken cancellationToken) + internal static async Task StartAsync(IpcEndpoint endpoint, EventPipeSessionConfiguration config, CancellationToken cancellationToken) { - IpcMessage requestMessage = CreateStartMessage(providers, requestRundown, circularBufferMB); + IpcMessage requestMessage = CreateStartMessage(config); IpcResponse? response = await IpcClient.SendMessageGetContinuationAsync(endpoint, requestMessage, cancellationToken).ConfigureAwait(false); return CreateSessionFromResponse(endpoint, ref response, nameof(StartAsync)); } @@ -81,10 +81,14 @@ public async Task StopAsync(CancellationToken cancellationToken) } } - private static IpcMessage CreateStartMessage(IEnumerable providers, bool requestRundown, int circularBufferMB) + private static IpcMessage CreateStartMessage(EventPipeSessionConfiguration config) { - EventPipeSessionConfiguration config = new(circularBufferMB, EventPipeSerializationFormat.NetTrace, providers, requestRundown); - return new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)EventPipeCommandId.CollectTracing2, config.SerializeV2()); + // To keep backward compatibility with older runtimes we only use newer serialization format when needed + // V3 has added support to disable the stacktraces + bool shouldUseV3 = !config.RequestStackwalk; + EventPipeCommandId command = shouldUseV3 ? EventPipeCommandId.CollectTracing3 : EventPipeCommandId.CollectTracing2; + byte[] payload = shouldUseV3 ? config.SerializeV3() : config.SerializeV2(); + return new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)command, payload); } private static EventPipeSession CreateSessionFromResponse(IpcEndpoint endpoint, ref IpcResponse? response, string operationName) diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs index 0e837d908c..8f3f3b2b7f 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; namespace Microsoft.Diagnostics.NETCore.Client { @@ -13,9 +14,29 @@ internal enum EventPipeSerializationFormat NetTrace } - internal class EventPipeSessionConfiguration + public sealed class EventPipeSessionConfiguration { - public EventPipeSessionConfiguration(int circularBufferSizeMB, EventPipeSerializationFormat format, IEnumerable providers, bool requestRundown = true) + /// + /// Creates a new configuration object for the EventPipeSession. + /// For details, see the documentation of each property of this object. + /// + /// An IEnumerable containing the list of Providers to turn on. + /// The size of the runtime's buffer for collecting events in MB + /// If true, request rundown events from the runtime. + /// If true, record a stacktrace for every emitted event. + public EventPipeSessionConfiguration( + IEnumerable providers, + int circularBufferSizeMB = 256, + bool requestRundown = true, + bool requestStackwalk = true) : this(circularBufferSizeMB, EventPipeSerializationFormat.NetTrace, providers, requestRundown, requestStackwalk) + {} + + private EventPipeSessionConfiguration( + int circularBufferSizeMB, + EventPipeSerializationFormat format, + IEnumerable providers, + bool requestRundown, + bool requestStackwalk) { if (circularBufferSizeMB == 0) { @@ -32,39 +53,68 @@ public EventPipeSessionConfiguration(int circularBufferSizeMB, EventPipeSerializ throw new ArgumentNullException(nameof(providers)); }; + _providers = new List(providers); + if (_providers.Count == 0) + { + throw new ArgumentException("At least one provider must be specified."); + }; + CircularBufferSizeInMB = circularBufferSizeMB; Format = format; RequestRundown = requestRundown; - _providers = new List(providers); + RequestStackwalk = requestStackwalk; } + /// + /// If true, request rundown events from the runtime. + /// + /// Rundown events are needed to correctly decode the stacktrace information for dynamically generated methods. + /// Rundown happens at the end of the session. It increases the time needed to finish the session and, for large applications, may have important impact on the final trace file size. + /// Consider to set this parameter to false if you don't need stacktrace information or if you're analyzing events on the fly. + /// + /// public bool RequestRundown { get; } + + /// + /// The size of the runtime's buffer for collecting events in MB. + /// If the buffer size is too small to accommodate all in-flight events some events may be lost. + /// public int CircularBufferSizeInMB { get; } - public EventPipeSerializationFormat Format { get; } + /// + /// If true, record a stacktrace for every emitted event. + /// + /// The support of this parameter only comes with NET 9. Before, the stackwalk is always enabled and if this property is set to false the connection attempt will fail. + /// Disabling the stackwalk makes event collection overhead considerably less + /// Note that some events may choose to omit the stacktrace regardless of this parameter, specifically the events emitted from the native runtime code. + /// If the stacktrace collection is disabled application-wide (using the env variable DOTNET_EventPipeEnableStackwalk) this parameter is ignored. + /// + /// + public bool RequestStackwalk { get; } + + /// + /// Providers to enable for this session. + /// public IReadOnlyCollection Providers => _providers.AsReadOnly(); private readonly List _providers; - public byte[] SerializeV2() + internal EventPipeSerializationFormat Format { get; } + } + + internal static class EventPipeSessionConfigurationExtensions + { + public static byte[] SerializeV2(this EventPipeSessionConfiguration config) { byte[] serializedData = null; using (MemoryStream stream = new()) using (BinaryWriter writer = new(stream)) { - writer.Write(CircularBufferSizeInMB); - writer.Write((uint)Format); - writer.Write(RequestRundown); + writer.Write(config.CircularBufferSizeInMB); + writer.Write((uint)config.Format); + writer.Write(config.RequestRundown); - writer.Write(Providers.Count); - foreach (EventPipeProvider provider in Providers) - { - writer.Write(unchecked((ulong)provider.Keywords)); - writer.Write((uint)provider.EventLevel); - - writer.WriteString(provider.Name); - writer.WriteString(provider.GetArgumentString()); - } + SerializeProviders(config, writer); writer.Flush(); serializedData = stream.ToArray(); @@ -73,6 +123,36 @@ public byte[] SerializeV2() return serializedData; } + public static byte[] SerializeV3(this EventPipeSessionConfiguration config) + { + byte[] serializedData = null; + using (MemoryStream stream = new()) + using (BinaryWriter writer = new(stream)) + { + writer.Write(config.CircularBufferSizeInMB); + writer.Write((uint)config.Format); + writer.Write(config.RequestRundown); + writer.Write(config.RequestStackwalk); + + SerializeProviders(config, writer); + writer.Flush(); + serializedData = stream.ToArray(); + } + + return serializedData; + } + + private static void SerializeProviders(EventPipeSessionConfiguration config, BinaryWriter writer) + { + writer.Write(config.Providers.Count); + foreach (EventPipeProvider provider in config.Providers) + { + writer.Write(unchecked((ulong)provider.Keywords)); + writer.Write((uint)provider.EventLevel); + writer.WriteString(provider.Name); + writer.WriteString(provider.GetArgumentString()); + } + } } } diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs index f9eb3019ef..b9d6ea30a9 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs @@ -25,6 +25,7 @@ internal enum EventPipeCommandId : byte StopTracing = 0x01, CollectTracing = 0x02, CollectTracing2 = 0x03, + CollectTracing3 = 0x04, } internal enum DumpCommandId : byte diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcTransport.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcTransport.cs index 6789b0aedf..7d4fb0a909 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcTransport.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcTransport.cs @@ -329,7 +329,14 @@ public static string GetDefaultAddress(int pid) if (!TryGetDefaultAddress(pid, out string defaultAddress)) { - throw new ServerNotAvailableException($"Process {pid} not running compatible .NET runtime."); + string msg = $"Unable to connect to Process {pid}."; + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + msg += $" Please verify that {IpcRootPath} is writable by the current user. " + + "If the target process has environment variable TMPDIR set, please set TMPDIR to the same directory. " + + "Please see https://aka.ms/dotnet-diagnostics-port for more information"; + } + throw new ServerNotAvailableException(msg); } return defaultAddress; diff --git a/src/SOS/SOS.Extensions/Clrma/ExceptionWrapper.cs b/src/SOS/SOS.Extensions/Clrma/ExceptionWrapper.cs new file mode 100644 index 0000000000..7380306a69 --- /dev/null +++ b/src/SOS/SOS.Extensions/Clrma/ExceptionWrapper.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Diagnostics.DebugServices; +using Microsoft.Diagnostics.Runtime.Utilities; + +namespace SOS.Extensions.Clrma +{ + public sealed class ExceptionWrapper : COMCallableIUnknown + { + public static readonly Guid IID_ICLRMAClrException = new("7C165652-D539-472e-A6CF-F657FFF31751"); + + public IException Exception { get; } + + public IntPtr ICLRMACClrException { get; } + + private ExceptionWrapper[] _innerExceptions; + + public ExceptionWrapper(IException exception) + { + Debug.Assert(exception != null); + Exception = exception; + + VTableBuilder builder = AddInterface(IID_ICLRMAClrException, validate: false); + builder.AddMethod(new DebuggerCommandDelegate(DebuggerCommand)); + builder.AddMethod(new AddressDelegate(GetAddress)); + builder.AddMethod(new HResultDelegate(GetHResult)); + builder.AddMethod(new TypeDelegate(GetType)); + builder.AddMethod(new MessageDelegate(GetMessage)); + builder.AddMethod(new FrameCountDelegate(FrameCount)); + builder.AddMethod(new FrameDelegate(Frame)); + builder.AddMethod(new InnerExceptionCountDelegate(InnerExceptionCount)); + builder.AddMethod(new InnerExceptionDelegate(InnerException)); + ICLRMACClrException = builder.Complete(); + + AddRef(); + } + + protected override void Destroy() + { + Trace.TraceInformation("ExceptionWrapper.Destroy"); + } + + private int DebuggerCommand( + IntPtr self, + out string command) + { + command = null; + return HResult.S_FALSE; + } + + private int GetAddress( + IntPtr self, + out ulong address) + { + address = Exception.Address; + return HResult.S_OK; + } + + private int GetHResult( + IntPtr self, + out uint hresult) + { + hresult = Exception.HResult; + return HResult.S_OK; + } + + private int GetType( + IntPtr self, + out string type) + { + type = Exception.Type; + return HResult.S_OK; + } + + private int GetMessage( + IntPtr self, + out string message) + { + message = Exception.Message; + return HResult.S_OK; + } + + private int FrameCount( + IntPtr self, + out int count) + { + count = Exception.Stack.FrameCount; + return count > 0 ? HResult.S_OK : HResult.S_FALSE; + } + + private int Frame( + IntPtr self, + int nFrame, + out ulong ip, + out ulong sp, + out string moduleName, + out string functionName, + out ulong displacement) + { + ip = 0; + sp = 0; + moduleName = null; + functionName = null; + displacement = 0; + IStackFrame frame; + try + { + frame = Exception.Stack.GetStackFrame(nFrame); + } + catch (ArgumentOutOfRangeException) + { + return ManagedAnalysisWrapper.E_BOUNDS; + } + ip = frame.InstructionPointer; + sp = frame.StackPointer; + frame.GetMethodName(out moduleName, out functionName, out displacement); + moduleName ??= $"module_{frame.ModuleBase:X16}"; + functionName ??= $"function_{frame.InstructionPointer:X16}"; + return HResult.S_OK; + } + + private int InnerExceptionCount( + IntPtr self, + out ushort count) + { + count = (ushort)InnerExceptions.Length; + return count > 0 ? HResult.S_OK : HResult.S_FALSE; + } + + private int InnerException( + IntPtr self, + ushort index, + out IntPtr clrmaClrException) + { + clrmaClrException = IntPtr.Zero; + if (index >= InnerExceptions.Length) + { + return ManagedAnalysisWrapper.E_BOUNDS; + } + ExceptionWrapper exception = InnerExceptions[index]; + exception.AddRef(); + clrmaClrException = exception.ICLRMACClrException; + return HResult.S_OK; + } + + private ExceptionWrapper[] InnerExceptions + { + get { return _innerExceptions ??= Exception.InnerExceptions.Select((exception) => new ExceptionWrapper(exception)).ToArray(); } + } + + #region ICLRMAClrException delegates + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int DebuggerCommandDelegate( + [In] IntPtr self, + [Out, MarshalAs(UnmanagedType.BStr)] out string command); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int AddressDelegate( + [In] IntPtr self, + [Out] out ulong address); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int HResultDelegate( + [In] IntPtr self, + [Out] out uint hresult); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int TypeDelegate( + [In] IntPtr self, + [Out, MarshalAs(UnmanagedType.BStr)] out string type); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int MessageDelegate( + [In] IntPtr self, + [Out, MarshalAs(UnmanagedType.BStr)] out string message); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int FrameCountDelegate( + [In] IntPtr self, + [Out] out int count); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int FrameDelegate( + [In] IntPtr self, + [In] int nFrame, + [Out] out ulong ip, + [Out] out ulong sp, + [Out, MarshalAs(UnmanagedType.BStr)] out string moduleName, + [Out, MarshalAs(UnmanagedType.BStr)] out string functionName, + [Out] out ulong displacement); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int InnerExceptionCountDelegate( + [In] IntPtr self, + [Out] out ushort count); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int InnerExceptionDelegate( + [In] IntPtr self, + [In] ushort index, + [Out] out IntPtr clrmaClrException); + + #endregion + } +} diff --git a/src/SOS/SOS.Extensions/Clrma/ManagedAnalysisWrapper.cs b/src/SOS/SOS.Extensions/Clrma/ManagedAnalysisWrapper.cs new file mode 100644 index 0000000000..9e4fb309aa --- /dev/null +++ b/src/SOS/SOS.Extensions/Clrma/ManagedAnalysisWrapper.cs @@ -0,0 +1,245 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Diagnostics.DebugServices; +using Microsoft.Diagnostics.Runtime.Utilities; +using SOS.Hosting; +using SOS.Hosting.DbgEng.Interop; + +namespace SOS.Extensions.Clrma +{ + public sealed class ManagedAnalysisWrapper : COMCallableIUnknown + { + public static readonly Guid IID_ICLRManagedAnalysis = new("8CA73A16-C017-4c8f-AD51-B758727478CA"); + + public const int E_BOUNDS = unchecked((int)0x8000000B); + public const uint DEBUG_ANY_ID = uint.MaxValue; + + private readonly IServiceProvider _serviceProvider; + private readonly ServiceWrapper _serviceWrapper; + private ICrashInfoService _crashInfoService; + private IDebugClient _debugClient; + private ThreadWrapper _thread; + private ExceptionWrapper _exception; + + public ManagedAnalysisWrapper(ITarget target, IServiceProvider serviceProvider, ServiceWrapper serviceWrapper) + { + _serviceProvider = serviceProvider; + _serviceWrapper = serviceWrapper; + + target.OnFlushEvent.Register(Flush); + + VTableBuilder builder = AddInterface(IID_ICLRManagedAnalysis, validate: false); + builder.AddMethod(new GetProviderNameDelegate(GetProviderName)); + builder.AddMethod(new AssociateClientDelegate(AssociateClient)); + builder.AddMethod(new GetThreadDelegate(GetThread)); + builder.AddMethod(new GetExceptionDelegate(GetException)); + builder.AddMethod(new ObjectInspectionDelegate(ObjectInspection)); + builder.Complete(); + // Since this wrapper is only created through a ServiceWrapper factory, no AddRef() is needed. + } + + protected override void Destroy() + { + Trace.TraceInformation("ManagedAnalysisWrapper.Destroy"); + _serviceWrapper.RemoveServiceWrapper(ManagedAnalysisWrapper.IID_ICLRManagedAnalysis); + Flush(); + } + + private void Flush() + { + _crashInfoService = null; + FlushDebugClient(); + FlushThread(); + FlushException(); + } + + private void FlushDebugClient() + { + if (_debugClient != null) + { + int count = Marshal.ReleaseComObject(_debugClient); + Debug.Assert(count >= 0); + _debugClient = null; + } + } + + private void FlushThread() + { + _thread?.ReleaseWithCheck(); + _thread = null; + } + + private void FlushException() + { + _exception?.ReleaseWithCheck(); + _exception = null; + } + + private int GetProviderName( + IntPtr self, + out string provider) + { + provider = "SOSCLRMA"; + return HResult.S_OK; + } + + private int AssociateClient( + IntPtr self, + IntPtr punk) + { + // If the crash info service doesn't exist, then tell Watson/CLRMA to go on to the next provider + if (CrashInfoService is null) + { + return HResult.E_NOINTERFACE; + } + FlushDebugClient(); + _debugClient = Marshal.GetObjectForIUnknown(punk) as IDebugClient; + if (_debugClient == null) + { + return HResult.E_NOINTERFACE; + } + // We don't currently need the IDebugClient instance passed this this function. + return HResult.S_OK; + } + + private int GetThread( + IntPtr self, + uint osThreadId, + out IntPtr clrmaClrThread) + { + clrmaClrThread = IntPtr.Zero; + if (CrashInfoService is null) + { + return HResult.E_FAIL; + } + // osThreadId == 0 is current thread and -1 is "last event thread". The only thread + // information we have currently is the crashing thread id so always return it. + if (osThreadId == 0) + { + HResult hr = ((IDebugSystemObjects)_debugClient).GetCurrentThreadSystemId(out osThreadId); + if (!hr.IsOK) + { + return hr; + } + } + else if (osThreadId == uint.MaxValue) + { + HResult hr = ((IDebugControl)_debugClient).GetLastEventInformation( + out DEBUG_EVENT _, + out uint _, + out uint threadIndex, + IntPtr.Zero, + 0, + out uint _, + null, + 0, + out uint _); + + if (!hr.IsOK) + { + return hr; + } + if (threadIndex == DEBUG_ANY_ID) + { + return HResult.E_INVALIDARG; + } + uint[] ids = new uint[1]; + uint[] sysIds = new uint[1]; + hr = ((IDebugSystemObjects)_debugClient).GetThreadIdsByIndex(threadIndex, 1, ids, sysIds); + if (!hr.IsOK) + { + return hr; + } + osThreadId = sysIds[0]; + } + if (_thread is null || _thread.ThreadId != osThreadId) + { + IThread thread = ThreadService?.GetThreadFromId(osThreadId); + if (thread is null) + { + return HResult.E_INVALIDARG; + } + FlushThread(); + _thread = new ThreadWrapper(CrashInfoService, thread); + } + _thread.AddRef(); + clrmaClrThread = _thread.ICLRMACClrThread; + return HResult.S_OK; + } + + private int GetException( + IntPtr self, + ulong address, + out IntPtr clrmaClrException) + { + clrmaClrException = IntPtr.Zero; + if (_exception is null || _exception.Exception.Address != address) + { + IException exception = null; + try + { + exception = CrashInfoService?.GetException(address); + } + catch (ArgumentOutOfRangeException) + { + } + if (exception is null) + { + return HResult.E_INVALIDARG; + } + FlushException(); + _exception = new ExceptionWrapper(exception); + } + _exception.AddRef(); + clrmaClrException = _exception.ICLRMACClrException; + return HResult.S_OK; + } + + private int ObjectInspection( + IntPtr self, + out IntPtr clrmaObjectInspection) + { + clrmaObjectInspection = IntPtr.Zero; + return HResult.E_NOTIMPL; + } + + private ICrashInfoService CrashInfoService => _crashInfoService ??= _serviceProvider.GetService(); + + private IThreadService ThreadService => _serviceProvider.GetService(); + + #region ICLRManagedAnalysis delegates + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int GetProviderNameDelegate( + [In] IntPtr self, + [Out, MarshalAs(UnmanagedType.BStr)] out string provider); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int AssociateClientDelegate( + [In] IntPtr self, + [In] IntPtr punk); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int GetThreadDelegate( + [In] IntPtr self, + [In] uint osThreadId, + [Out] out IntPtr clrmaClrThread); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int GetExceptionDelegate( + [In] IntPtr self, + [In] ulong address, + [Out] out IntPtr clrmaClrException); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int ObjectInspectionDelegate( + [In] IntPtr self, + [Out] out IntPtr objectInspection); + + #endregion + } +} diff --git a/src/SOS/SOS.Extensions/Clrma/ThreadWrapper.cs b/src/SOS/SOS.Extensions/Clrma/ThreadWrapper.cs new file mode 100644 index 0000000000..5c797ea201 --- /dev/null +++ b/src/SOS/SOS.Extensions/Clrma/ThreadWrapper.cs @@ -0,0 +1,208 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Diagnostics.DebugServices; +using Microsoft.Diagnostics.Runtime.Utilities; +using SOS.Hosting; + +namespace SOS.Extensions.Clrma +{ + public sealed class ThreadWrapper : COMCallableIUnknown + { + public static readonly Guid IID_ICLRMAClrThread = new("9849CFC9-0868-406e-9059-6B04E9ADBBB8"); + + public uint ThreadId => _thread.ThreadId; + + public IntPtr ICLRMACClrThread { get; } + + private readonly ICrashInfoService _crashInfoService; + private readonly IThread _thread; + private ExceptionWrapper _currentException; + private ExceptionWrapper[] _nestedExceptions; + + public ThreadWrapper(ICrashInfoService crashInfoService, IThread thread) + { + Debug.Assert(crashInfoService != null); + _crashInfoService = crashInfoService; + _thread = thread; + + VTableBuilder builder = AddInterface(IID_ICLRMAClrThread, validate: false); + builder.AddMethod(new DebuggerCommandDelegate(DebuggerCommand)); + builder.AddMethod(new OSThreadIdDelegate(OSThreadId)); + builder.AddMethod(new FrameCountDelegate(FrameCount)); + builder.AddMethod(new FrameDelegate(Frame)); + builder.AddMethod(new CurrentExceptionDelegate(CurrentException)); + builder.AddMethod(new NestedExceptionCountDelegate(NestedExceptionCount)); + builder.AddMethod(new NestedExceptionDelegate(NestedException)); + ICLRMACClrThread = builder.Complete(); + + AddRef(); + } + + protected override void Destroy() + { + Trace.TraceInformation("ThreadWrapper.Destroy"); + _currentException?.ReleaseWithCheck(); + _currentException = null; + } + + private int DebuggerCommand( + IntPtr self, + out string command) + { + command = null; + return HResult.S_FALSE; + } + + private int OSThreadId( + IntPtr self, + out uint osThreadId) + { + osThreadId = ThreadId; + return osThreadId > 0 ? HResult.S_OK : HResult.S_FALSE; + } + + private int FrameCount( + IntPtr self, + out int count) + { + count = 0; + return HResult.E_NOTIMPL; + } + + private int Frame( + IntPtr self, + int nFrame, + out ulong ip, + out ulong sp, + out string moduleName, + out string functionName, + out ulong displacement) + { + ip = 0; + sp = 0; + moduleName = null; + functionName = null; + displacement = 0; + return HResult.E_NOTIMPL; + } + + private int CurrentException( + IntPtr self, + out IntPtr clrmaClrException) + { + clrmaClrException = IntPtr.Zero; + if (_currentException is null) + { + IException exception = null; + try + { + exception = _crashInfoService.GetThreadException(ThreadId); + } + catch (ArgumentOutOfRangeException) + { + } + if (exception is null) + { + return HResult.S_FALSE; + } + _currentException ??= new ExceptionWrapper(exception); + } + _currentException.AddRef(); + clrmaClrException = _currentException.ICLRMACClrException; + return HResult.S_OK; + } + + private int NestedExceptionCount( + IntPtr self, + out ushort count) + { + count = (ushort)NestedExceptions.Length; + return count > 0 ? HResult.S_OK : HResult.S_FALSE; + } + + private int NestedException( + IntPtr self, + ushort index, + out IntPtr clrmaClrException) + { + clrmaClrException = IntPtr.Zero; + if (index >= NestedExceptions.Length) + { + return ManagedAnalysisWrapper.E_BOUNDS; + } + ExceptionWrapper exception = NestedExceptions[index]; + exception.AddRef(); + clrmaClrException = exception.ICLRMACClrException; + return HResult.S_OK; + } + + private ExceptionWrapper[] NestedExceptions + { + get + { + if (_nestedExceptions is null) + { + try + { + _nestedExceptions = _crashInfoService.GetNestedExceptions(ThreadId).Select((exception) => new ExceptionWrapper(exception)).ToArray(); + } + catch (ArgumentOutOfRangeException) + { + _nestedExceptions = Array.Empty(); + } + } + return _nestedExceptions; + } + } + + #region ICLRMAClrThread delegates + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int DebuggerCommandDelegate( + [In] IntPtr self, + [Out, MarshalAs(UnmanagedType.BStr)] out string command); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int OSThreadIdDelegate( + [In] IntPtr self, + [Out] out uint osThreadId); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int FrameCountDelegate( + [In] IntPtr self, + [Out] out int count); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int FrameDelegate( + [In] IntPtr self, + [In] int nFrame, + [Out] out ulong ip, + [Out] out ulong sp, + [Out, MarshalAs(UnmanagedType.BStr)] out string moduleName, + [Out, MarshalAs(UnmanagedType.BStr)] out string functionName, + [Out] out ulong displacement); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int CurrentExceptionDelegate( + [In] IntPtr self, + [Out] out IntPtr clrmaClrException); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int NestedExceptionCountDelegate( + [In] IntPtr self, + [Out] out ushort count); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int NestedExceptionDelegate( + [In] IntPtr self, + [In] ushort index, + [Out] out IntPtr clrmaClrException); + + #endregion + } +} diff --git a/src/SOS/SOS.Extensions/DebuggerServices.cs b/src/SOS/SOS.Extensions/DebuggerServices.cs index b5702a7a82..9f4fe231f1 100644 --- a/src/SOS/SOS.Extensions/DebuggerServices.cs +++ b/src/SOS/SOS.Extensions/DebuggerServices.cs @@ -16,7 +16,7 @@ namespace SOS.Extensions { - internal sealed unsafe class DebuggerServices : CallableCOMWrapper, SOSHost.INativeClient + internal sealed unsafe class DebuggerServices : CallableCOMWrapper, SOSHost.INativeDebugger { internal enum OperatingSystem { @@ -41,7 +41,6 @@ internal DebuggerServices(IntPtr punk, HostType hostType) : base(new RefCountedFreeLibrary(IntPtr.Zero), IID_IDebuggerServices, punk) { _hostType = hostType; - Client = punk; // This uses COM marshalling code, so we also check that the OSPlatform is Windows. if (hostType == HostType.DbgEng && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -54,9 +53,20 @@ internal DebuggerServices(IntPtr punk, HostType hostType) } } - #region INativeClient + #region SOSHost.INativeDebugger - public IntPtr Client { get; } + public IntPtr GetNativeClient() + { + if (_hostType == HostType.DbgEng) + { + return QueryInterface(typeof(IDebugClient).GUID); + } + else if (_hostType == HostType.Lldb) + { + return QueryInterface(LLDBServices.IID_ILLDBServices); + } + throw new InvalidOperationException($"DebuggerServices.GetNativeClient: invalid host type {_hostType}"); + } #endregion diff --git a/src/SOS/SOS.Extensions/HostServices.cs b/src/SOS/SOS.Extensions/HostServices.cs index 9ee85d1942..c5c042ce55 100644 --- a/src/SOS/SOS.Extensions/HostServices.cs +++ b/src/SOS/SOS.Extensions/HostServices.cs @@ -223,7 +223,7 @@ private int RegisterDebuggerServices( _serviceContainer.AddService(_serviceManager); _serviceContainer.AddService(this); _serviceContainer.AddService(this); - _serviceContainer.AddService(DebuggerServices); + _serviceContainer.AddService(DebuggerServices); _serviceContainer.AddService(_commandService); _serviceContainer.AddService(_symbolService); _serviceContainer.AddService(fileLoggingConsoleService); diff --git a/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs b/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs index e0c2bee85b..fd4a8648c4 100644 --- a/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs +++ b/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs @@ -7,6 +7,7 @@ using Microsoft.Diagnostics.DebugServices; using Microsoft.Diagnostics.DebugServices.Implementation; using Microsoft.Diagnostics.Runtime.Utilities; +using SOS.Extensions.Clrma; using SOS.Hosting; using SOS.Hosting.DbgEng.Interop; using Architecture = System.Runtime.InteropServices.Architecture; @@ -54,6 +55,7 @@ internal TargetFromDebuggerServices(DebuggerServices debuggerServices, IHost hos IMAGE_FILE_MACHINE.THUMB2 => Architecture.Arm, IMAGE_FILE_MACHINE.AMD64 => Architecture.X64, IMAGE_FILE_MACHINE.ARM64 => Architecture.Arm64, + IMAGE_FILE_MACHINE.RISCV64 => (Architecture)9 /* Architecture.RiscV64 */, _ => throw new PlatformNotSupportedException($"Machine type not supported: {type}"), }; } @@ -105,6 +107,9 @@ internal TargetFromDebuggerServices(DebuggerServices debuggerServices, IHost hos } Finished(); + + TargetWrapper targetWrapper = Services.GetService(); + targetWrapper?.ServiceWrapper.AddServiceWrapper(ManagedAnalysisWrapper.IID_ICLRManagedAnalysis, () => new ManagedAnalysisWrapper(this, Services, targetWrapper.ServiceWrapper)); } private unsafe ICrashInfoService CreateCrashInfoService(IServiceProvider services, DebuggerServices debuggerServices) @@ -128,7 +133,7 @@ private unsafe ICrashInfoService CreateCrashInfoService(IServiceProvider service Span buffer = new byte[triageBufferSize]; if (services.GetService().ReadMemory(triageBufferAddress, buffer, out int bytesRead) && bytesRead == triageBufferSize) { - return CrashInfoService.Create(hresult, buffer); + return CrashInfoService.Create(hresult, buffer, services.GetService()); } else { diff --git a/src/SOS/SOS.Hosting/CorDebugDataTargetWrapper.cs b/src/SOS/SOS.Hosting/CorDebugDataTargetWrapper.cs index 5e69c47532..c46e8f1591 100644 --- a/src/SOS/SOS.Hosting/CorDebugDataTargetWrapper.cs +++ b/src/SOS/SOS.Hosting/CorDebugDataTargetWrapper.cs @@ -107,6 +107,9 @@ private int GetPlatform( case Architecture.Arm64: platform = CorDebugPlatform.CORDB_PLATFORM_POSIX_ARM64; break; + case (Architecture)9 /* Architecture.RiscV64 */: + platform = CorDebugPlatform.CORDB_PLATFORM_POSIX_RISCV64; + break; default: return HResult.E_FAIL; } diff --git a/src/SOS/SOS.Hosting/CorDebugPlatform.cs b/src/SOS/SOS.Hosting/CorDebugPlatform.cs index 61111bb3f4..9609ea8b23 100644 --- a/src/SOS/SOS.Hosting/CorDebugPlatform.cs +++ b/src/SOS/SOS.Hosting/CorDebugPlatform.cs @@ -19,6 +19,8 @@ public enum CorDebugPlatform CORDB_PLATFORM_POSIX_AMD64 = 8, CORDB_PLATFORM_POSIX_X86 = 9, CORDB_PLATFORM_POSIX_ARM = 10, - CORDB_PLATFORM_POSIX_ARM64 = 11 + CORDB_PLATFORM_POSIX_ARM64 = 11, + CORDB_PLATFORM_POSIX_LOONGARCH64 = 12, + CORDB_PLATFORM_POSIX_RISCV64 = 13 } } diff --git a/src/SOS/SOS.Hosting/DataTargetWrapper.cs b/src/SOS/SOS.Hosting/DataTargetWrapper.cs index 303e88d825..f61cbd4310 100644 --- a/src/SOS/SOS.Hosting/DataTargetWrapper.cs +++ b/src/SOS/SOS.Hosting/DataTargetWrapper.cs @@ -112,6 +112,7 @@ private int GetMachineType( Architecture.X86 => IMAGE_FILE_MACHINE.I386, Architecture.Arm => IMAGE_FILE_MACHINE.THUMB2, Architecture.Arm64 => IMAGE_FILE_MACHINE.ARM64, + (Architecture)9 /* Architecture.RiscV64 */ => IMAGE_FILE_MACHINE.RISCV64, _ => IMAGE_FILE_MACHINE.UNKNOWN, }; return HResult.S_OK; diff --git a/src/SOS/SOS.Hosting/DbgEng/Interop/Enums/ImageFileMachine.cs b/src/SOS/SOS.Hosting/DbgEng/Interop/Enums/ImageFileMachine.cs index ebda53b006..cb9ed62a24 100644 --- a/src/SOS/SOS.Hosting/DbgEng/Interop/Enums/ImageFileMachine.cs +++ b/src/SOS/SOS.Hosting/DbgEng/Interop/Enums/ImageFileMachine.cs @@ -35,6 +35,7 @@ public enum IMAGE_FILE_MACHINE : uint AMD64 = 0x8664, // AMD64 (K8) M32R = 0x9041, // M32R little-endian ARM64 = 0xAA64, // ARM64 Little-endian - CEE = 0xC0EE + CEE = 0xC0EE, + RISCV64 = 0x5064 } } diff --git a/src/SOS/SOS.Hosting/LLDBServices.cs b/src/SOS/SOS.Hosting/LLDBServices.cs index 5ac39d92a0..1e6b127090 100644 --- a/src/SOS/SOS.Hosting/LLDBServices.cs +++ b/src/SOS/SOS.Hosting/LLDBServices.cs @@ -13,10 +13,10 @@ namespace SOS.Hosting { - internal sealed unsafe class LLDBServices : COMCallableIUnknown + public sealed unsafe class LLDBServices : COMCallableIUnknown { - private static readonly Guid IID_ILLDBServices = new("2E6C569A-9E14-4DA4-9DFC-CDB73A532566"); - private static readonly Guid IID_ILLDBServices2 = new("012F32F0-33BA-4E8E-BC01-037D382D8A5E"); + public static readonly Guid IID_ILLDBServices = new("2E6C569A-9E14-4DA4-9DFC-CDB73A532566"); + public static readonly Guid IID_ILLDBServices2 = new("012F32F0-33BA-4E8E-BC01-037D382D8A5E"); public IntPtr ILLDBServices { get; } diff --git a/src/SOS/SOS.Hosting/SOSHost.cs b/src/SOS/SOS.Hosting/SOSHost.cs index 0fe569a0fa..5d23b98c3f 100644 --- a/src/SOS/SOS.Hosting/SOSHost.cs +++ b/src/SOS/SOS.Hosting/SOSHost.cs @@ -24,12 +24,12 @@ public sealed class SOSHost : IDisposable /// /// Provides the native debugger's debug client instance /// - public interface INativeClient + public interface INativeDebugger { - /// - /// Native debugger client interface - /// - IntPtr Client { get; } + /// + /// Get the native SOS client for commands (IDebugClient under dbgeng, ILLDBServices under lldb) + /// + public IntPtr GetNativeClient(); } // This is what dbgeng/IDebuggerServices returns for non-PE modules that don't have a timestamp @@ -58,21 +58,20 @@ public interface INativeClient private readonly IntPtr _client; private readonly ulong _ignoreAddressBitsMask; - private readonly bool _releaseClient; /// /// Create an instance of the hosting class. Has the lifetime of the target. /// - public SOSHost(ITarget target, IMemoryService memoryService, [ServiceImport(Optional = true)] INativeClient client) + public SOSHost(ITarget target, IMemoryService memoryService, [ServiceImport(Optional = true)] INativeDebugger nativeDebugger) { Target = target; MemoryService = memoryService; _ignoreAddressBitsMask = memoryService.SignExtensionMask(); // If running under a native debugger, use the client instance supplied by the debugger for commands - if (client != null) + if (nativeDebugger != null) { - _client = client.Client; + _client = nativeDebugger.GetNativeClient(); } else { @@ -86,17 +85,14 @@ public SOSHost(ITarget target, IMemoryService memoryService, [ServiceImport(Opti LLDBServices lldbServices = new(this); _client = lldbServices.ILLDBServices; } - _releaseClient = true; } + Debug.Assert(_client != IntPtr.Zero); } void IDisposable.Dispose() { Trace.TraceInformation($"SOSHost.Dispose"); - if (_releaseClient) - { - ComWrapper.ReleaseWithCheck(_client); - } + ComWrapper.ReleaseWithCheck(_client); } /// @@ -183,6 +179,9 @@ internal unsafe int GetExecutingProcessorType( case Architecture.Arm64: *type = IMAGE_FILE_MACHINE.ARM64; break; + case (Architecture)9 /* Architecture.RiscV64 */: + *type = IMAGE_FILE_MACHINE.RISCV64; + break; default: *type = IMAGE_FILE_MACHINE.UNKNOWN; break; diff --git a/src/SOS/SOS.Hosting/SOSLibrary.cs b/src/SOS/SOS.Hosting/SOSLibrary.cs index 42e1f3e425..c8d4fc70af 100644 --- a/src/SOS/SOS.Hosting/SOSLibrary.cs +++ b/src/SOS/SOS.Hosting/SOSLibrary.cs @@ -17,7 +17,7 @@ namespace SOS.Hosting public sealed class SOSLibrary : IDisposable { /// - /// Provides the SOS module handle + /// Provides the SOS module path and handle /// public interface ISOSModule { diff --git a/src/SOS/SOS.Hosting/ServiceWrapper.cs b/src/SOS/SOS.Hosting/ServiceWrapper.cs index 125fda0994..78300591ea 100644 --- a/src/SOS/SOS.Hosting/ServiceWrapper.cs +++ b/src/SOS/SOS.Hosting/ServiceWrapper.cs @@ -25,6 +25,7 @@ public void Dispose() wrapper.ReleaseWithCheck(); } _wrappers.Clear(); + _factories.Clear(); } /// @@ -48,12 +49,11 @@ public void AddServiceWrapper(in Guid serviceId, COMCallableIUnknown service) } /// - /// Remove the service instance + /// Remove the service instance (doesn't release it) but not the factory. /// /// guid public void RemoveServiceWrapper(in Guid serviceId) { - _factories.Remove(serviceId); _wrappers.Remove(serviceId); } diff --git a/src/SOS/SOS.Hosting/TargetWrapper.cs b/src/SOS/SOS.Hosting/TargetWrapper.cs index daf13d8519..8960df57c0 100644 --- a/src/SOS/SOS.Hosting/TargetWrapper.cs +++ b/src/SOS/SOS.Hosting/TargetWrapper.cs @@ -30,11 +30,12 @@ private enum OperatingSystem private readonly ITarget _target; private readonly IContextService _contextService; - public TargetWrapper(ITarget target, IContextService contextService, ISymbolService symbolService, IMemoryService memoryService) + public TargetWrapper( + ITarget target, + IContextService contextService, + ISymbolService symbolService, + IMemoryService memoryService) { - Debug.Assert(target != null); - Debug.Assert(contextService != null); - Debug.Assert(symbolService != null); _target = target; _contextService = contextService; @@ -62,7 +63,6 @@ void IDisposable.Dispose() protected override void Destroy() { Trace.TraceInformation("TargetWrapper.Destroy"); - ServiceWrapper.RemoveServiceWrapper(SymbolServiceWrapper.IID_ISymbolService); ServiceWrapper.Dispose(); } diff --git a/src/SOS/Strike/CMakeLists.txt b/src/SOS/Strike/CMakeLists.txt index 52456d7387..19551ab0a8 100644 --- a/src/SOS/Strike/CMakeLists.txt +++ b/src/SOS/Strike/CMakeLists.txt @@ -82,6 +82,7 @@ if (CLR_CMAKE_HOST_WIN32) add_definitions(-DUSE_STL) set(SOS_SOURCES + clrma.cpp disasm.cpp dllsext.cpp eeheap.cpp @@ -219,6 +220,10 @@ elseif(CLR_CMAKE_HOST_ARCH_ARM64) set(SOS_SOURCES_ARCH disasmARM64.cpp ) +elseif(CLR_CMAKE_HOST_ARCH_RISCV64) + set(SOS_SOURCES_ARCH + disasmRISCV64.cpp + ) endif() list(APPEND SOS_SOURCES ${SOS_SOURCES_ARCH}) diff --git a/src/SOS/Strike/Strike.vcxproj b/src/SOS/Strike/Strike.vcxproj index 29dde66bfa..609d90ec02 100644 --- a/src/SOS/Strike/Strike.vcxproj +++ b/src/SOS/Strike/Strike.vcxproj @@ -385,6 +385,7 @@ + diff --git a/src/SOS/Strike/Strike.vcxproj.filters b/src/SOS/Strike/Strike.vcxproj.filters index e7b14e9eaf..53a91f82dd 100644 --- a/src/SOS/Strike/Strike.vcxproj.filters +++ b/src/SOS/Strike/Strike.vcxproj.filters @@ -33,6 +33,7 @@ platform + diff --git a/src/SOS/Strike/clrma.cpp b/src/SOS/Strike/clrma.cpp new file mode 100644 index 0000000000..7ffc2fe2ad --- /dev/null +++ b/src/SOS/Strike/clrma.cpp @@ -0,0 +1,59 @@ +#include +#include +#include // IDL +#include "exts.h" + +HRESULT CLRMACreateInstance(ICLRManagedAnalysis** ppCLRMA); +HRESULT CLRMAReleaseInstance(); + +ICLRManagedAnalysis* g_managedAnalysis = nullptr; + +// +// Exports +// + +HRESULT CLRMACreateInstance(ICLRManagedAnalysis** ppCLRMA) +{ + HRESULT hr = E_UNEXPECTED; + + if (ppCLRMA == nullptr) + { + return E_INVALIDARG; + } + *ppCLRMA = nullptr; + + if (g_managedAnalysis == nullptr) + { + Extensions* extensions = Extensions::GetInstance(); + if (extensions == nullptr || extensions->GetDebuggerServices() == nullptr) + { + return E_FAIL; + } + ITarget* target = extensions->GetTarget(); + if (target == nullptr) + { + return E_FAIL; + } + hr = target->GetService(__uuidof(ICLRManagedAnalysis), (void**)&g_managedAnalysis); + if (FAILED(hr)) + { + return hr; + } + } + else + { + g_managedAnalysis->AddRef(); + } + *ppCLRMA = g_managedAnalysis; + return S_OK; +} + +HRESULT CLRMAReleaseInstance() +{ + if (g_managedAnalysis != nullptr) + { + g_managedAnalysis->Release(); + g_managedAnalysis = nullptr; + } + return S_OK; +} diff --git a/src/SOS/Strike/disasm.cpp b/src/SOS/Strike/disasm.cpp index 10590c165d..b116c72b98 100644 --- a/src/SOS/Strike/disasm.cpp +++ b/src/SOS/Strike/disasm.cpp @@ -1127,6 +1127,19 @@ LPCSTR ARM64Machine::s_SPName = "sp"; #endif // SOS_TARGET_ARM64 +#ifdef SOS_TARGET_RISCV64 +/// +/// RISCV64Machine implementation +/// +LPCSTR RISCV64Machine::s_DumpStackHeading = "ChildFP RetAddr Caller, Callee\n"; +LPCSTR RISCV64Machine::s_GCRegs[30] = {"r0", "ra", "gp", "tp", "t0", "t1", "t2", "s1", "a0", + "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", + "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", + "t3", "t4", "t5", "t6"}; +LPCSTR RISCV64Machine::s_SPName = "sp"; + +#endif // SOS_TARGET_RISCV64 + // // GCEncodingInfo class member implementations // diff --git a/src/SOS/Strike/disasm.h b/src/SOS/Strike/disasm.h index a09e5abecf..714e0fddab 100644 --- a/src/SOS/Strike/disasm.h +++ b/src/SOS/Strike/disasm.h @@ -401,6 +401,76 @@ class ARM64Machine : public IMachine }; // class ARM64Machine #endif // SOS_TARGET_ARM64 + + +#ifdef SOS_TARGET_RISCV64 + +/// RISCV64 Machine specific code +class RISCV64Machine : public IMachine +{ +public: + typedef RISCV64_CONTEXT TGT_CTXT; + + static IMachine* GetInstance() + { static RISCV64Machine s_RISCV64MachineInstance; return &s_RISCV64MachineInstance; } + + ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_RISCV64; } + ULONG GetContextSize() const { return sizeof(RISCV64_CONTEXT); } + ULONG GetFullContextFlags() const { return 0x01000007L; } + void SetContextFlags(BYTE* context, ULONG32 contextFlags) { ((RISCV64_CONTEXT*)context)->ContextFlags = contextFlags; }; + + virtual void Unassembly( + TADDR IPBegin, + TADDR IPEnd, + TADDR IPAskedFor, + TADDR GCStressCodeCopy, + GCEncodingInfo *pGCEncodingInfo, + SOSEHInfo *pEHInfo, + BOOL bSuppressLines, + BOOL bDisplayOffsets, + std::function displayIL) const; + virtual void IsReturnAddress( + TADDR retAddr, + TADDR* whereCalled) const; + virtual BOOL GetExceptionContext ( + TADDR stack, + TADDR PC, + TADDR *cxrAddr, + CROSS_PLATFORM_CONTEXT * cxr, + TADDR *exrAddr, + PEXCEPTION_RECORD exr) const; + + // retrieve stack pointer, frame pointer, and instruction pointer from the target context + virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.RiscV64Context.Sp; } + virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.RiscV64Context.Fp; } + virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.RiscV64Context.Pc; } + + virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const; + virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const; + + virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; } + virtual LPCSTR GetSPName() const { return s_SPName; } + virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const + { _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; + + int StackWalkIPAdjustOffset() const { return 4; } + +private: + RISCV64Machine() {} + ~RISCV64Machine() {} + RISCV64Machine(const RISCV64Machine& machine); // undefined + RISCV64Machine & operator=(const RISCV64Machine&); // undefined + + static LPCSTR s_DumpStackHeading; + static LPCSTR s_GCRegs[30]; + static LPCSTR s_SPName; + +}; // class RISCV64Machine + +#endif // SOS_TARGET_RISCV64 + #ifdef _MSC_VER #pragma warning(pop) #endif // _MSC_VER @@ -478,4 +548,19 @@ inline void ARM64Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int i } #endif // SOS_TARGET_ARM64 +#ifdef SOS_TARGET_RISCV64 +inline void RISCV64Machine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const +{ + TGT_CTXT& src = *(TGT_CTXT*) srcCtx; + dest->StackOffset = src.Sp; + dest->FrameOffset = src.Fp; + dest->InstructionOffset = src.Pc; +} + +inline void RISCV64Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const +{ + TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx; + *dest = *(TGT_CTXT*)srcCtx; +} +#endif // SOS_TARGET_RISCV64 #endif // __disasm_h__ diff --git a/src/SOS/Strike/disasmRISCV64.cpp b/src/SOS/Strike/disasmRISCV64.cpp new file mode 100644 index 0000000000..531fbd7dac --- /dev/null +++ b/src/SOS/Strike/disasmRISCV64.cpp @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#undef _TARGET_AMD64_ +#ifndef _TARGET_RISCV64_ +#define _TARGET_RISCV64_ +#endif + +#undef TARGET_AMD64 +#ifndef TARGET_RISCV64 +#define TARGET_RISCV64 +#endif + +#include "strike.h" +#include "util.h" +#include + +#include "disasm.h" + +#include "corhdr.h" +#include "cor.h" +#include "dacprivate.h" + +namespace RISCV64GCDump +{ +#undef TARGET_X86 +#undef LIMITED_METHOD_CONTRACT +#define LIMITED_METHOD_DAC_CONTRACT ((void)0) +#define SUPPORTS_DAC ((void)0) +#define LF_GCROOTS +#define LL_INFO1000 +#define LOG(x) +#define LOG_PIPTR(pObjRef, gcFlags, hCallBack) +#define DAC_ARG(x) +#include "gcdumpnonx86.cpp" +} + +#if !defined(_TARGET_WIN64_) +#error This file only supports SOS targeting RISCV64 from a 64-bit debugger +#endif + +#if !defined(SOS_TARGET_RISCV64) +#error This file should be used to support SOS targeting RISCV64 debuggees +#endif + + +void RISCV64Machine::IsReturnAddress(TADDR retAddr, TADDR* whereCalled) const +{ + *whereCalled = 0; + _ASSERTE("RISCV64:NYI"); +} + +// Determine if a value is MT/MD/Obj +static void HandleValue(TADDR value) +{ + // A MethodTable? + if (IsMethodTable(value)) + { + NameForMT_s (value, g_mdName,mdNameLen); + ExtOut (" (MT: %S)", g_mdName); + return; + } + + // A Managed Object? + TADDR dwMTAddr; + move_xp (dwMTAddr, value); + if (IsStringObject(value)) + { + ExtOut (" (\""); + StringObjectContent (value, TRUE); + ExtOut ("\")"); + return; + } + else if (IsMethodTable(dwMTAddr)) + { + NameForMT_s (dwMTAddr, g_mdName,mdNameLen); + ExtOut (" (Object: %S)", g_mdName); + return; + } + + // A MethodDesc? + if (IsMethodDesc(value)) + { + NameForMD_s (value, g_mdName,mdNameLen); + ExtOut (" (MD: %S)", g_mdName); + return; + } + + // A JitHelper? + const char* name = HelperFuncName(value); + if (name) { + ExtOut (" (JitHelp: %s)", name); + return; + } + + // A call to managed code? + // RISCV64TODO: not (yet) implemented. perhaps we don't need it at all. + + // Random symbol. + char Symbol[1024]; + if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(value), Symbol, 1024, + NULL, NULL))) + { + if (Symbol[0] != '\0') + { + ExtOut (" (%s)", Symbol); + return; + } + } + +} + +/**********************************************************************\ +* Routine Description: * +* * +* Unassembly a managed code. Translating managed object, * +* call. * +* * +\**********************************************************************/ +void RISCV64Machine::Unassembly ( + TADDR PCBegin, + TADDR PCEnd, + TADDR PCAskedFor, + TADDR GCStressCodeCopy, + GCEncodingInfo *pGCEncodingInfo, + SOSEHInfo *pEHInfo, + BOOL bSuppressLines, + BOOL bDisplayOffsets, + std::function displayIL) const +{ + _ASSERTE("RISCV64:NYI"); +} + +BOOL RISCV64Machine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CROSS_PLATFORM_CONTEXT * cxr, + TADDR * exrAddr, PEXCEPTION_RECORD exr) const +{ + _ASSERTE("RISCV64:NYI"); + return FALSE; +} + +/// +/// Dump RISCV GCInfo table +/// +void RISCV64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const +{ + if (bPrintHeader) + { + ExtOut("Pointer table:\n"); + } + + RISCV64GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true); + gcDump.gcPrintf = gcPrintf; + + gcDump.DumpGCTable(dac_cast(gcInfoToken.Info), methodSize, 0); +} + diff --git a/src/SOS/Strike/exts.cpp b/src/SOS/Strike/exts.cpp index 112e42bd98..2d51903063 100644 --- a/src/SOS/Strike/exts.cpp +++ b/src/SOS/Strike/exts.cpp @@ -132,6 +132,12 @@ GetTargetMachine(ULONG processorType) targetMachine = ARM64Machine::GetInstance(); } #endif // SOS_TARGET_ARM64 +#ifdef SOS_TARGET_RISCV64 + if (processorType == IMAGE_FILE_MACHINE_RISCV64) + { + targetMachine = RISCV64Machine::GetInstance(); + } +#endif // SOS_TARGET_RISCV64 return targetMachine; } @@ -161,6 +167,9 @@ ArchQuery(void) case IMAGE_FILE_MACHINE_ARM64: architecture = "arm64"; break; + case IMAGE_FILE_MACHINE_RISCV64: + architecture = "riscv64"; + break; } ExtErr("SOS does not support the current target architecture '%s' (0x%04x). A 32 bit target may require a 32 bit debugger or vice versa. In general, try to use the same bitness for the debugger and target process.\n", architecture, processorType); diff --git a/src/SOS/Strike/exts.h b/src/SOS/Strike/exts.h index 96876879a7..35a1959d96 100644 --- a/src/SOS/Strike/exts.h +++ b/src/SOS/Strike/exts.h @@ -56,6 +56,10 @@ // printing CDA values. #define CDA_TO_UL64(cda) ((ULONG64)(TO_TADDR(cda))) +#ifndef IMAGE_FILE_MACHINE_RISCV64 +#define IMAGE_FILE_MACHINE_RISCV64 0x5064 // RISCV64 +#endif // !IMAGE_FILE_MACHINE_RISCV64 + typedef struct _TADDR_RANGE { TADDR start; @@ -493,6 +497,7 @@ inline BOOL IsDbgTargetX86() { return g_targetMachine->GetPlatform() == IMAGE inline BOOL IsDbgTargetAmd64() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_AMD64; } inline BOOL IsDbgTargetArm() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_ARMNT; } inline BOOL IsDbgTargetArm64() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_ARM64; } +inline BOOL IsDbgTargetRiscV64(){ return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_RISCV64; } inline BOOL IsDbgTargetWin64() { return IsDbgTargetAmd64(); } /* Returns the instruction pointer for the given CONTEXT. We need this and its family of diff --git a/src/SOS/Strike/platform/cordebugdatatarget.h b/src/SOS/Strike/platform/cordebugdatatarget.h index 25e75f2c75..a911769191 100644 --- a/src/SOS/Strike/platform/cordebugdatatarget.h +++ b/src/SOS/Strike/platform/cordebugdatatarget.h @@ -96,6 +96,8 @@ class CorDebugDataTarget : public ICorDebugMutableDataTarget, public ICorDebugMe *pPlatform = CORDB_PLATFORM_POSIX_ARM; else if (platformKind == IMAGE_FILE_MACHINE_ARM64) *pPlatform = CORDB_PLATFORM_POSIX_ARM64; + else if (platformKind == IMAGE_FILE_MACHINE_RISCV64) + *pPlatform = CORDB_PLATFORM_POSIX_RISCV64; else return E_FAIL; } diff --git a/src/SOS/Strike/sos.def b/src/SOS/Strike/sos.def index f298a3f9fd..7f594e187a 100644 --- a/src/SOS/Strike/sos.def +++ b/src/SOS/Strike/sos.def @@ -253,6 +253,9 @@ EXPORTS TraceToCode tracetocode=TraceToCode + CLRMACreateInstance + CLRMAReleaseInstance + SOSInitializeByHost SOSUninitializeByHost InitializeHostServices diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index 0d7aad0d49..4ef4bfd0d5 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -10492,7 +10492,7 @@ class ClrStackImplWithICorDebug InternalFrameManager internalFrameManager; IfFailRet(internalFrameManager.Init(pThread3)); - #if defined(_AMD64_) || defined(_ARM64_) + #if defined(_AMD64_) || defined(_ARM64_) || defined(_RISCV64_) ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site"); #elif defined(_X86_) || defined(_ARM_) ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site"); @@ -10962,6 +10962,25 @@ class ClrStackImpl ExtOut(" cpsr=%08x fpcr=%08x fpsr=%08x\n", context.Arm64Context.Cpsr, context.Arm64Context.Fpcr, context.Arm64Context.Fpsr); } #endif +#if defined(SOS_TARGET_RISCV64) + if (IsDbgTargetRiscV64()) + { + foundPlatform = true; + String outputFormat3 = " %3s=%016llx %3s=%016llx %3s=%016llx\n"; + ExtOut(outputFormat3, "r0", context.RiscV64Context.R0, "ra", context.RiscV64Context.Ra, "sp", context.RiscV64Context.Sp); + ExtOut(outputFormat3, "gp", context.RiscV64Context.Gp, "tp", context.RiscV64Context.Tp, "t0", context.RiscV64Context.T0); + ExtOut(outputFormat3, "t1", context.RiscV64Context.T1, "t2", context.RiscV64Context.T2, "fp", context.RiscV64Context.Fp); + ExtOut(outputFormat3, "s1", context.RiscV64Context.S1, "a0", context.RiscV64Context.A0, "a1", context.RiscV64Context.A1); + ExtOut(outputFormat3, "a2", context.RiscV64Context.A2, "a3", context.RiscV64Context.A3, "a4", context.RiscV64Context.A4); + ExtOut(outputFormat3, "a5", context.RiscV64Context.A5, "a6", context.RiscV64Context.A6, "a7", context.RiscV64Context.A7); + ExtOut(outputFormat3, "s2", context.RiscV64Context.S2, "s3", context.RiscV64Context.S3, "s4", context.RiscV64Context.S4); + ExtOut(outputFormat3, "s5", context.RiscV64Context.S5, "s6", context.RiscV64Context.S6, "s7", context.RiscV64Context.S7); + ExtOut(outputFormat3, "s8", context.RiscV64Context.S8, "s9", context.RiscV64Context.S9, "s10", context.RiscV64Context.S10); + ExtOut(outputFormat3, "s11", context.RiscV64Context.S11, "t3", context.RiscV64Context.T3, "t4", context.RiscV64Context.T4); + ExtOut(outputFormat3, "t5", context.RiscV64Context.T5, "t6", context.RiscV64Context.T6, "pc", context.RiscV64Context.Pc); + } +#endif + if (!foundPlatform) { ExtOut("Can't display register values for this platform\n"); diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index ec384ea237..9fa4c8d86e 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -4416,20 +4416,6 @@ void ExtErr(PCSTR Format, ...) va_end(Args); } -/// -/// Internal trace output for extensions library -/// -void TraceError(PCSTR format, ...) -{ - if (Output::g_bDbgOutput) - { - va_list args; - va_start(args, format); - OutputVaList(DEBUG_OUTPUT_ERROR, format, args); - va_end(args); - } -} - void ExtDbgOut(PCSTR Format, ...) { if (Output::g_bDbgOutput) diff --git a/src/SOS/Strike/util.h b/src/SOS/Strike/util.h index 8eb3f60e52..a0b039b888 100644 --- a/src/SOS/Strike/util.h +++ b/src/SOS/Strike/util.h @@ -1545,7 +1545,7 @@ BOOL IsMiniDumpFile(); void ReportOOM(); BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead); -#if !defined(_TARGET_WIN64_) && !defined(_ARM64_) && !defined(_MIPS64_) +#if !defined(_TARGET_WIN64_) && !defined(_ARM64_) && !defined(_MIPS64_) && !defined(_RISCV64_) // on 64-bit platforms TADDR and CLRDATA_ADDRESS are identical inline BOOL SafeReadMemory (CLRDATA_ADDRESS offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead) { return SafeReadMemory(TO_TADDR(offset), lpBuffer, cb, lpcbBytesRead); } @@ -2416,6 +2416,54 @@ typedef struct { } ARM64_CONTEXT; +///RISCV64 Context +#define RISCV64_MAX_BREAKPOINTS 8 +#define RISCV64_MAX_WATCHPOINTS 2 +typedef struct { + + DWORD ContextFlags; + + DWORD64 R0; + DWORD64 Ra; + DWORD64 Sp; + DWORD64 Gp; + DWORD64 Tp; + DWORD64 T0; + DWORD64 T1; + DWORD64 T2; + DWORD64 Fp; + DWORD64 S1; + DWORD64 A0; + DWORD64 A1; + DWORD64 A2; + DWORD64 A3; + DWORD64 A4; + DWORD64 A5; + DWORD64 A6; + DWORD64 A7; + DWORD64 S2; + DWORD64 S3; + DWORD64 S4; + DWORD64 S5; + DWORD64 S6; + DWORD64 S7; + DWORD64 S8; + DWORD64 S9; + DWORD64 S10; + DWORD64 S11; + DWORD64 T3; + DWORD64 T4; + DWORD64 T5; + DWORD64 T6; + DWORD64 Pc; + + ULONGLONG F[32]; + DWORD Fcsr; + + DWORD Padding[3]; + +} RISCV64_CONTEXT; + typedef struct _CROSS_PLATFORM_CONTEXT { _CROSS_PLATFORM_CONTEXT() {} @@ -2425,6 +2473,7 @@ typedef struct _CROSS_PLATFORM_CONTEXT { AMD64_CONTEXT Amd64Context; ARM_CONTEXT ArmContext; ARM64_CONTEXT Arm64Context; + RISCV64_CONTEXT RiscV64Context; }; } CROSS_PLATFORM_CONTEXT, *PCROSS_PLATFORM_CONTEXT; diff --git a/src/SOS/extensions/extensions.cpp b/src/SOS/extensions/extensions.cpp index 1866088932..a3f48bfcd5 100644 --- a/src/SOS/extensions/extensions.cpp +++ b/src/SOS/extensions/extensions.cpp @@ -234,3 +234,45 @@ bool GetAbsolutePath(const char* path, std::string& absolutePath) } return false; } + +/// +/// Internal output helper function +/// +void InternalOutputVaList( + ULONG mask, + PCSTR format, + va_list args) +{ + char str[1024]; + va_list argsCopy; + va_copy(argsCopy, args); + + // Try and format our string into a fixed buffer first and see if it fits + size_t length = vsnprintf(str, sizeof(str), format, args); + if (length < sizeof(str)) + { + Extensions::GetInstance()->GetDebuggerServices()->OutputString(mask, str); + } + else + { + // Our stack buffer wasn't big enough to contain the entire formatted string + char *str_ptr = (char*)::malloc(length + 1); + if (str_ptr != nullptr) + { + vsnprintf(str_ptr, length + 1, format, argsCopy); + Extensions::GetInstance()->GetDebuggerServices()->OutputString(mask, str_ptr); + ::free(str_ptr); + } + } +} + +/// +/// Internal trace output for extensions library +/// +void TraceError(PCSTR format, ...) +{ + va_list args; + va_start(args, format); + InternalOutputVaList(DEBUG_OUTPUT_ERROR, format, args); + va_end(args); +} diff --git a/src/SOS/extensions/hostcoreclr.cpp b/src/SOS/extensions/hostcoreclr.cpp index 025d6a3f9e..0ec62b486c 100644 --- a/src/SOS/extensions/hostcoreclr.cpp +++ b/src/SOS/extensions/hostcoreclr.cpp @@ -640,7 +640,7 @@ static HRESULT InitializeNetCoreHost() char* exePath = minipal_getexepath(); if (!exePath) { - TraceError("Could not get full path to current executable"); + TraceError("Could not get full path to current executable\n"); return E_FAIL; } diff --git a/src/SOS/inc/clrma.h b/src/SOS/inc/clrma.h new file mode 100644 index 0000000000..34f6944b09 --- /dev/null +++ b/src/SOS/inc/clrma.h @@ -0,0 +1,735 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 8.01.0628 */ +/* at Mon Jan 18 19:14:07 2038 + */ +/* Compiler settings for C:\ssd\reliability.banganalyze\src\CLRMA\Interface\idl\CLRMA.idl: + Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0628 + protocol : dce , ms_ext, c_ext, robust + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +/* @@MIDL_FILE_HEADING( ) */ + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 475 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif /* __RPCNDR_H_VERSION__ */ + + +#ifndef __CLRMA_h__ +#define __CLRMA_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#ifndef DECLSPEC_XFGVIRT +#if defined(_CONTROL_FLOW_GUARD_XFG) +#define DECLSPEC_XFGVIRT(base, func) __declspec(xfg_virtual(base, func)) +#else +#define DECLSPEC_XFGVIRT(base, func) +#endif +#endif + +/* Forward Declarations */ + +#ifndef __ICLRManagedAnalysis_FWD_DEFINED__ +#define __ICLRManagedAnalysis_FWD_DEFINED__ +typedef interface ICLRManagedAnalysis ICLRManagedAnalysis; + +#endif /* __ICLRManagedAnalysis_FWD_DEFINED__ */ + + +#ifndef __ICLRMAClrThread_FWD_DEFINED__ +#define __ICLRMAClrThread_FWD_DEFINED__ +typedef interface ICLRMAClrThread ICLRMAClrThread; + +#endif /* __ICLRMAClrThread_FWD_DEFINED__ */ + + +#ifndef __ICLRMAClrException_FWD_DEFINED__ +#define __ICLRMAClrException_FWD_DEFINED__ +typedef interface ICLRMAClrException ICLRMAClrException; + +#endif /* __ICLRMAClrException_FWD_DEFINED__ */ + + +#ifndef __ICLRMAObjectInspection_FWD_DEFINED__ +#define __ICLRMAObjectInspection_FWD_DEFINED__ +typedef interface ICLRMAObjectInspection ICLRMAObjectInspection; + +#endif /* __ICLRMAObjectInspection_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "unknwn.h" + +#ifdef __cplusplus +extern "C"{ +#endif + + + +#ifndef __CLRMA_LIBRARY_DEFINED__ +#define __CLRMA_LIBRARY_DEFINED__ + +/* library CLRMA */ +/* [helpstring][version][uuid] */ + + + + + + +EXTERN_C const IID LIBID_CLRMA; + +#ifndef __ICLRManagedAnalysis_INTERFACE_DEFINED__ +#define __ICLRManagedAnalysis_INTERFACE_DEFINED__ + +/* interface ICLRManagedAnalysis */ +/* [object][helpstring][uuid] */ + + +EXTERN_C const IID IID_ICLRManagedAnalysis; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("8CA73A16-C017-4c8f-AD51-B758727478CA") + ICLRManagedAnalysis : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ProviderName( + /* [retval][out] */ BSTR *bstrProvider) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE AssociateClient( + /* [in] */ IUnknown *pUnknown) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetThread( + /* [in] */ ULONG osThreadID, + /* [retval][out] */ ICLRMAClrThread **ppClrThread) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetException( + /* [in] */ ULONG64 addr, + /* [retval][out] */ ICLRMAClrException **ppClrException) = 0; + + virtual /* [propget][helpstring] */ HRESULT STDMETHODCALLTYPE get_ObjectInspection( + /* [retval][out] */ ICLRMAObjectInspection **ppObjectInspection) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICLRManagedAnalysisVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICLRManagedAnalysis * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICLRManagedAnalysis * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + ICLRManagedAnalysis * This); + + DECLSPEC_XFGVIRT(ICLRManagedAnalysis, get_ProviderName) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ProviderName )( + ICLRManagedAnalysis * This, + /* [retval][out] */ BSTR *bstrProvider); + + DECLSPEC_XFGVIRT(ICLRManagedAnalysis, AssociateClient) + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *AssociateClient )( + ICLRManagedAnalysis * This, + /* [in] */ IUnknown *pUnknown); + + DECLSPEC_XFGVIRT(ICLRManagedAnalysis, GetThread) + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetThread )( + ICLRManagedAnalysis * This, + /* [in] */ ULONG osThreadID, + /* [retval][out] */ ICLRMAClrThread **ppClrThread); + + DECLSPEC_XFGVIRT(ICLRManagedAnalysis, GetException) + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetException )( + ICLRManagedAnalysis * This, + /* [in] */ ULONG64 addr, + /* [retval][out] */ ICLRMAClrException **ppClrException); + + DECLSPEC_XFGVIRT(ICLRManagedAnalysis, get_ObjectInspection) + /* [propget][helpstring] */ HRESULT ( STDMETHODCALLTYPE *get_ObjectInspection )( + ICLRManagedAnalysis * This, + /* [retval][out] */ ICLRMAObjectInspection **ppObjectInspection); + + END_INTERFACE + } ICLRManagedAnalysisVtbl; + + interface ICLRManagedAnalysis + { + CONST_VTBL struct ICLRManagedAnalysisVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICLRManagedAnalysis_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICLRManagedAnalysis_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICLRManagedAnalysis_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICLRManagedAnalysis_get_ProviderName(This,bstrProvider) \ + ( (This)->lpVtbl -> get_ProviderName(This,bstrProvider) ) + +#define ICLRManagedAnalysis_AssociateClient(This,pUnknown) \ + ( (This)->lpVtbl -> AssociateClient(This,pUnknown) ) + +#define ICLRManagedAnalysis_GetThread(This,osThreadID,ppClrThread) \ + ( (This)->lpVtbl -> GetThread(This,osThreadID,ppClrThread) ) + +#define ICLRManagedAnalysis_GetException(This,addr,ppClrException) \ + ( (This)->lpVtbl -> GetException(This,addr,ppClrException) ) + +#define ICLRManagedAnalysis_get_ObjectInspection(This,ppObjectInspection) \ + ( (This)->lpVtbl -> get_ObjectInspection(This,ppObjectInspection) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICLRManagedAnalysis_INTERFACE_DEFINED__ */ + + +#ifndef __ICLRMAClrThread_INTERFACE_DEFINED__ +#define __ICLRMAClrThread_INTERFACE_DEFINED__ + +/* interface ICLRMAClrThread */ +/* [object][helpstring][uuid] */ + + +EXTERN_C const IID IID_ICLRMAClrThread; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9849CFC9-0868-406e-9059-6B04E9ADBBB8") + ICLRMAClrThread : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_DebuggerCommand( + /* [retval][out] */ BSTR *pValue) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_OSThreadId( + /* [retval][out] */ ULONG *pValue) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_FrameCount( + /* [retval][out] */ UINT *pCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE Frame( + /* [in] */ UINT nFrame, + /* [out] */ ULONG64 *pAddrIP, + /* [out] */ ULONG64 *pAddrSP, + /* [out] */ BSTR *bstrModule, + /* [out] */ BSTR *bstrFunction, + /* [out] */ ULONG64 *pDisplacement) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CurrentException( + /* [retval][out] */ ICLRMAClrException **ppClrException) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_NestedExceptionCount( + /* [retval][out] */ USHORT *pCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE NestedException( + /* [in] */ USHORT nIndex, + /* [retval][out] */ ICLRMAClrException **ppClrException) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICLRMAClrThreadVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICLRMAClrThread * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICLRMAClrThread * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + ICLRMAClrThread * This); + + DECLSPEC_XFGVIRT(ICLRMAClrThread, get_DebuggerCommand) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DebuggerCommand )( + ICLRMAClrThread * This, + /* [retval][out] */ BSTR *pValue); + + DECLSPEC_XFGVIRT(ICLRMAClrThread, get_OSThreadId) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_OSThreadId )( + ICLRMAClrThread * This, + /* [retval][out] */ ULONG *pValue); + + DECLSPEC_XFGVIRT(ICLRMAClrThread, get_FrameCount) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_FrameCount )( + ICLRMAClrThread * This, + /* [retval][out] */ UINT *pCount); + + DECLSPEC_XFGVIRT(ICLRMAClrThread, Frame) + HRESULT ( STDMETHODCALLTYPE *Frame )( + ICLRMAClrThread * This, + /* [in] */ UINT nFrame, + /* [out] */ ULONG64 *pAddrIP, + /* [out] */ ULONG64 *pAddrSP, + /* [out] */ BSTR *bstrModule, + /* [out] */ BSTR *bstrFunction, + /* [out] */ ULONG64 *pDisplacement); + + DECLSPEC_XFGVIRT(ICLRMAClrThread, get_CurrentException) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CurrentException )( + ICLRMAClrThread * This, + /* [retval][out] */ ICLRMAClrException **ppClrException); + + DECLSPEC_XFGVIRT(ICLRMAClrThread, get_NestedExceptionCount) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_NestedExceptionCount )( + ICLRMAClrThread * This, + /* [retval][out] */ USHORT *pCount); + + DECLSPEC_XFGVIRT(ICLRMAClrThread, NestedException) + HRESULT ( STDMETHODCALLTYPE *NestedException )( + ICLRMAClrThread * This, + /* [in] */ USHORT nIndex, + /* [retval][out] */ ICLRMAClrException **ppClrException); + + END_INTERFACE + } ICLRMAClrThreadVtbl; + + interface ICLRMAClrThread + { + CONST_VTBL struct ICLRMAClrThreadVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICLRMAClrThread_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICLRMAClrThread_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICLRMAClrThread_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICLRMAClrThread_get_DebuggerCommand(This,pValue) \ + ( (This)->lpVtbl -> get_DebuggerCommand(This,pValue) ) + +#define ICLRMAClrThread_get_OSThreadId(This,pValue) \ + ( (This)->lpVtbl -> get_OSThreadId(This,pValue) ) + +#define ICLRMAClrThread_get_FrameCount(This,pCount) \ + ( (This)->lpVtbl -> get_FrameCount(This,pCount) ) + +#define ICLRMAClrThread_Frame(This,nFrame,pAddrIP,pAddrSP,bstrModule,bstrFunction,pDisplacement) \ + ( (This)->lpVtbl -> Frame(This,nFrame,pAddrIP,pAddrSP,bstrModule,bstrFunction,pDisplacement) ) + +#define ICLRMAClrThread_get_CurrentException(This,ppClrException) \ + ( (This)->lpVtbl -> get_CurrentException(This,ppClrException) ) + +#define ICLRMAClrThread_get_NestedExceptionCount(This,pCount) \ + ( (This)->lpVtbl -> get_NestedExceptionCount(This,pCount) ) + +#define ICLRMAClrThread_NestedException(This,nIndex,ppClrException) \ + ( (This)->lpVtbl -> NestedException(This,nIndex,ppClrException) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICLRMAClrThread_INTERFACE_DEFINED__ */ + + +#ifndef __ICLRMAClrException_INTERFACE_DEFINED__ +#define __ICLRMAClrException_INTERFACE_DEFINED__ + +/* interface ICLRMAClrException */ +/* [object][helpstring][uuid] */ + + +EXTERN_C const IID IID_ICLRMAClrException; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("7C165652-D539-472e-A6CF-F657FFF31751") + ICLRMAClrException : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_DebuggerCommand( + /* [retval][out] */ BSTR *pValue) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Address( + /* [retval][out] */ ULONG64 *pValue) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HResult( + /* [retval][out] */ HRESULT *pValue) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Type( + /* [retval][out] */ BSTR *pValue) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Message( + /* [retval][out] */ BSTR *pValue) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_FrameCount( + /* [retval][out] */ UINT *pCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE Frame( + /* [in] */ UINT nFrame, + /* [out] */ ULONG64 *pAddrIP, + /* [out] */ ULONG64 *pAddrSP, + /* [out] */ BSTR *pModule, + /* [out] */ BSTR *pFunction, + /* [out] */ ULONG64 *pDisplacement) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_InnerExceptionCount( + /* [retval][out] */ USHORT *pCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE InnerException( + /* [in] */ USHORT nIndex, + /* [retval][out] */ ICLRMAClrException **ppClrException) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICLRMAClrExceptionVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICLRMAClrException * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICLRMAClrException * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + ICLRMAClrException * This); + + DECLSPEC_XFGVIRT(ICLRMAClrException, get_DebuggerCommand) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DebuggerCommand )( + ICLRMAClrException * This, + /* [retval][out] */ BSTR *pValue); + + DECLSPEC_XFGVIRT(ICLRMAClrException, get_Address) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Address )( + ICLRMAClrException * This, + /* [retval][out] */ ULONG64 *pValue); + + DECLSPEC_XFGVIRT(ICLRMAClrException, get_HResult) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HResult )( + ICLRMAClrException * This, + /* [retval][out] */ HRESULT *pValue); + + DECLSPEC_XFGVIRT(ICLRMAClrException, get_Type) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Type )( + ICLRMAClrException * This, + /* [retval][out] */ BSTR *pValue); + + DECLSPEC_XFGVIRT(ICLRMAClrException, get_Message) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Message )( + ICLRMAClrException * This, + /* [retval][out] */ BSTR *pValue); + + DECLSPEC_XFGVIRT(ICLRMAClrException, get_FrameCount) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_FrameCount )( + ICLRMAClrException * This, + /* [retval][out] */ UINT *pCount); + + DECLSPEC_XFGVIRT(ICLRMAClrException, Frame) + HRESULT ( STDMETHODCALLTYPE *Frame )( + ICLRMAClrException * This, + /* [in] */ UINT nFrame, + /* [out] */ ULONG64 *pAddrIP, + /* [out] */ ULONG64 *pAddrSP, + /* [out] */ BSTR *pModule, + /* [out] */ BSTR *pFunction, + /* [out] */ ULONG64 *pDisplacement); + + DECLSPEC_XFGVIRT(ICLRMAClrException, get_InnerExceptionCount) + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_InnerExceptionCount )( + ICLRMAClrException * This, + /* [retval][out] */ USHORT *pCount); + + DECLSPEC_XFGVIRT(ICLRMAClrException, InnerException) + HRESULT ( STDMETHODCALLTYPE *InnerException )( + ICLRMAClrException * This, + /* [in] */ USHORT nIndex, + /* [retval][out] */ ICLRMAClrException **ppClrException); + + END_INTERFACE + } ICLRMAClrExceptionVtbl; + + interface ICLRMAClrException + { + CONST_VTBL struct ICLRMAClrExceptionVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICLRMAClrException_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICLRMAClrException_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICLRMAClrException_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICLRMAClrException_get_DebuggerCommand(This,pValue) \ + ( (This)->lpVtbl -> get_DebuggerCommand(This,pValue) ) + +#define ICLRMAClrException_get_Address(This,pValue) \ + ( (This)->lpVtbl -> get_Address(This,pValue) ) + +#define ICLRMAClrException_get_HResult(This,pValue) \ + ( (This)->lpVtbl -> get_HResult(This,pValue) ) + +#define ICLRMAClrException_get_Type(This,pValue) \ + ( (This)->lpVtbl -> get_Type(This,pValue) ) + +#define ICLRMAClrException_get_Message(This,pValue) \ + ( (This)->lpVtbl -> get_Message(This,pValue) ) + +#define ICLRMAClrException_get_FrameCount(This,pCount) \ + ( (This)->lpVtbl -> get_FrameCount(This,pCount) ) + +#define ICLRMAClrException_Frame(This,nFrame,pAddrIP,pAddrSP,pModule,pFunction,pDisplacement) \ + ( (This)->lpVtbl -> Frame(This,nFrame,pAddrIP,pAddrSP,pModule,pFunction,pDisplacement) ) + +#define ICLRMAClrException_get_InnerExceptionCount(This,pCount) \ + ( (This)->lpVtbl -> get_InnerExceptionCount(This,pCount) ) + +#define ICLRMAClrException_InnerException(This,nIndex,ppClrException) \ + ( (This)->lpVtbl -> InnerException(This,nIndex,ppClrException) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICLRMAClrException_INTERFACE_DEFINED__ */ + + +#ifndef __ICLRMAObjectInspection_INTERFACE_DEFINED__ +#define __ICLRMAObjectInspection_INTERFACE_DEFINED__ + +/* interface ICLRMAObjectInspection */ +/* [object][helpstring][uuid] */ + + +EXTERN_C const IID IID_ICLRMAObjectInspection; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("836259DB-7452-4b2b-95C4-4BC52CB9ABC7") + ICLRMAObjectInspection : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetType( + /* [in] */ ULONG64 addr, + /* [retval][out] */ BSTR *pValue) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAddressFromCcwAddress( + /* [in] */ ULONG64 addr, + /* [retval][out] */ ULONG64 *pAddress) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetField_SystemString( + /* [in] */ ULONG64 addr, + /* [in] */ BSTR bstrField, + /* [retval][out] */ BSTR *pValue) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetField_SystemUInt32( + /* [in] */ ULONG64 addr, + /* [in] */ BSTR bstrField, + /* [retval][out] */ ULONG *pValue) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetField_SystemInt32( + /* [in] */ ULONG64 addr, + /* [in] */ BSTR bstrField, + /* [retval][out] */ LONG *pValue) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICLRMAObjectInspectionVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICLRMAObjectInspection * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICLRMAObjectInspection * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + ICLRMAObjectInspection * This); + + DECLSPEC_XFGVIRT(ICLRMAObjectInspection, GetType) + HRESULT ( STDMETHODCALLTYPE *GetType )( + ICLRMAObjectInspection * This, + /* [in] */ ULONG64 addr, + /* [retval][out] */ BSTR *pValue); + + DECLSPEC_XFGVIRT(ICLRMAObjectInspection, GetAddressFromCcwAddress) + HRESULT ( STDMETHODCALLTYPE *GetAddressFromCcwAddress )( + ICLRMAObjectInspection * This, + /* [in] */ ULONG64 addr, + /* [retval][out] */ ULONG64 *pAddress); + + DECLSPEC_XFGVIRT(ICLRMAObjectInspection, GetField_SystemString) + HRESULT ( STDMETHODCALLTYPE *GetField_SystemString )( + ICLRMAObjectInspection * This, + /* [in] */ ULONG64 addr, + /* [in] */ BSTR bstrField, + /* [retval][out] */ BSTR *pValue); + + DECLSPEC_XFGVIRT(ICLRMAObjectInspection, GetField_SystemUInt32) + HRESULT ( STDMETHODCALLTYPE *GetField_SystemUInt32 )( + ICLRMAObjectInspection * This, + /* [in] */ ULONG64 addr, + /* [in] */ BSTR bstrField, + /* [retval][out] */ ULONG *pValue); + + DECLSPEC_XFGVIRT(ICLRMAObjectInspection, GetField_SystemInt32) + HRESULT ( STDMETHODCALLTYPE *GetField_SystemInt32 )( + ICLRMAObjectInspection * This, + /* [in] */ ULONG64 addr, + /* [in] */ BSTR bstrField, + /* [retval][out] */ LONG *pValue); + + END_INTERFACE + } ICLRMAObjectInspectionVtbl; + + interface ICLRMAObjectInspection + { + CONST_VTBL struct ICLRMAObjectInspectionVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICLRMAObjectInspection_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICLRMAObjectInspection_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICLRMAObjectInspection_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICLRMAObjectInspection_GetType(This,addr,pValue) \ + ( (This)->lpVtbl -> GetType(This,addr,pValue) ) + +#define ICLRMAObjectInspection_GetAddressFromCcwAddress(This,addr,pAddress) \ + ( (This)->lpVtbl -> GetAddressFromCcwAddress(This,addr,pAddress) ) + +#define ICLRMAObjectInspection_GetField_SystemString(This,addr,bstrField,pValue) \ + ( (This)->lpVtbl -> GetField_SystemString(This,addr,bstrField,pValue) ) + +#define ICLRMAObjectInspection_GetField_SystemUInt32(This,addr,bstrField,pValue) \ + ( (This)->lpVtbl -> GetField_SystemUInt32(This,addr,bstrField,pValue) ) + +#define ICLRMAObjectInspection_GetField_SystemInt32(This,addr,bstrField,pValue) \ + ( (This)->lpVtbl -> GetField_SystemInt32(This,addr,bstrField,pValue) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICLRMAObjectInspection_INTERFACE_DEFINED__ */ + +#endif /* __CLRMA_LIBRARY_DEFINED__ */ + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/src/SOS/inc/clrma.idl b/src/SOS/inc/clrma.idl new file mode 100644 index 0000000000..bd880068c0 --- /dev/null +++ b/src/SOS/inc/clrma.idl @@ -0,0 +1,103 @@ +// +// CLRMA.idl +// + +import "unknwn.idl"; + +[ + uuid(511BCC9D-5B25-4d15-8F00-4DDFB0C142D5), + version(3.0), + helpstring("CLR Managed Analysis") +] +library CLRMA +{ + // + // Forward declarations + // + + interface ICLRManagedAnalysis; + interface ICLRMAClrThread; + interface ICLRMAClrException; + interface ICLRMAObjectInspection; + + // + // Interface declarations + // + + // ICLRManagedAnalysis + [ + uuid(8CA73A16-C017-4c8f-AD51-B758727478CA), + helpstring("ICLRManagedAnalysis interface") + ] + interface ICLRManagedAnalysis : IUnknown + { + [propget] HRESULT ProviderName([out, retval] BSTR* bstrProvider); + + [helpstring("Associate a debug engine (usually an IDebugClient) with the CLRMA Provider.")] + HRESULT AssociateClient([in] IUnknown* pUnknown); + + [helpstring("-1 : Last Event Thread, 0 : Current Thread, NN : OS Thread ID (TID)")] + HRESULT GetThread([in] ULONG osThreadID, [out, retval] ICLRMAClrThread** ppClrThread); + + [helpstring(" 0 : Current Thread's Current Exception\nNN : Exception Object Address")] + HRESULT GetException([in] ULONG64 addr, [out, retval] ICLRMAClrException** ppClrException); + + [helpstring("Interface to Inspect Objects")] + [propget] HRESULT ObjectInspection([out, retval] ICLRMAObjectInspection** ppObjectInspection); + }; + + // ICLRMAClrThread + [ + uuid(9849CFC9-0868-406e-9059-6B04E9ADBBB8), + helpstring("ICLRMAClrThread interface") + ] + interface ICLRMAClrThread : IUnknown + { + [propget] HRESULT DebuggerCommand([out, retval] BSTR* pValue); + [propget] HRESULT OSThreadId([out, retval] ULONG* pValue); + + [propget] HRESULT FrameCount([out, retval] UINT* pCount); + HRESULT Frame([in] UINT nFrame, [out] ULONG64* pAddrIP, [out] ULONG64* pAddrSP, [out] BSTR* bstrModule, [out] BSTR* bstrFunction, [out] ULONG64* pDisplacement); + + [propget] HRESULT CurrentException([out, retval] ICLRMAClrException** ppClrException); + + [propget] HRESULT NestedExceptionCount([out, retval] USHORT* pCount); + HRESULT NestedException([in] USHORT nIndex, [out, retval] ICLRMAClrException** ppClrException); + }; + + // ICLRMAClrException + [ + uuid(7C165652-D539-472e-A6CF-F657FFF31751), + helpstring("ICLRMAClrException interface") + ] + interface ICLRMAClrException : IUnknown + { + [propget] HRESULT DebuggerCommand([out, retval] BSTR* pValue); + [propget] HRESULT Address([out, retval] ULONG64* pValue); + [propget] HRESULT HResult([out, retval] HRESULT* pValue); + [propget] HRESULT Type([out, retval] BSTR* pValue); + [propget] HRESULT Message([out, retval] BSTR* pValue); + + [propget] HRESULT FrameCount([out, retval] UINT* pCount); + HRESULT Frame([in] UINT nFrame, [out] ULONG64* pAddrIP, [out] ULONG64* pAddrSP, [out] BSTR* pModule, [out] BSTR* pFunction, [out] ULONG64* pDisplacement) ; + + [propget] HRESULT InnerExceptionCount([out, retval] USHORT* pCount); + HRESULT InnerException([in] USHORT nIndex, [out, retval] ICLRMAClrException** ppClrException); + }; + + // ICLRMAObjectInspection + [ + uuid(836259DB-7452-4b2b-95C4-4BC52CB9ABC7), + helpstring("ICLRMAObjectInspection interface") + ] + interface ICLRMAObjectInspection : IUnknown + { + HRESULT GetType([in] ULONG64 addr, [out, retval] BSTR* pValue); + HRESULT GetAddressFromCcwAddress([in] ULONG64 addr, [out, retval] ULONG64* pAddress); + + HRESULT GetField_SystemString([in] ULONG64 addr, [in] BSTR bstrField, [out, retval] BSTR* pValue); + HRESULT GetField_SystemUInt32([in] ULONG64 addr, [in] BSTR bstrField, [out, retval] ULONG* pValue); + HRESULT GetField_SystemInt32([in] ULONG64 addr, [in] BSTR bstrField, [out, retval] LONG* pValue); + }; + +}; // CLRMA Library diff --git a/src/SOS/lldbplugin/services.cpp b/src/SOS/lldbplugin/services.cpp index 1ecc095710..f340a069ad 100644 --- a/src/SOS/lldbplugin/services.cpp +++ b/src/SOS/lldbplugin/services.cpp @@ -973,7 +973,7 @@ LLDBServices::GetNameByOffset( goto exit; } - address = module.ResolveFileAddress(offset); + address = target.ResolveLoadAddress(offset); if (!address.IsValid()) { hr = E_INVALIDARG; @@ -1445,16 +1445,7 @@ LLDBServices::GetModuleSize( /* const */ lldb::SBModule& module) { ULONG64 size = 0; -#if defined(__APPLE__) - mach_header_64 header; - ULONG read; - HRESULT hr = ReadVirtual(baseAddress, &header, sizeof(mach_header_64), &read) == S_OK; - if (SUCCEEDED(hr)) - { - // Since MachO segments are not contiguous the image size is just the headers/commands - size = sizeof(mach_header_64) + header.sizeofcmds; - } -#else + // Find the first section with an valid base address int numSections = module.GetNumSections(); for (int si = 0; si < numSections; si++) @@ -1462,10 +1453,15 @@ LLDBServices::GetModuleSize( lldb::SBSection section = module.GetSectionAtIndex(si); if (section.IsValid()) { +#if defined(__APPLE__) + if (strcmp(section.GetName(), "__LINKEDIT") == 0) + { + continue; + } + #endif size += section.GetByteSize(); } } -#endif // For core dumps lldb doesn't return the section sizes when it // doesn't have access to the actual module file, but SOS (like // the SymbolReader code) still needs a non-zero module size. diff --git a/src/SOS/lldbplugin/sosplugin.cpp b/src/SOS/lldbplugin/sosplugin.cpp index 2c39d5034b..f4ef26e1c9 100644 --- a/src/SOS/lldbplugin/sosplugin.cpp +++ b/src/SOS/lldbplugin/sosplugin.cpp @@ -19,14 +19,3 @@ bool lldb::PluginInitialize(lldb::SBDebugger debugger) sethostruntimeCommandInitialize(debugger); return true; } - -/// -/// Internal trace output for extensions library -/// -void TraceError(PCSTR format, ...) -{ - va_list args; - va_start(args, format); - g_services->InternalOutputVaList(DEBUG_OUTPUT_ERROR, format, args); - va_end(args); -} diff --git a/src/Tools/dotnet-counters/Exporters/JSONExporter.cs b/src/Tools/dotnet-counters/Exporters/JSONExporter.cs index ec571ad7ba..6a578018a9 100644 --- a/src/Tools/dotnet-counters/Exporters/JSONExporter.cs +++ b/src/Tools/dotnet-counters/Exporters/JSONExporter.cs @@ -42,7 +42,7 @@ public void Initialize() builder = new StringBuilder(); builder .Append("{ \"TargetProcess\": \"").Append(_processName).Append("\", ") - .Append("\"StartTime\": \"").Append(DateTime.Now.ToString()).Append("\", ") + .Append("\"StartTime\": \"").Append(DateTime.Now.ToString("O")).Append("\", ") .Append("\"Events\": ["); } } @@ -72,7 +72,7 @@ public void CounterPayloadReceived(CounterPayload payload, bool _) builder.Clear(); } builder - .Append("{ \"timestamp\": \"").Append(DateTime.Now.ToString("u")).Append("\", ") + .Append("{ \"timestamp\": \"").Append(DateTime.Now.ToString("O")).Append("\", ") .Append(" \"provider\": \"").Append(JsonEscape(payload.Provider)).Append("\", ") .Append(" \"name\": \"").Append(JsonEscape(payload.GetDisplay())).Append("\", ") .Append(" \"tags\": \"").Append(JsonEscape(payload.Metadata)).Append("\", ") diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs b/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs index 15eab9c799..720373be6d 100644 --- a/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs +++ b/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs @@ -12,6 +12,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.Diagnostics.Monitoring.EventPipe; using Microsoft.Diagnostics.NETCore.Client; using Microsoft.Internal.Common.Utils; using Microsoft.Tools.Common; @@ -31,7 +32,7 @@ private static void ConsoleWriteLine(string str) } } - private delegate Task CollectDelegate(CancellationToken ct, IConsole console, int processId, FileInfo output, uint buffersize, string providers, string profile, TraceFileFormat format, TimeSpan duration, string clrevents, string clreventlevel, string name, string port, bool showchildio, bool resumeRuntime); + private delegate Task CollectDelegate(CancellationToken ct, IConsole console, int processId, FileInfo output, uint buffersize, string providers, string profile, TraceFileFormat format, TimeSpan duration, string clrevents, string clreventlevel, string name, string port, bool showchildio, bool resumeRuntime, string stoppingEventProviderName, string stoppingEventEventName, string stoppingEventPayloadFilter); /// /// Collects a diagnostic trace from a currently running process or launch a child process and trace it. @@ -52,8 +53,11 @@ private static void ConsoleWriteLine(string str) /// Path to the diagnostic port to be used. /// Should IO from a child process be hidden. /// Resume runtime once session has been initialized. + /// A string, parsed as-is, that will stop the trace upon hitting an event with the matching provider name. For a more specific stopping event, additionally provide `--stopping-event-event-name` and/or `--stopping-event-payload-filter`. + /// A string, parsed as-is, that will stop the trace upon hitting an event with the matching event name. Requires `--stopping-event-provider-name` to be set. For a more specific stopping event, additionally provide `--stopping-event-payload-filter`. + /// A string, parsed as [payload_field_name]:[payload_field_value] pairs separated by commas, that will stop the trace upon hitting an event with a matching payload. Requires `--stopping-event-provider-name` and `--stopping-event-event-name` to be set. /// - private static async Task Collect(CancellationToken ct, IConsole console, int processId, FileInfo output, uint buffersize, string providers, string profile, TraceFileFormat format, TimeSpan duration, string clrevents, string clreventlevel, string name, string diagnosticPort, bool showchildio, bool resumeRuntime) + private static async Task Collect(CancellationToken ct, IConsole console, int processId, FileInfo output, uint buffersize, string providers, string profile, TraceFileFormat format, TimeSpan duration, string clrevents, string clreventlevel, string name, string diagnosticPort, bool showchildio, bool resumeRuntime, string stoppingEventProviderName, string stoppingEventEventName, string stoppingEventPayloadFilter) { bool collectionStopped = false; bool cancelOnEnter = true; @@ -159,6 +163,39 @@ private static async Task Collect(CancellationToken ct, IConsole console, i PrintProviders(providerCollection, enabledBy); + // Validate and parse stoppingEvent parameters: stoppingEventProviderName, stoppingEventEventName, stoppingEventPayloadFilter + + bool hasStoppingEventProviderName = !string.IsNullOrEmpty(stoppingEventProviderName); + bool hasStoppingEventEventName = !string.IsNullOrEmpty(stoppingEventEventName); + bool hasStoppingEventPayloadFilter = !string.IsNullOrEmpty(stoppingEventPayloadFilter); + if (!hasStoppingEventProviderName && (hasStoppingEventEventName || hasStoppingEventPayloadFilter)) + { + Console.Error.WriteLine($"`{nameof(stoppingEventProviderName)}` is required to stop tracing after a specific event for a particular `{nameof(stoppingEventEventName)}` event name or `{nameof(stoppingEventPayloadFilter)}` payload filter."); + return (int)ReturnCode.ArgumentError; + } + if (!hasStoppingEventEventName && hasStoppingEventPayloadFilter) + { + Console.Error.WriteLine($"`{nameof(stoppingEventEventName)}` is required to stop tracing after a specific event for a particular `{nameof(stoppingEventPayloadFilter)}` payload filter."); + return (int)ReturnCode.ArgumentError; + } + + Dictionary payloadFilter = new(); + if (hasStoppingEventPayloadFilter) + { + string[] payloadFieldNameValuePairs = stoppingEventPayloadFilter.Split(','); + foreach (string pair in payloadFieldNameValuePairs) + { + string[] payloadFieldNameValuePair = pair.Split(':'); + if (payloadFieldNameValuePair.Length != 2) + { + Console.Error.WriteLine($"`{nameof(stoppingEventPayloadFilter)}` does not have valid format. Ensure that it has `payload_field_name:payload_field_value` pairs separated by commas."); + return (int)ReturnCode.ArgumentError; + } + + payloadFilter[payloadFieldNameValuePair[0]] = payloadFieldNameValuePair[1]; + } + } + DiagnosticsClient diagnosticsClient; Process process = null; DiagnosticsClientBuilder builder = new("dotnet-trace", 10); @@ -220,7 +257,6 @@ private static async Task Collect(CancellationToken ct, IConsole console, i bool rundownRequested = false; System.Timers.Timer durationTimer = null; - using (VirtualTerminalMode vTermMode = printStatusOverTime ? VirtualTerminalMode.TryEnable() : null) { EventPipeSession session = null; @@ -281,7 +317,31 @@ private static async Task Collect(CancellationToken ct, IConsole console, i ConsoleWriteLine("\n\n"); FileInfo fileInfo = new(output.FullName); - Task copyTask = session.EventStream.CopyToAsync(fs); + EventMonitor eventMonitor = null; + Task copyTask = null; + if (hasStoppingEventProviderName) + { + eventMonitor = new( + stoppingEventProviderName, + stoppingEventEventName, + payloadFilter, + onEvent: (traceEvent) => + { + shouldExit.Set(); + }, + onPayloadFilterMismatch: (traceEvent) => + { + ConsoleWriteLine($"One or more field names specified in the payload filter for event '{traceEvent.ProviderName}/{traceEvent.EventName}' do not match any of the known field names: '{string.Join(' ', traceEvent.PayloadNames)}'. As a result the requested stopping event is unreachable; will continue to collect the trace for the remaining specified duration."); + }, + eventStream: new PassthroughStream(session.EventStream, fs, (int)buffersize, leaveDestinationStreamOpen: true), + callOnEventOnlyOnce: true); + + copyTask = eventMonitor.ProcessAsync(CancellationToken.None); + } + else + { + copyTask = session.EventStream.CopyToAsync(fs); + } Task shouldExitTask = copyTask.ContinueWith( (task) => shouldExit.Set(), CancellationToken.None, @@ -339,6 +399,11 @@ private static async Task Collect(CancellationToken ct, IConsole console, i { printStatus(); } while (!copyTask.Wait(100)); + + if (eventMonitor != null) + { + await eventMonitor.DisposeAsync().ConfigureAwait(false); + } } // At this point the copyTask will have finished, so wait on the shouldExitTask in case it threw // an exception or had some other interesting behavior @@ -455,7 +520,10 @@ public static Command CollectCommand() => CommonOptions.NameOption(), DiagnosticPortOption(), ShowChildIOOption(), - ResumeRuntimeOption() + ResumeRuntimeOption(), + StoppingEventProviderNameOption(), + StoppingEventEventNameOption(), + StoppingEventPayloadFilterOption() }; private static uint DefaultCircularBufferSizeInMB() => 256; @@ -546,5 +614,29 @@ private static Option ResumeRuntimeOption() => { Argument = new Argument(name: "resumeRuntime", getDefaultValue: () => true) }; + + private static Option StoppingEventProviderNameOption() => + new( + alias: "--stopping-event-provider-name", + description: @"A string, parsed as-is, that will stop the trace upon hitting an event with the matching provider name. For a more specific stopping event, additionally provide `--stopping-event-event-name` and/or `--stopping-event-payload-filter`.") + { + Argument = new Argument(name: "stoppingEventProviderName", getDefaultValue: () => null) + }; + + private static Option StoppingEventEventNameOption() => + new( + alias: "--stopping-event-event-name", + description: @"A string, parsed as-is, that will stop the trace upon hitting an event with the matching event name. Requires `--stopping-event-provider-name` to be set. For a more specific stopping event, additionally provide `--stopping-event-payload-filter`.") + { + Argument = new Argument(name: "stoppingEventEventName", getDefaultValue: () => null) + }; + + private static Option StoppingEventPayloadFilterOption() => + new( + alias: "--stopping-event-payload-filter", + description: @"A string, parsed as [payload_field_name]:[payload_field_value] pairs separated by commas, that will stop the trace upon hitting an event with a matching payload. Requires `--stopping-event-provider-name` and `--stopping-event-event-name` to be set.") + { + Argument = new Argument(name: "stoppingEventPayloadFilter", getDefaultValue: () => null) + }; } } diff --git a/src/Tools/dotnet-trace/dotnet-trace.csproj b/src/Tools/dotnet-trace/dotnet-trace.csproj index 1beb21c32d..a4ed81dde4 100644 --- a/src/Tools/dotnet-trace/dotnet-trace.csproj +++ b/src/Tools/dotnet-trace/dotnet-trace.csproj @@ -17,6 +17,7 @@ + diff --git a/src/shared/gcdump/gcdumpnonx86.cpp b/src/shared/gcdump/gcdumpnonx86.cpp index 80f24a659c..2492c4d29e 100644 --- a/src/shared/gcdump/gcdumpnonx86.cpp +++ b/src/shared/gcdump/gcdumpnonx86.cpp @@ -70,6 +70,45 @@ PCSTR GetRegName (UINT32 regnum) _snprintf_s(szRegName, ARRAY_SIZE(szRegName), sizeof(szRegName), "r%u", regnum); return szRegName; +#elif defined(TARGET_RISCV64) + switch (regnum) + { + case 0: return "r0"; + case 1: return "ra"; + case 2: return "sp"; + case 3: return "gp"; + case 4: return "tp"; + case 5: return "t0"; + case 6: return "t1"; + case 7: return "t2"; + case 8: return "fp"; + case 9: return "s1"; + case 10: return "a0"; + case 11: return "a1"; + case 12: return "a2"; + case 13: return "a3"; + case 14: return "a4"; + case 15: return "a5"; + case 16: return "a6"; + case 17: return "a7"; + case 18: return "s2"; + case 19: return "s3"; + case 20: return "s4"; + case 21: return "s5"; + case 22: return "s6"; + case 23: return "s7"; + case 24: return "s8"; + case 25: return "s9"; + case 26: return "s10"; + case 27: return "s11"; + case 28: return "t3"; + case 29: return "t4"; + case 30: return "t5"; + case 31: return "t6"; + case 32: return "pc"; + } + + return "???"; #endif } @@ -285,7 +324,7 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, | DECODE_GC_LIFETIMES | DECODE_PROLOG_LENGTH | DECODE_RETURN_KIND -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) | DECODE_HAS_TAILCALLS #endif ), @@ -438,7 +477,7 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, #ifdef TARGET_AMD64 gcPrintf("Wants Report Only Leaf: %u\n", hdrdecoder.WantsReportOnlyLeaf()); -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) gcPrintf("Has tailcalls: %u\n", hdrdecoder.HasTailCalls()); #endif // TARGET_AMD64 #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA diff --git a/src/shared/gcdump/gcinfodecoder.cpp b/src/shared/gcdump/gcinfodecoder.cpp index c8e1bf3a39..adb22d7023 100644 --- a/src/shared/gcdump/gcinfodecoder.cpp +++ b/src/shared/gcdump/gcinfodecoder.cpp @@ -130,7 +130,7 @@ GcInfoDecoder::GcInfoDecoder( int hasStackBaseRegister = headerFlags & GC_INFO_HAS_STACK_BASE_REGISTER; #ifdef TARGET_AMD64 m_WantsReportOnlyLeaf = ((headerFlags & GC_INFO_WANTS_REPORT_ONLY_LEAF) != 0); -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) m_HasTailCalls = ((headerFlags & GC_INFO_HAS_TAILCALLS) != 0); #endif // TARGET_AMD64 int hasSizeOfEditAndContinuePreservedArea = headerFlags & GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS; @@ -361,7 +361,7 @@ bool GcInfoDecoder::IsSafePoint(UINT32 codeOffset) if(m_NumSafePoints == 0) return false; -#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64)|| defined(TARGET_RISCV64) // Safepoints are encoded with a -1 adjustment codeOffset--; #endif @@ -381,7 +381,7 @@ UINT32 GcInfoDecoder::FindSafePoint(UINT32 breakOffset) const UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength)); UINT32 result = m_NumSafePoints; -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) // Safepoints are encoded with a -1 adjustment // but normalizing them masks off the low order bit // Thus only bother looking if the address is odd @@ -428,7 +428,7 @@ void GcInfoDecoder::EnumerateSafePoints(EnumerateSafePointsCallback *pCallback, UINT32 normOffset = (UINT32)m_Reader.Read(numBitsPerOffset); UINT32 offset = DENORMALIZE_CODE_OFFSET(normOffset) + 2; -#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) // Safepoints are encoded with a -1 adjustment offset--; #endif @@ -1742,6 +1742,143 @@ OBJECTREF* GcInfoDecoder::GetCapturedRegister( } #endif // TARGET_UNIX && !FEATURE_REDHAWK +#elif defined(TARGET_RISCV64) + +#if defined(TARGET_UNIX) && !defined(FEATURE_REDHAWK) +OBJECTREF* GcInfoDecoder::GetCapturedRegister( + int regNum, + PREGDISPLAY pRD + ) +{ + _ASSERTE(regNum >= 1 && regNum <= 31); + + // The fields of CONTEXT are in the same order as + // the processor encoding numbers. + + DWORD64 *pR0 = &pRD->pCurrentContext->R0; + + return (OBJECTREF*)(pR0 + regNum); +} +#endif // TARGET_UNIX && !FEATURE_REDHAWK + +OBJECTREF* GcInfoDecoder::GetRegisterSlot( + int regNum, + PREGDISPLAY pRD + ) +{ + _ASSERTE((regNum == 1) || (regNum >= 5 && regNum <= 31)); + +#ifdef FEATURE_REDHAWK + PTR_UIntNative* ppReg = &pRD->pR0; + + return (OBJECTREF*)*(ppReg + regNum); +#else + if(regNum == 1) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->Ra; + } + else if (regNum < 8) + { + return (OBJECTREF*)*(DWORD64**)(&pRD->volatileCurrContextPointers.T0 + (regNum - 5)); + } + else if(regNum == 8) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->Fp; + } + else if (regNum == 9) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->S1; + } + else if (regNum < 18) + { + return (OBJECTREF*)*(DWORD64**)(&pRD->volatileCurrContextPointers.A0 + (regNum - 10)); + } + else if (regNum < 28) + { + return (OBJECTREF*)*(DWORD64**)(&pRD->pCurrentContextPointers->S2 + (regNum-18)); + } + return (OBJECTREF*)*(DWORD64**)(&pRD->volatileCurrContextPointers.T3 + (regNum-28)); +#endif +} + +bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) +{ + _ASSERTE(regNum >= 0 && regNum <= 31); + + return (regNum >= 5 && regNum <= 7) || (regNum >= 10 and regNum <= 17) || regNum >= 28 || regNum == 1; +} + +bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD) +{ +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + _ASSERTE( m_Flags & DECODE_GC_LIFETIMES ); + + TADDR pSlot = (TADDR) GetStackSlot(spOffset, spBase, pRD); + _ASSERTE(pSlot >= pRD->SP); + + return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea); +#else + return FALSE; +#endif +} + +void GcInfoDecoder::ReportRegisterToGC( + int regNum, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + void * hCallBack) +{ + GCINFODECODER_CONTRACT; + + _ASSERTE(regNum > 0 && regNum <= 31); + + LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum )); + + OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD ); +#if defined(TARGET_UNIX) && !defined(FEATURE_REDHAWK) && !defined(SOS_TARGET_RISCV64) + // On PAL, we don't always have the context pointers available due to + // a limitation of an unwinding library. In such case, the context + // pointers for some nonvolatile registers are NULL. + // In such case, we let the pObjRef point to the captured register + // value in the context and pin the object itself. + if (pObjRef == NULL) + { + // Report a pinned object to GC only in the promotion phase when the + // GC is scanning roots. + GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack); + if (!pGCCtx->sc->promotion) + { + return; + } + + pObjRef = GetCapturedRegister(regNum, pRD); + + gcFlags |= GC_CALL_PINNED; + } +#endif // TARGET_UNIX && !SOS_TARGET_RISCV64 + +#ifdef _DEBUG + if(IsScratchRegister(regNum, pRD)) + { + // Scratch registers cannot be reported for non-leaf frames + _ASSERTE(flags & ActiveStackFrame); + } + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */ + "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) )); + + VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef); + + LOG_PIPTR(pObjRef, gcFlags, hCallBack); +#endif //_DEBUG + + gcFlags |= CHECK_APP_DOMAIN; + + pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false))); +} + #else // Unknown platform OBJECTREF* GcInfoDecoder::GetRegisterSlot( @@ -1827,6 +1964,8 @@ int GcInfoDecoder::GetStackReg(int spBase) int esp = 13; #elif defined(TARGET_ARM64) int esp = 31; +#elif defined(TARGET_RISCV64) + int esp = 2; #endif if( GC_SP_REL == spBase ) diff --git a/src/shared/gcdump/gcinfodumper.cpp b/src/shared/gcdump/gcinfodumper.cpp index 41c427e635..79b09ae040 100644 --- a/src/shared/gcdump/gcinfodumper.cpp +++ b/src/shared/gcdump/gcinfodumper.cpp @@ -185,6 +185,44 @@ BOOL GcInfoDumper::ReportPointerRecord ( REG(Lr, Lr), { FIELD_OFFSET(T_CONTEXT, Sp) }, #undef REG +#elif defined(TARGET_RISCV64) +#undef REG +#define REG(reg, field) { offsetof(RiscV64VolatileContextPointer, field) } + REG(zero, R0), + REG(a0, A0), + REG(a1, A1), + REG(a2, A2), + REG(a3, A3), + REG(a4, A4), + REG(a5, A5), + REG(a6, A6), + REG(a7, A7), + REG(t0, T0), + REG(t1, T1), + REG(t2, T2), + REG(t3, T3), + REG(t4, T4), + REG(t5, T5), + REG(t6, T6), +#undef REG +#define REG(reg, field) { offsetof(T_KNONVOLATILE_CONTEXT_POINTERS, field) } + REG(s1, S1), + REG(s2, S2), + REG(s3, S3), + REG(s4, S4), + REG(s5, S5), + REG(s6, S6), + REG(s7, S7), + REG(s8, S8), + REG(s9, S9), + REG(s10, S10), + REG(s11, S11), + REG(ra, Ra), + REG(gp, Gp), + REG(tp, Tp), + REG(fp, Fp), + { offsetof(T_CONTEXT, Sp) }, +#undef REG #else PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this platform.") #endif @@ -202,6 +240,9 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this #elif defined(TARGET_ARM) iSPRegister = (FIELD_OFFSET(T_CONTEXT, Sp) - FIELD_OFFSET(T_CONTEXT, R0)) / sizeof(ULONG); UINT iBFRegister = m_StackBaseRegister; +#elif defined(TARGET_RISCV64) + assert(!"unimplemented on RISCV64 yet"); + iSPRegister = 0; #endif #if defined(TARGET_ARM) || defined(TARGET_ARM64) @@ -597,6 +638,9 @@ GcInfoDumper::EnumerateStateChangesResults GcInfoDumper::EnumerateStateChanges ( { *(ppVolatileReg+iReg) = ®disp.pCurrentContext->X0 + iReg; } +#elif defined(TARGET_RISCV64) +#pragma message("Unimplemented for RISCV64 yet.") + assert(!"unimplemented on RISCV64 yet"); #else PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on this platform.") #endif diff --git a/src/shared/inc/gcinfodecoder.h b/src/shared/inc/gcinfodecoder.h index 6ccdbe4aae..2b18fd0dd2 100644 --- a/src/shared/inc/gcinfodecoder.h +++ b/src/shared/inc/gcinfodecoder.h @@ -85,6 +85,8 @@ inline TADDR GetSP(T_CONTEXT* context) return (TADDR)context->Sp; #elif defined(TARGET_ARM64) return (TADDR)context->Sp; +#elif defined(TARGET_RISCV64) + return (TADDR)context->Sp; #else _ASSERTE(!"nyi for platform"); #endif @@ -98,6 +100,8 @@ inline PCODE GetIP(T_CONTEXT* context) return (PCODE)context->Pc; #elif defined(TARGET_ARM64) return (PCODE)context->Pc; +#elif defined(TARGET_RISCV64) + return (PCODE)context->Pc; #else _ASSERTE(!"nyi for platform"); #endif @@ -214,7 +218,7 @@ enum GcInfoDecoderFlags DECODE_RETURN_KIND = 0x2000, #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) DECODE_HAS_TAILCALLS = 0x4000, -#endif // TARGET_ARM || TARGET_ARM64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_RISCV64 }; enum GcInfoHeaderFlags diff --git a/src/shared/pal/prebuilt/inc/cordebug.h b/src/shared/pal/prebuilt/inc/cordebug.h index 5feeaff766..0d4d04e81b 100644 --- a/src/shared/pal/prebuilt/inc/cordebug.h +++ b/src/shared/pal/prebuilt/inc/cordebug.h @@ -9195,15 +9195,15 @@ enum CorDebugRegister REGISTER_ARM64_V30 = ( REGISTER_ARM64_V29 + 1 ) , REGISTER_ARM64_V31 = ( REGISTER_ARM64_V30 + 1 ) , REGISTER_RISCV64_PC = 0, - REGISTER_RISCV64_RA = ( REGISTER_RISCV64_PC + 1), - REGISTER_RISCV64_SP = ( REGISTER_RISCV64_RA + 1), - REGISTER_RISCV64_GP = ( REGISTER_RISCV64_SP + 1), + REGISTER_RISCV64_SP = ( REGISTER_RISCV64_PC + 1 ), + REGISTER_RISCV64_FP = ( REGISTER_RISCV64_SP + 1 ), + REGISTER_RISCV64_RA = ( REGISTER_RISCV64_FP + 1 ), + REGISTER_RISCV64_GP = ( REGISTER_RISCV64_RA + 1 ), REGISTER_RISCV64_TP = ( REGISTER_RISCV64_GP + 1 ), REGISTER_RISCV64_T0 = ( REGISTER_RISCV64_TP + 1 ), REGISTER_RISCV64_T1 = ( REGISTER_RISCV64_T0 + 1 ), REGISTER_RISCV64_T2 = ( REGISTER_RISCV64_T1 + 1 ), - REGISTER_RISCV64_FP = ( REGISTER_RISCV64_T2 + 1 ), - REGISTER_RISCV64_S1 = ( REGISTER_RISCV64_FP + 1 ), + REGISTER_RISCV64_S1 = ( REGISTER_RISCV64_T2 + 1 ), REGISTER_RISCV64_A0 = ( REGISTER_RISCV64_S1 + 1 ), REGISTER_RISCV64_A1 = ( REGISTER_RISCV64_A0 + 1 ), REGISTER_RISCV64_A2 = ( REGISTER_RISCV64_A1 + 1 ), diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShim.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShim.cs index 21aa5a6e4a..40458a8418 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShim.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShim.cs @@ -63,6 +63,17 @@ public async Task ResumeRuntime(TimeSpan timeout) } } + public async Task StartEventPipeSession(EventPipeSessionConfiguration config, TimeSpan timeout) + { + if (_useAsync) + { + CancellationTokenSource cancellation = new(timeout); + return await _client.StartEventPipeSessionAsync(config, cancellation.Token).ConfigureAwait(false); + } + + throw new NotSupportedException($"{nameof(StartEventPipeSession)} with config parameter is only supported on async path"); + } + public async Task StartEventPipeSession(IEnumerable providers, TimeSpan timeout) { if (_useAsync) diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShimExtensions.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShimExtensions.cs index dc5492e5fe..2f83077f44 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShimExtensions.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClientApiShimExtensions.cs @@ -37,6 +37,11 @@ public static Task StartEventPipeSession(this DiagnosticsClien return shim.StartEventPipeSession(provider, DefaultPositiveVerificationTimeout); } + public static Task StartEventPipeSession(this DiagnosticsClientApiShim shim, EventPipeSessionConfiguration config) + { + return shim.StartEventPipeSession(config, DefaultPositiveVerificationTimeout); + } + public static Task EnablePerfMap(this DiagnosticsClientApiShim shim, PerfMapType type) { return shim.EnablePerfMap(type, DefaultPositiveVerificationTimeout); diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs index 9bfeb9c9ec..e5344e0742 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs @@ -4,10 +4,14 @@ using System; using System.Collections.Generic; using System.Diagnostics.Tracing; +using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Diagnostics.TestHelpers; using Microsoft.Diagnostics.Tracing; +using Microsoft.Diagnostics.Tracing.Etlx; +using Microsoft.Diagnostics.Tracing.Session; using Xunit; using Xunit.Abstractions; using Xunit.Extensions; @@ -170,5 +174,70 @@ private async Task StartEventPipeSessionWithSingleProviderTestCore(TestConfigura Assert.True(session.EventStream != null); runner.Stop(); } + + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task StartEventPipeSessionWithoutStackwalkTestAsync(TestConfiguration testConfig) + { + if (testConfig.RuntimeFrameworkVersionMajor < 9) + { + throw new SkipTestException("Not supported on < .NET 9.0"); + } + + await using TestRunner runner = await TestRunner.Create(testConfig, _output, "Tracee"); + await runner.Start(testProcessTimeout: 60_000); + DiagnosticsClientApiShim clientShim = new(new DiagnosticsClient(runner.Pid), useAsync: true); + + var config = new EventPipeSessionConfiguration( + new[] { + new EventPipeProvider("System.Runtime", EventLevel.Informational, 0, new Dictionary() { + { "EventCounterIntervalSec", "1" } + }) + }, + circularBufferSizeMB: 256, + requestRundown: true, + requestStackwalk: true); + + string nettraceFileName = Path.GetTempFileName(); + using (EventPipeSession session = await clientShim.StartEventPipeSession(config)) + { + var tmpFileStream = File.Create(nettraceFileName); + + Task streamTask = Task.Run(() => { + try + { + session.EventStream.CopyTo(tmpFileStream); + } + catch (Exception ex) + { + // This exception can happen if the target process exits while EventPipeEventSource is in the middle of reading from the pipe. + runner.WriteLine($"Error encountered while processing events {ex}"); + } + finally + { + runner.WakeupTracee(); + } + }); + runner.WriteLine("Waiting for stream Task"); + streamTask.Wait(10000); + runner.WriteLine("Done waiting for stream Task"); + session.Stop(); + await streamTask; + + tmpFileStream.Close(); + runner.WriteLine($"EventPipe file is written, size: {new FileInfo(nettraceFileName).Length} bytes"); + } + + string etlxFileName = TraceLog.CreateFromEventPipeDataFile(nettraceFileName); + using (TraceLog log = TraceLog.OpenOrConvert(etlxFileName)) + { + foreach (TraceEvent e in log.Events) + { + runner.WriteLine($"PARSED {e.ProviderName} {e.EventName} {e.CallStack()?.CodeAddress?.FullMethodName}"); + Assert.True(e.CallStack() == null); + } + } + + runner.Stop(); + } } }