Files
2026-04-04 15:32:51 +08:00

227 lines
10 KiB
PowerShell
Executable File

# 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
}
}