# Monitor whether DPTF/INT3404 sends fan control: ETW trace + high-frequency _FST poll. # Run as Administrator. Needs ectest (ec-test-app) for _FST polling. # -Mode watch: poll _FST, print only when FAND *changes* (INT3404 sends _FSL => FAND changes). param( [ValidateSet("check", "poll", "watch", "etw", "all")] [string]$Mode = "check", [string]$EctestPath = "C:\Users\Jack\ec-test-app\exe\x64\Release\ectest.exe", [int]$PollSeconds = 60, [double]$PollIntervalMs = 200, [string]$LogDir = $PSScriptRoot ) $ErrorActionPreference = "Stop" # ---- 1) Check DPTF presence ---- function Get-DptfPresence { Write-Host "=== DPTF / thermal stack check ===" -ForegroundColor Cyan $svc = Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "dptf|esif|intel.*thermal|dynamic.*thermal" } if ($svc) { $svc | Format-Table Name, Status, DisplayName -AutoSize } else { Write-Host "No DPTF/ESIF/thermal service found by name." -ForegroundColor Yellow } $drv = Get-WmiObject Win32_PnPSignedDriver -ErrorAction SilentlyContinue | Where-Object { $_.DeviceName -match "DPTF|Dynamic Platform|Intel.*Thermal|ESIF" } if ($drv) { $drv | Select-Object DeviceName, DriverVersion | Format-Table -AutoSize } else { Write-Host "No DPTF/thermal driver found by name." -ForegroundColor Yellow } $acpiByClass = Get-PnpDevice -Class ACPI -ErrorAction SilentlyContinue | Where-Object { $_.FriendlyName -match "INT3404|INT3400|DPTF|Thermal|Dynamic Tuning|Fan Participant" } $acpiById = Get-PnpDevice -ErrorAction SilentlyContinue | Where-Object { $_.InstanceId -match "ACPI\\INT340[04]" } $acpi = @($acpiByClass) + @($acpiById) | Sort-Object -Property InstanceId -Unique if ($acpi) { Write-Host "ACPI devices (DPTF/thermal/fan, INT3404/INT3400):" -ForegroundColor Green $acpi | Select-Object Class, Status, FriendlyName, InstanceId | Format-Table -AutoSize } else { Write-Host "No DPTF-related ACPI device (INT3404/INT3400) found." -ForegroundColor Yellow Write-Host " -> INT3404 = DPTF fan device (_FSL); INT3400 = thermal. Without these, Windows/DPTF cannot send fan control." -ForegroundColor DarkGray } $any = Get-PnpDevice -ErrorAction SilentlyContinue | Where-Object { $_.FriendlyName -match "Dynamic Tuning|Fan Participant" } if ($any -and -not $acpi) { Write-Host "Dynamic Tuning devices (any class):" -ForegroundColor Cyan $any | Select-Object Class, Status, FriendlyName | Format-Table -AutoSize Write-Host " -> Check device Details for 'Device instance path'; if ACPI\INT3404\0, the fan device is present but was missed by class filter." -ForegroundColor DarkGray } Write-Host "" # Power / thermal (DPTF may be gated by power plan or OEM settings) Write-Host "=== Power / thermal (for DPTF fan control) ===" -ForegroundColor Cyan try { $active = powercfg /getactivescheme if ($active) { Write-Host "Active power scheme:" -ForegroundColor Green $active } } catch {} Write-Host "Manual check:" -ForegroundColor Yellow Write-Host " 1. Settings -> System -> Power -> Power mode / Additional power settings" -ForegroundColor DarkGray Write-Host " 2. Control Panel -> Power Options -> Change plan settings -> Change advanced power settings" -ForegroundColor DarkGray Write-Host " Look for: Processor power, Intel, Thermal, or OEM (e.g. Acer) thermal/fan." -ForegroundColor DarkGray Write-Host " 3. Device Manager -> Intel Dynamic Tuning Fan Participant -> Properties -> Driver: ensure enabled, no errors." -ForegroundColor DarkGray Write-Host " 4. OEM app (e.g. Acer Care, Lenovo Vantage): turn OFF any 'quiet/silent fan' or 'disable DPTF'." -ForegroundColor DarkGray Write-Host "" } # ---- 2) High-frequency _FST poll and log ---- function Start-FstPollLog { if (-not (Test-Path $EctestPath)) { Write-Host "ectest not found: $EctestPath" -ForegroundColor Red return } $ts = Get-Date -Format "yyyyMMdd_HHmmss" $csvPath = Join-Path $LogDir "FST_poll_$ts.csv" $end = [DateTime]::UtcNow.AddSeconds($PollSeconds) Write-Host "Polling _FST every $PollIntervalMs ms for $PollSeconds s. Log: $csvPath" -ForegroundColor Cyan Write-Host "Apply CPU load (e.g. stress) to see if FAND changes. Ctrl+C to stop early." -ForegroundColor Yellow $headers = "TimeUtc,ElapsedMs,FAND" $headers | Set-Content $csvPath -Encoding UTF8 $t0 = [DateTime]::UtcNow $lastFand = $null $changeCount = 0 while ([DateTime]::UtcNow -lt $end) { $out = & $EctestPath -acpi "\_SB.ECT0._FST" 2>$null $fand = $null $capture = $false foreach ($line in ($out -split "`n")) { if ($line -match 'Argument\[1\]:') { $capture = $true; continue } if ($capture -and $line -match 'Integer Value:\s*(0x[0-9A-Fa-f]+|\d+)') { $fand = $Matches[1] break } } $elapsed = ([DateTime]::UtcNow - $t0).TotalMilliseconds $val = if ($fand) { $fand } else { "N/A" } $line = "{0:O},{1:F0},{2}" -f [DateTime]::UtcNow, $elapsed, $val Add-Content $csvPath $line -Encoding UTF8 if ($null -ne $lastFand -and $fand -ne $lastFand) { $changeCount++ } $lastFand = $fand [System.Threading.Thread]::Sleep([int]$PollIntervalMs) } Write-Host "Done. FAND changed $changeCount times. Log: $csvPath" -ForegroundColor Green } # ---- 2b) Watch FAND: print only when value changes (see if INT3404 sends _FSL) ---- function Start-FstWatch { if (-not (Test-Path $EctestPath)) { Write-Host "ectest not found: $EctestPath" -ForegroundColor Red return } $ts = Get-Date -Format "yyyyMMdd_HHmmss" $csvPath = Join-Path $LogDir "FST_watch_$ts.csv" $end = [DateTime]::UtcNow.AddSeconds($PollSeconds) Write-Host "=== INT3404 / FAND watch (FAND change = something wrote _FSL) ===" -ForegroundColor Cyan Write-Host "Polling _FST every $PollIntervalMs ms for $PollSeconds s. Log: $csvPath" -ForegroundColor Cyan Write-Host "Apply CPU load; if you see 'FAND changed' below, INT3404 or host wrote _FSL." -ForegroundColor Yellow Write-Host "" "TimeUtc,ElapsedMs,FAND,Changed" | Set-Content $csvPath -Encoding UTF8 $t0 = [DateTime]::UtcNow $lastFand = $null $changeCount = 0 while ([DateTime]::UtcNow -lt $end) { $out = & $EctestPath -acpi "\_SB.ECT0._FST" 2>$null $fand = $null $capture = $false foreach ($line in ($out -split "`n")) { if ($line -match 'Argument\[1\]:') { $capture = $true; continue } if ($capture -and $line -match 'Integer Value:\s*(0x[0-9A-Fa-f]+|\d+)') { $fand = $Matches[1] break } } $val = if ($fand) { $fand } else { "N/A" } $elapsed = ([DateTime]::UtcNow - $t0).TotalMilliseconds $changed = $false if ($null -ne $lastFand -and $fand -ne $lastFand) { $changeCount++ $changed = $true Write-Host (" {0} FAND changed: {1} -> {2}" -f (Get-Date -Format "HH:mm:ss"), $lastFand, $val) -ForegroundColor Green } $flag = if ($changed) { "1" } else { "0" } $line = "{0:O},{1:F0},{2},{3}" -f [DateTime]::UtcNow, $elapsed, $val, $flag Add-Content $csvPath $line -Encoding UTF8 $lastFand = $fand [System.Threading.Thread]::Sleep([int]$PollIntervalMs) } Write-Host "" if ($changeCount -eq 0) { Write-Host "FAND never changed => INT3404 did not send _FSL (or EC overwrote)." -ForegroundColor Yellow } else { Write-Host "FAND changed $changeCount times => fan control was written (e.g. by INT3404)." -ForegroundColor Green } Write-Host "Log: $csvPath" -ForegroundColor Cyan } # ---- 3) ETW trace (DPTF / ESIF) ---- function Start-DptfEtwTrace { $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host "ETW mode requires Administrator. Right-click PowerShell -> Run as administrator, then run again." -ForegroundColor Red return } $ts = Get-Date -Format "yyyyMMdd_HHmmss" $etlPath = Join-Path $LogDir "DPTF_trace_$ts.etl" $sessionName = "DptfMonitorSession" # Remove if leftover (suppress "data collector set not found" and "access denied" when not admin) $null = cmd /c "logman stop $sessionName 2>nul" $null = cmd /c "logman delete $sessionName 2>nul" # Prefer ESIF low-level (often present when DPTF is installed) $providers = @( @{ N = "EsifLfEtwProvider"; K = "0x200000"; L = 5 }, @{ N = "EsifUmdf2EtwProvider"; K = "0xFFFFFFFF"; L = 5 } ) $added = $false foreach ($p in $providers) { $err = & { logman create trace $sessionName -p $p.N $p.K $p.L -o $etlPath -ets 2>&1 } if ($LASTEXITCODE -eq 0) { Write-Host "Created trace with provider: $($p.N)" -ForegroundColor Green $added = $true break } } if (-not $added) { Write-Host "Could not create trace. Possible causes:" -ForegroundColor Red Write-Host " - Not running as Administrator (required for ETW)." -ForegroundColor DarkGray Write-Host " - DPTF ETW providers (EsifLfEtwProvider etc.) not registered on this build." -ForegroundColor DarkGray Write-Host "You can still use -Mode watch to see if FAND changes." -ForegroundColor Yellow return } logman start $sessionName -ets Write-Host "ETW trace started. Run CPU stress for 1-2 min, then press Enter to stop..." -ForegroundColor Yellow Read-Host logman stop $sessionName -ets $csvOut = $etlPath -replace '\.etl$', '.csv' tracerpt $etlPath -o $csvOut -of CSV 2>$null if (Test-Path $csvOut) { Write-Host "Trace summary: $csvOut" -ForegroundColor Green } Write-Host "Raw ETL: $etlPath" -ForegroundColor Cyan logman delete $sessionName -ets 2>$null } # ---- Main ---- switch ($Mode) { "check" { Get-DptfPresence } "poll" { Get-DptfPresence Start-FstPollLog } "watch" { Get-DptfPresence Start-FstWatch } "etw" { Get-DptfPresence Start-DptfEtwTrace } "all" { Get-DptfPresence Start-DptfEtwTrace Write-Host "Now polling _FST for $PollSeconds s..." -ForegroundColor Cyan Start-FstPollLog } }