Scripts/compare-blg-report.ps1

#Requires -Version 5.1
# Run on FILESERVER (powershell.exe 5.1). Stop the Perfmon set first.
# Evaluates the .blg only. Paste the output here to compare with the HTML report.
#
# powershell.exe -NoProfile -File .\compare-blg-report.ps1
# powershell.exe -NoProfile -File .\compare-blg-report.ps1 -Start '2026-08-30 13:49' -End '2026-08-30 14:49'

[CmdletBinding()]
param(
    [string]$BlgPath = 'C:\PerfLogs\Admin\SMB_MBF\FILESERVER_20260830-000001\DataCollector01.blg',
    [datetime]$Start,
    [datetime]$End
)

$ErrorActionPreference = 'Stop'

function ConvertTo-SMBeatCompareGB {
    param([double]$Bytes)
    [math]::Round(($Bytes / 1000000000.0), 3)
}

function ConvertFrom-SMBeatCompareNumber {
    param([string]$Text)
    if ([string]::IsNullOrWhiteSpace($Text)) {
        return $null
    }
    $s = $Text.Trim().Trim('"')
    if ($s.Length -eq 0 -or $s -eq '-' -or $s -eq ' ') {
        return $null
    }
    $n = 0.0
    if ([double]::TryParse($s, [System.Globalization.NumberStyles]::Float, [cultureinfo]::GetCultureInfo('de-DE'), [ref]$n)) {
        return $n
    }
    if ([double]::TryParse($s, [System.Globalization.NumberStyles]::Float, [cultureinfo]::InvariantCulture, [ref]$n)) {
        return $n
    }
    return $null
}

function ConvertFrom-SMBeatCompareWhen {
    param([string]$Text)
    $s = $Text.Trim().Trim('"')
    $styles = [System.Globalization.DateTimeStyles]::AllowWhiteSpaces
    $dto = [datetime]::MinValue
    foreach ($cult in @([cultureinfo]::GetCultureInfo('de-DE'), [cultureinfo]::InvariantCulture, [cultureinfo]::GetCultureInfo('en-US'))) {
        if ([datetime]::TryParse($s, $cult, $styles, [ref]$dto)) {
            return $dto
        }
    }
    return $null
}

function Test-SMBeatCompareHeader {
    param(
        [string]$Header,
        [string]$Kind
    )

    $h = $Header.ToLowerInvariant()
    $isShare = ($h -like '*freigabe*' -or $h -like '*share*')
    $isSmb = ($h -like '*smb-server*' -or $h -like '*smb server*' -or $h -like '*smbserver*')
    $isNic = ($h -like '*netzwerkschnittstelle*' -or $h -like '*network interface*')
    $isSent = ($h -like '*gesendete*' -or $h -like '*bytes sent*' -or $h -like '*bytes gesendet*' -or $h -like '*sent bytes*')
    $isRecv = ($h -like '*empfangene*' -or $h -like '*bytes received*' -or $h -like '*bytes empfangen*' -or $h -like '*received bytes*')
    $isPrivat = ($h -like '*privat*')
    $isAdmin = ($h -like '*admin$*' -or $h -like '*ipc$*')

    if ($Kind -eq 'smbServerSent') { return ($isSmb -and -not $isShare -and $isSent) }
    if ($Kind -eq 'smbServerRecv') { return ($isSmb -and -not $isShare -and $isRecv) }
    if ($Kind -eq 'sharePrivatSent') { return ($isShare -and $isPrivat -and $isSent -and -not $isAdmin) }
    if ($Kind -eq 'sharePrivatRecv') { return ($isShare -and $isPrivat -and $isRecv -and -not $isAdmin) }
    if ($Kind -eq 'nicSent') { return ($isNic -and $isSent) }
    if ($Kind -eq 'nicRecv') { return ($isNic -and $isRecv) }
    return $false
}

if ($env:OS -ne 'Windows_NT') {
    throw 'Needs Windows relog.exe. Run on FILESERVER.'
}

