Skip to content

Commit

Permalink
WiP - Use custom script wrapper for all km tests to capture user, km …
Browse files Browse the repository at this point in the history
…dumps on hangs
  • Loading branch information
Dhiren Vispute authored and Dhiren Vispute committed Feb 26, 2024
1 parent 12f73c3 commit f421d23
Show file tree
Hide file tree
Showing 7 changed files with 802 additions and 327 deletions.
226 changes: 226 additions & 0 deletions scripts/Run-Self-Hosted-Runner-Test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# Copyright (c) Microsoft Corporation
# SPDX-License-Identifier: MIT
#
# This script executes the provided test command, waits for <TestHangTimeout> (in seconds) and then triggers user and
# kernel dumps if the test process is still ruunning (This is typically the case when the test process is hung for some
# reason).
#
# The user mode dump is created using 'procdump64.exe' and the kernel dump using 'notmyfault64.exe' utilities
# respectively, both from the SysInternals Suite. The script assumes the presence of these utilities under the current
# working directory.
#
# (While 'procdump64.exe' also has the ability to generate a kernel dump, that dump is restricted to the kernel
# portions of the user mode app's threads _only_ and does not provide any visibility into other kernel threads. This
# script therefore uses the 'notmyfault64.exe' tool to generate a 'true' kernel dump which captures the state of all
# kernel threads and related data structures)
#

[CmdletBinding()]
Param(
[Parameter(Mandatory = $true)] [string] $TestCommand,
[Parameter(Mandatory = $false)] [string] $TestArguments = "",
[Parameter(Mandatory = $false)] [int] $TestHangTimeout = 3600,
[Parameter(Mandatory = $false)] [string] $UserModeDumpFolder = "C:\Dumps",
[Parameter(Mandatory = $false)] [bool] $NeedKernelDump = $true
)

# Finds and returns the specified tool's location under the current directory. If not found, throws an exception.
function GetToolLocationPath
{
param(
[Parameter(Mandatory = $True)] [string] $ToolName
)

$ToolLocationPath = Get-ChildItem -Path "$Pwd" `
-Recurse -Filter "$ToolName" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($ToolLocationPath -eq $null) {
$ErrorMessage = "*** ERROR *** $ToolName not found under $Pwd."
Write-output $ErrorMessage
Start-Sleep -seconds 5
throw $ErrorMessage
}

return $ToolLocationPath.FullName
}

function ThrowWithErrorMessage
{
Param(
[Parameter(Mandatory = $True)] [string] $ErrorMessage
)

Write-Log $ErrorMessage
# Wait a bit to let the above message to show up.
Start-Sleep -seconds 5
throw $ErrorMessage
}

if ($VerbosePreference -eq 'Continue') {
Write-Output "Command : $TestCommand"
Write-Output "Arguments : $TestArguments"
Write-Output "Test Hang Timeout : $TestHangTimeout"
Write-Output "User mode dump Folder : $UserModeDumpFolder"
Write-Output "Kernel dump needed : $NeedKernelDump"
}

# Verify timeout is non-zero.
if ($TestHangTimeout -le 0) {
ThrowWithErrorMessage `
-ErrorMessage "*** ERROR *** Invalid test hang timeout value: $TestHangTimeout"
}

