Preserve formatted NAND boot and Wii Remote pairing, add DMA/SRAM conformance tests and a Wii comparison probe. Keep P1 profiling tools and native update A/B evidence; reject the unhelpful targeted SRAM shortcut.
107 lines
5.0 KiB
PowerShell
107 lines
5.0 KiB
PowerShell
param(
|
|
[Parameter(Mandatory)][string]$OutputDirectory,
|
|
[ValidateRange(30,300)][int]$TimeoutSeconds=120,
|
|
[ValidateRange(0,9223372036854775807)][long]$ProbeAffinityMask=0,
|
|
[switch]$NoTrace
|
|
)
|
|
# Synthetic user-mode ETW qualification. This does not sample CPU or scheduling.
|
|
$ErrorActionPreference='Stop'
|
|
function ConvertTo-P1WindowsArgument([string]$Value) {
|
|
$p1Quoted = '"'
|
|
$p1Slashes = 0
|
|
foreach ($p1Char in $Value.ToCharArray()) {
|
|
if ($p1Char -eq '\') { ++$p1Slashes; continue }
|
|
if ($p1Char -eq '"') {
|
|
$p1Quoted += ('\' * (2 * $p1Slashes + 1)) + '"'
|
|
$p1Slashes = 0
|
|
continue
|
|
}
|
|
$p1Quoted += ('\' * $p1Slashes) + $p1Char
|
|
$p1Slashes = 0
|
|
}
|
|
return $p1Quoted + ('\' * (2 * $p1Slashes)) + '"'
|
|
}
|
|
$p1Repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
|
if ($ProbeAffinityMask -ne 0) {
|
|
$p1Allowed = [Diagnostics.Process]::GetCurrentProcess().ProcessorAffinity.ToInt64()
|
|
if (($ProbeAffinityMask -band $p1Allowed) -ne $ProbeAffinityMask) {
|
|
throw "Probe affinity mask $ProbeAffinityMask is not available to this process."
|
|
}
|
|
}
|
|
$p1Output=[IO.Path]::GetFullPath($(if([IO.Path]::IsPathRooted($OutputDirectory)){
|
|
$OutputDirectory
|
|
}else{
|
|
Join-Path (Get-Location).ProviderPath $OutputDirectory
|
|
}))
|
|
$p1Probe=Join-Path $p1Repo '.starlet_diag_p1/Binaries/Tests/TraceProbeP1.exe'
|
|
$p1PDB=Join-Path $p1Repo '.starlet_diag_p1/pdb/TraceProbeP1.pdb'
|
|
$p1Network=Join-Path $p1Repo '.starlet_diag_p1/Binaries/Network'
|
|
$p1Decoder=Join-Path $p1Repo '.starlet-profile/P1-tools/DecodeP1.exe'
|
|
foreach($p1Input in @($p1Probe,$p1PDB,$p1Network,$p1Decoder)) {
|
|
if(-not (Test-Path -LiteralPath $p1Input)) { throw "Missing local qualification dependency: $p1Input" }
|
|
}
|
|
if(Test-Path -LiteralPath $p1Output) { throw 'Use a NEW directory; trace evidence is never overwritten.' }
|
|
New-Item -ItemType Directory -Path $p1Output | Out-Null
|
|
$p1Journal=Join-Path $p1Output 'synthetic.journal'
|
|
$p1ETL=Join-Path $p1Output 'native.etl'
|
|
$p1Events=Join-Path $p1Output 'events.jsonl'
|
|
$p1Info=[Diagnostics.ProcessStartInfo]::new($p1Probe)
|
|
$p1Info.UseShellExecute=$false
|
|
$p1Info.CreateNoWindow=$true
|
|
$p1Info.RedirectStandardOutput=$true
|
|
$p1Info.RedirectStandardError=$true
|
|
$p1Info.WorkingDirectory=$p1Repo
|
|
if($NoTrace) { $p1Info.Environment.Remove('DOLPHIN_PERF_TRACE') | Out-Null }
|
|
else { $p1Info.Environment['DOLPHIN_PERF_TRACE']='1' }
|
|
$p1Arguments=if($NoTrace) { @($p1Journal,$p1Network) } else { @($p1Journal,$p1Network,$p1ETL) }
|
|
if($p1Info.PSObject.Properties['ArgumentList'] -and $p1Info.ArgumentList) {
|
|
foreach($p1Arg in $p1Arguments) { $p1Info.ArgumentList.Add($p1Arg) }
|
|
} else {
|
|
$p1Info.Arguments=($p1Arguments | ForEach-Object {
|
|
ConvertTo-P1WindowsArgument $_
|
|
}) -join ' '
|
|
}
|
|
$p1Process=[Diagnostics.Process]::Start($p1Info)
|
|
if ($ProbeAffinityMask -ne 0) {
|
|
try {
|
|
$p1Process.ProcessorAffinity = [IntPtr]$ProbeAffinityMask
|
|
} catch {
|
|
$p1Process.Kill() # Only the synthetic probe started by this script.
|
|
$p1Process.WaitForExit()
|
|
throw "Could not pin owned synthetic probe to mask ${ProbeAffinityMask}: $_"
|
|
}
|
|
}
|
|
$p1ObservedAffinity = $p1Process.ProcessorAffinity.ToInt64()
|
|
$p1Stdout=$p1Process.StandardOutput.ReadToEndAsync()
|
|
$p1Stderr=$p1Process.StandardError.ReadToEndAsync()
|
|
if(-not $p1Process.WaitForExit($TimeoutSeconds*1000)) {
|
|
throw "Local probe exceeded $TimeoutSeconds seconds. Its PID is $($p1Process.Id); no process was forcibly stopped."
|
|
}
|
|
$p1Stdout.GetAwaiter().GetResult() | Out-File -LiteralPath (Join-Path $p1Output 'probe.stdout.log') -Encoding utf8
|
|
$p1Stderr.GetAwaiter().GetResult() | Out-File -LiteralPath (Join-Path $p1Output 'probe.stderr.log') -Encoding utf8
|
|
$p1Manifest=[ordered]@{
|
|
Schema=1; Collection=$(if($NoTrace){'NoTrace'}else{'PrivateUserMode'}); Probe=$p1Probe
|
|
ProbeSHA256=(Get-FileHash -LiteralPath $p1Probe).Hash
|
|
ProbePDB=$p1PDB; ProbePDBSHA256=(Get-FileHash -LiteralPath $p1PDB).Hash
|
|
ProbePID=$p1Process.Id; ProbeExitCode=$p1Process.ExitCode
|
|
ProbeAffinityMask=$ProbeAffinityMask; ProbeObservedAffinityMask=$p1ObservedAffinity
|
|
ProbeStartUTC=$p1Process.StartTime.ToUniversalTime().ToString('o')
|
|
ProbeEndUTC=$p1Process.ExitTime.ToUniversalTime().ToString('o')
|
|
ProbeWallSeconds=($p1Process.ExitTime-$p1Process.StartTime).TotalSeconds
|
|
ProbeCPUSeconds=$p1Process.TotalProcessorTime.TotalSeconds
|
|
NativeQualificationPassed=$false
|
|
}
|
|
$p1Manifest | ConvertTo-Json -Depth 5 | Out-File -LiteralPath (Join-Path $p1Output 'manifest.json') -Encoding utf8
|
|
if($p1Process.ExitCode -ne 0) { throw 'Local probe failed; see probe.stderr.log.' }
|
|
if($NoTrace) {
|
|
Write-Output "No-trace fixed-work baseline completed: $p1Output"
|
|
return
|
|
}
|
|
if(-not (Test-Path -LiteralPath $p1ETL)) { throw 'Local probe produced no ETL.' }
|
|
$p1DecodeText=& $p1Decoder $p1ETL $p1Events
|
|
$p1DecodeExit=$LASTEXITCODE
|
|
$p1DecodeText | Out-File -LiteralPath (Join-Path $p1Output 'decoder-summary.json') -Encoding utf8
|
|
if($p1DecodeExit) { throw 'ETL decode/loss check failed; see decoder-summary.json.' }
|
|
Write-Output "Private user-mode trace decoded: $p1Output"
|
|
Write-Output 'This capture has no CPU samples, context switches or disk wait events.'
|