$blg = $BlgPath
if (Test-Path -LiteralPath $BlgPath -PathType Container) {
    $found = @(Get-ChildItem -LiteralPath $BlgPath -Filter '*.blg' | Sort-Object LastWriteTime -Descending)
    if ($found.Count -eq 0) {
        throw ('No .blg in {0}' -f $BlgPath)
    }
    $blg = $found[0].FullName
}
if (-not (Test-Path -LiteralPath $blg)) {
    throw ('BLG not found: {0}' -f $blg)
}

$relog = Join-Path $env:SystemRoot 'System32\relog.exe'
if (-not (Test-Path -LiteralPath $relog)) {
    throw 'relog.exe not found.'
}

$csv = Join-Path $env:TEMP ('smbeat-blg-{0}.csv' -f [guid]::NewGuid().ToString('N'))
Write-Host ('BLG : {0}' -f $blg)
& $relog $blg -f csv -o $csv | Out-Host
if (-not (Test-Path -LiteralPath $csv)) {
    throw 'relog did not write CSV. Stop the data collector set and retry.'
}

$lines = @(Get-Content -LiteralPath $csv -Encoding Default | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
if ($lines.Count -lt 2) {
    throw 'CSV has no samples.'
}

$headerLine = $lines[0]
$sep = ','
if ($headerLine.IndexOf(';') -ge 0 -and ($headerLine.Split(';').Count -gt $headerLine.Split(',').Count)) {
    $sep = ';'
}

$headers = @($headerLine.Split($sep))
$idxSmbSent = @()
$idxSmbRecv = @()
$idxPrivSent = @()
$idxPrivRecv = @()
$idxNicSent = @()
$idxNicRecv = @()
for ($i = 1; $i -lt $headers.Count; $i++) {
    $name = $headers[$i]
    if (Test-SMBeatCompareHeader -Header $name -Kind 'smbServerSent') { $idxSmbSent += $i }
    if (Test-SMBeatCompareHeader -Header $name -Kind 'smbServerRecv') { $idxSmbRecv += $i }
    if (Test-SMBeatCompareHeader -Header $name -Kind 'sharePrivatSent') { $idxPrivSent += $i }
    if (Test-SMBeatCompareHeader -Header $name -Kind 'sharePrivatRecv') { $idxPrivRecv += $i }
    if (Test-SMBeatCompareHeader -Header $name -Kind 'nicSent') { $idxNicSent += $i }
    if (Test-SMBeatCompareHeader -Header $name -Kind 'nicRecv') { $idxNicRecv += $i }
}

function Show-SMBeatCompareCols {
    param([string]$Label, [int[]]$Indexes)
    $text = '(none)'
    if ($Indexes.Count -gt 0) {
        $text = ($Indexes | ForEach-Object { $headers[$_] }) -join ' | '
    }
    Write-Host ('{0}: {1}' -f $Label, $text)
}

Write-Host ''
Write-Host '=== columns ==='
Show-SMBeatCompareCols -Label 'SMB-Server Out' -Indexes $idxSmbSent
Show-SMBeatCompareCols -Label 'SMB-Server In ' -Indexes $idxSmbRecv
Show-SMBeatCompareCols -Label 'Privat Out ' -Indexes $idxPrivSent
Show-SMBeatCompareCols -Label 'Privat In ' -Indexes $idxPrivRecv
Show-SMBeatCompareCols -Label 'NIC Out ' -Indexes $idxNicSent
Show-SMBeatCompareCols -Label 'NIC In ' -Indexes $idxNicRecv

$sum = @{
    smbSent  = 0.0
    smbRecv  = 0.0
    privSent = 0.0
    privRecv = 0.0
    nicSent  = 0.0
    nicRecv  = 0.0
}
$used = 0
$first = $null
$last = $null
$prevWhen = $null

function Add-SMBeatCompareRate {
    param(
        [hashtable]$Map,
        [int[]]$Indexes,
        [string[]]$Cells,
        [double]$Seconds,
        [string]$Key
    )
    $rate = 0.0
    $any = $false
    foreach ($i in $Indexes) {
        if ($i -ge $Cells.Count) { continue }
        $n = ConvertFrom-SMBeatCompareNumber -Text $Cells[$i]
        if ($null -ne $n) {
            $rate += [double]$n
            $any = $true
        }
    }
    if ($any) {
        $Map[$Key] = $Map[$Key] + ($rate * $Seconds)
    }
}

foreach ($line in ($lines | Select-Object -Skip 1)) {
    $cells = @($line.Split($sep))
    if ($cells.Count -lt 2) { continue }
    $when = ConvertFrom-SMBeatCompareWhen -Text $cells[0]
    if ($null -eq $when) { continue }
    if ($PSBoundParameters.ContainsKey('Start') -and $when -lt $Start) { continue }
    if ($PSBoundParameters.ContainsKey('End') -and $when -gt $End) { continue }

    $dt = 60.0
    if ($null -ne $prevWhen) {
        $gap = ($when - $prevWhen).TotalSeconds
        if ($gap -gt 0 -and $gap -le 3600) { $dt = $gap }
    }
    $prevWhen = $when
    if ($null -eq $first) { $first = $when }
    $last = $when
    $used++
    Add-SMBeatCompareRate -Map $sum -Indexes $idxSmbSent -Cells $cells -Seconds $dt -Key 'smbSent'
    Add-SMBeatCompareRate -Map $sum -Indexes $idxSmbRecv -Cells $cells -Seconds $dt -Key 'smbRecv'
    Add-SMBeatCompareRate -Map $sum -Indexes $idxPrivSent -Cells $cells -Seconds $dt -Key 'privSent'
    Add-SMBeatCompareRate -Map $sum -Indexes $idxPrivRecv -Cells $cells -Seconds $dt -Key 'privRecv'
    Add-SMBeatCompareRate -Map $sum -Indexes $idxNicSent -Cells $cells -Seconds $dt -Key 'nicSent'
    Add-SMBeatCompareRate -Map $sum -Indexes $idxNicRecv -Cells $cells -Seconds $dt -Key 'nicRecv'
}

Write-Host ''
Write-Host '=== window ==='
if ($null -eq $first) {
    Write-Host 'No samples in the selected window.'
    Remove-Item -LiteralPath $csv -Force -ErrorAction SilentlyContinue
    return
}
Write-Host ('From : {0}' -f $first)
Write-Host ('To : {0}' -f $last)
Write-Host ('Samples : {0}' -f $used)
Write-Host ('Seconds : {0:N0}' -f (($last - $first).TotalSeconds))

Write-Host ''
Write-Host '=== volume (rate * interval) ==='
@(
    [PSCustomObject]@{ Source = 'SMB-Server Out'; Bytes = [int64]$sum.smbSent; GB = (ConvertTo-SMBeatCompareGB -Bytes $sum.smbSent) }
    [PSCustomObject]@{ Source = 'SMB-Server In';  Bytes = [int64]$sum.smbRecv; GB = (ConvertTo-SMBeatCompareGB -Bytes $sum.smbRecv) }
    [PSCustomObject]@{ Source = 'Privat Out';     Bytes = [int64]$sum.privSent; GB = (ConvertTo-SMBeatCompareGB -Bytes $sum.privSent) }
    [PSCustomObject]@{ Source = 'Privat In';      Bytes = [int64]$sum.privRecv; GB = (ConvertTo-SMBeatCompareGB -Bytes $sum.privRecv) }
    [PSCustomObject]@{ Source = 'NIC Out';        Bytes = [int64]$sum.nicSent; GB = (ConvertTo-SMBeatCompareGB -Bytes $sum.nicSent) }
    [PSCustomObject]@{ Source = 'NIC In';         Bytes = [int64]$sum.nicRecv; GB = (ConvertTo-SMBeatCompareGB -Bytes $sum.nicRecv) }
) | Format-Table -AutoSize

Write-Host 'Paste this output. HTML is compared in chat, not in this script.'
Remove-Item -LiteralPath $csv -Force -ErrorAction SilentlyContinue