# Create dump folder if not present.
if (-not (Test-Path -Path $UserModeDumpFolder)) {
New-Item -Path $UserModeDumpFolder -ItemType Directory -Force

# verify dump folder presence.
if (-not (Test-Path -Path $UserModeDumpFolder)) {
ThrowWithErrorMessage `
-ErrorMessage "*** ERROR *** User mode dump folder not found: $UserModeDumpFolder"
}
}

# Check if procdump64.exe and notmyfault64.exe are present on the system.
$ProcDumpBinary = "ProcDump64.exe"
Write-Log "Verifying $ProcDumpBinary presence in $Pwd..."
$ProcDumpBinaryPath = GetToolLocationPath -ToolName $ProcDumpBinary
Write-Log "$ProcDumpBinary location: $ProcDumpBinaryPath"
Write-Log "`n"

$NotMyFaultBinary = "NotMyFault64.exe"
Write-Log "Verifying $NotMyFaultBinary presence in $Pwd..."
$NotMyFaultBinaryPath = GetToolLocationPath -ToolName $NotMyFaultBinary
Write-Log "$NotMyFaultBinary location: $NotMyFaultBinaryPath"
Write-Log "`n"

# While at it, enable EULA for all SysInternals tools.
REG ADD HKCU\Software\Sysinternals /v EulaAccepted /t REG_DWORD /d 1 /f | Out-Null

# The following command will enable full memory dump.
# Not enabled yet as it needs a VM with an explicitly created page file of at least (physical_memory + 1MB) in size.
# The default value of the 'CrashDumpEnabled' key is 7 ('automatic' sizing of dump file size (system determined)).
#
# Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\CrashControl' -Name 'CrashDumpEnabled' -Value 1
#
if ($VerbosePreference -eq 'Continue') {
# Dump current kernel mode dump settings.
Write-Log "`n"
Write-Log "Current kernel dump configuration:`n"
$KernelDumpInfo = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\CrashControl'
$Lines = $KernelDumpInfo -split '; '
foreach($Line in $Lines) {
Write-Log "`t$Line"
}
Write-Log "`n"
}

$BeforeTestFreeSpaceGB = (((Get-Volume C).SizeRemaining) / 1GB).ToString("F2")
Write-Log "Available disk space (Before test start): $BeforeTestFreeSpaceGB GB"

$FullTestCommandSpec = Join-Path $Pwd $TestCommand

# Start the test process using the provided command and arguments.
Write-Log "`n`n"
Write-Log "Staring Test command: $FullTestCommandSpec $TestArguments"
Write-Log "Test hang timeout: $TestHangTimeout (seconds)"
Write-Log "`n"

# Create a temporary file for standard output and error.
$TestTempOutFile = [System.IO.Path]::GetTempFileName()

# Form the complete command line with output redirection for cmd.exe
$TestCommandLine = "$FullTestCommandSpec $($TestArguments) > $TestTempOutFile 2>&1"

# Start the test process and wait for it to exit or timeout
$TestProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c $TestCommandLine" -PassThru -NoNewWindow -Wait
$WaitResult = $TestProcess.WaitForExit($TestHangTimeout * 1000)

if (-not $WaitResult) {
Write-Log "`n"
Write-Log "*** ERROR *** Test execution hang timeout ($TestHangTimeout seconds) expired.`n"

# First generate a user mode dump.
$UserModeDumpFileName = "$($TestCommand)_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').dmp"
$UserModeDumpFilePath = Join-Path $UserModeDumpFolder $UserModeDumpFileName

if ($VerbosePreference -eq 'Continue') {
Write-Log "User mode Dumpfile name: $UserModeDumpFileName"
Write-Log "User mode Dumpfile Path: $UserModeDumpFilePath"
}

$DriveFreeSpaceGB = (((Get-Volume C).SizeRemaining) /1GB).ToString("F2")
Write-Log "Current available disk space: $DriveFreeSpaceGB GB`n"

Write-Log "Creating User mode dump @ $UserModeDumpFilePath"
$ProcDumpArguments = "-r -ma $($TestProcess.Id) $UserModeDumpFilePath"
Write-Log "Dump Command: $ProcDumpBinaryPath $ProcDumpArguments"
$ProcDumpProcess = Start-Process -NoNewWindow `
-FilePath $ProcDumpBinaryPath `
-ArgumentList $ProcDumpArguments `
-Wait -PassThru
Write-Log "Waiting for user mode dump to complete..."
$ProcDumpProcess.WaitForExit()

# Get procdump64.exe's exit code.
$ExitCode = $($ProcDumpProcess.ExitCode)
Write-Log "$ProcDumpBinaryPath completed with exit code: $ExitCode`n"
Write-Log "User mode dump completed. Flushing disk buffers..."
Write-VolumeCache -DriveLetter C

# Wait for a bit for things to settle down to ensure the user mode dump is completely flushed.
Start-Sleep -seconds 10

$UserModeDumpSizeMB =
(((Get-ItemProperty -Path $UserModeDumpFilePath).Length) /1MB).ToString("F2")
if ($UserModeDumpSizeMB -eq 0) {
Write-Log "* WARNING * User mode dump $UserModeDumpFilePath NOT CREATED"
} else {
Write-Log "`n`n"
Write-Log "Created $UserModeDumpFilePath, size: $UserModeDumpSizeMB MB"
Write-Log "`n`n"
}

if ($NeedKernelDump) {
Write-Log "Creating kernel dump...`n"
# Wait a bit for the above message to show up in the log.
Start-Sleep -seconds 5

# This will/should not return (system will/should bluescreen and reboot).
Start-Process -NoNewWindow -Wait -FilePath $NotMyFaultBinaryPath -ArgumentList "/crash"

# If we get here, notmyfault64.exe failed for some reason. Kill the hung process, throw error.
$TestProcess.Kill()
ThrowWithErrorMessage `
-ErrorMessage "*** ERROR *** $($PSCommandPath): kernel mode dump creation FAILED"
} else {
Write-Log "`n`n"
Write-Log "Kernel dump not needed, killing test process $($TestProcess.ProcessName)..."
$TestProcess.Kill()
Write-Log "`n`n"

$ErrorMessage = "*** ERROR *** $($PSCommandPath): $FullTestCommandSpec.exe exceeded test hang timout of " +
"$TestHangTimeout seconds"
ThrowWithErrorMessage -ErrorMessage $ErrorMessage
}
} else {

# Ensure the process has completely exited.
$TestProcess.WaitForExit()

# Read and display the output (if any) from the temporary output file.
$TestStandardOutput = Get-Content -Path $TestTempOutFile
if ($TestStandardOutput) {
Write-Log "`n`n"
Write-Log "Test Program output:`n"
foreach($Line in $TestStandardOutput) {
Write-Log "$Line"
}
}
Write-Log "`n"

if ($($TestProcess.ExitCode) -ne 0) {
$ErrorMessage = "*** ERROR *** $($PSCommandPath): $FullTestCommandSpec " +
"exited with exit code: $($TestProcess.ExitCode)"
ThrowWithErrorMessage -ErrorMessage $ErrorMessage
}
}
17 changes: 15 additions & 2 deletions scripts/config_test_vm.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ function Restore-AllVMs
{
param ([Parameter(Mandatory=$True)] $VMList)
foreach ($VM in $VMList) {
$VMName = $VM.Name
Write-Log "Restoring VM $VMName"
Restore-VMSnapshot -Name 'baseline' -VMName $VM.Name -Confirm:$false
Restore-VMSnapshot -Name 'baseline' -VMName $VMName -Confirm:$false
}
}

Expand Down Expand Up @@ -380,8 +381,18 @@ function Get-RegressionTestArtifacts
{
$ArifactVersionList = @("0.11.0")
$RegressionTestArtifactsPath = "$pwd\regression"
if (Test-Path -Path $RegressionTestArtifactsPath) {
Remove-Item -Path $RegressionTestArtifactsPath -Recurse -Force
}
mkdir $RegressionTestArtifactsPath

# verify Artifacts' folder presense
if (-not (Test-Path -Path $RegressionTestArtifactsPath)) {
$ErrorMessage = "*** ERROR *** Regression test artifacts folder not found: $RegressionTestArtifactsPath)"
Write-Log $ErrorMessage
throw $ErrorMessage
}

# Download regression test artifacts for each version.
foreach ($ArtifactVersion in $ArifactVersionList)
{
Expand All @@ -407,9 +418,11 @@ function Get-Duonic {
# Download and extract https://github.com/microsoft/corenet-ci.
$DownloadPath = "$pwd\corenet-ci"
mkdir $DownloadPath
Write-Host "Downloading CoreNet-CI"
Write-Host "Downloading CoreNet-CI to $DownloadPath"
Invoke-WebRequest -Uri "https://github.com/microsoft/corenet-ci/archive/refs/heads/main.zip" -OutFile "$DownloadPath\corenet-ci.zip"
Expand-Archive -Path "$DownloadPath\corenet-ci.zip" -DestinationPath $DownloadPath -Force
Move-Item -Path "$DownloadPath\corenet-ci-main\vm-setup\duonic\*" -Destination $pwd -Force
Move-Item -Path "$DownloadPath\corenet-ci-main\vm-setup\procdump64.exe" -Destination $pwd -Force
Move-Item -Path "$DownloadPath\corenet-ci-main\vm-setup\notmyfault64.exe" -Destination $pwd -Force
Remove-Item -Path $DownloadPath -Force -Recurse
}
61 changes: 46 additions & 15 deletions scripts/execute_ebpf_cicd_tests.ps1
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# Copyright (c) Microsoft Corporation
# SPDX-License-Identifier: MIT

param ([parameter(Mandatory=$false)][string] $AdminTarget = "TEST_VM",
[parameter(Mandatory=$false)][string] $StandardUserTarget = "TEST_VM_STANDARD",
[parameter(Mandatory=$false)][string] $LogFileName = "TestLog.log",
[parameter(Mandatory=$false)][string] $WorkingDirectory = $pwd.ToString(),
[parameter(Mandatory=$false)][string] $TestExecutionJsonFileName = "test_execution.json",
[parameter(Mandatory=$false)][bool] $Coverage = $false,
[parameter(Mandatory=$false)][string] $TestMode = "CI/CD",
[parameter(Mandatory=$false)][string[]] $Options = @("None"),
[parameter(Mandatory=$false)][string] $SelfHostedRunnerName)
param ([parameter(Mandatory = $false)][string] $AdminTarget = "TEST_VM",
[parameter(Mandatory = $false)][string] $StandardUserTarget = "TEST_VM_STANDARD",
[parameter(Mandatory = $false)][string] $LogFileName = "TestLog.log",
[parameter(Mandatory = $false)][string] $WorkingDirectory = $pwd.ToString(),
[parameter(Mandatory = $false)][string] $TestExecutionJsonFileName = "test_execution.json",
[parameter(Mandatory = $false)][bool] $Coverage = $false,
[parameter(Mandatory = $false)][string] $TestMode = "CI/CD",
[parameter(Mandatory = $false)][string[]] $Options = @("None"),
[parameter(Mandatory = $false)][string] $SelfHostedRunnerName,
[parameter(Mandatory = $false)][int] $TestHangTimeout = 3600,
[parameter(Mandatory = $false)][string] $UserModeDumpFolder = "C:\Dumps"
)

Push-Location $WorkingDirectory

Expand All @@ -18,7 +21,18 @@ $StandardUserTestVMCredential = Get-StoredCredential -Target $StandardUserTarget

# Load other utility modules.
Import-Module .\common.psm1 -Force -ArgumentList ($LogFileName) -WarningAction SilentlyContinue
Import-Module .\vm_run_tests.psm1 -Force -ArgumentList ($AdminTestVMCredential.UserName, $AdminTestVMCredential.Password, $StandardUserTestVMCredential.UserName, $StandardUserTestVMCredential.Password, $WorkingDirectory, $LogFileName) -WarningAction SilentlyContinue
Import-Module .\vm_run_tests.psm1 `
-Force `
-ArgumentList (
$AdminTestVMCredential.UserName,
$AdminTestVMCredential.Password,
$StandardUserTestVMCredential.UserName,
$StandardUserTestVMCredential.Password,
$WorkingDirectory,
$LogFileName,
$TestHangTimeout,
$UserModeDumpFolder) `
-WarningAction SilentlyContinue

# Read the test execution json.
$Config = Get-Content ("{0}\{1}" -f $PSScriptRoot, $TestExecutionJsonFileName) | ConvertFrom-Json
Expand All @@ -30,6 +44,8 @@ foreach ($VM in $VMList) {
-VMName $VM.Name `
-Coverage $Coverage `
-TestMode $TestMode `
-TestHangTimeout $TestHangTimeout `
-UserModeDumpFolder $UserModeDumpFolder `
-Options $Options
}

Expand All @@ -38,13 +54,28 @@ foreach ($VM in $VMList) {
if ($TestMode -eq "CI/CD") {

# Run XDP Tests.
Invoke-XDPTestsOnVM $Config.Interfaces $VMList[0].Name
Invoke-XDPTestsOnVM `
-Interfaces $Config.Interfaces `
-VMName $VMList[0].Name `
-TestHangTimeout $TestHangTimeout `
-UserModeDumpFolder $UserModeDumpFolder

# Run Connect Redirect Tests.
Invoke-ConnectRedirectTestsOnVM $Config.Interfaces $Config.ConnectRedirectTest `
-UserType "Administrator" $VMList[0].Name
Invoke-ConnectRedirectTestsOnVM $Config.Interfaces $Config.ConnectRedirectTest `
-UserType "StandardUser" $VMList[0].Name
Invoke-ConnectRedirectTestsOnVM `
-Interfaces $Config.Interfaces `
-ConnectRedirectTestConfig $Config.ConnectRedirectTest `
-UserType "Administrator" `
-VMName $VMList[0].Name `
-TestHangTimeout $TestHangTimeout `
-UserModeDumpFolder $UserModeDumpFolder

Invoke-ConnectRedirectTestsOnVM `
-Interfaces $Config.Interfaces `
-ConnectRedirectTestConfig $Config.ConnectRedirectTest `
-UserType "StandardUser" `
-VMName $VMList[0].Name `
-TestHangTimeout $TestHangTimeout `
-UserModeDumpFolder $UserModeDumpFolder
}

# Stop eBPF components on test VMs.
Expand Down
Loading

0 comments on commit f421d23

Please sign in to comment.