sp00n / corecycler

Script to test single core stability, e.g. for PBO & Curve Optimizer on AMD Ryzen or overclocking/undervolting on Intel processors
Other
664 stars 30 forks source link

FATAL ERROR: Could not get the localized Performance Process Counter name! #8

Open Neikon opened 3 years ago

Neikon commented 3 years ago

imagen I did all things in FAQ readme.txt. "lodctr.exe /q:PerfProc" said "[PerfProc] Performance Counters (Enabled)"

but I have always "FATAL ERROR: Could not get the localized Performance Process Counter name!"

tested with v0.7.9.0

sp00n commented 3 years ago

Did you execute the enable_performance_counter.bat in the /troubleshooting folder?

If yes, can you paste the output when you execute this code in a Powershell terminal?

function Get-PerformanceCounterIDs {
    param (
        [Parameter(Mandatory=$true)]
        [Array]
        $englishCounterNames
    )

    $key          = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009'
    $allCounters  = (Get-ItemProperty -Path $key -Name Counter).Counter
    $numCounters  = $allCounters.Count
    $countersHash = @{}

    # The string contains two-line pairs
    # The first line is the ID
    # The second line is the name
    for ($i = 0; $i -lt $numCounters; $i += 2) {
        $counterId   = [Int]$allCounters[$i]
        $counterName = [String]$allCounters[$i+1]

        if ($englishCounterNames.Contains($counterName)) {
            $countersHash[$counterName] = $counterId
        }

    }

    return $countersHash
}

function Get-PerformanceCounterLocalName {
    param (
        [UInt32]
        $ID,
        $ComputerName = $env:COMPUTERNAME
    )

    try {
        $code = '[DllImport("pdh.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern UInt32 PdhLookupPerfNameByIndex(string szMachineName, uint dwNameIndex, System.Text.StringBuilder szNameBuffer, ref uint pcchNameBufferSize);'

        $Buffer = New-Object System.Text.StringBuilder(1024)
        [UInt32]$BufferSize = $Buffer.Capacity

        $t = Add-Type -MemberDefinition $code -PassThru -Name PerfCounter -Namespace Utility
        $rv = $t::PdhLookupPerfNameByIndex($ComputerName, $ID, $Buffer, [Ref]$BufferSize)

        'ID:           ' + $ID
        'Found String: ' + $Buffer

        if ($rv -eq 0) {
            'Final Result:'
            $Buffer.ToString().Substring(0, $BufferSize-1)
        }
        else {
            Throw 'Get-PerformanceCounterLocalName : Unable to retrieve localized name. Check computer name and performance counter ID.'
        }
    }
    catch {
        'ERROR!'
        $Error
        $Error.Clear()
    }
}

$englishCounterNames = @(
    'Process',
    'ID Process',
    '% Processor Time'
)
$counterNames = @{}
$counterNameIds = Get-PerformanceCounterIDs $englishCounterNames

''
''
'-----------------'
'Counter Name IDs:'
'-----------------'
$counterNameIds

''
''
'------------------------'
'Localized Counter Names:'
'------------------------'

Get-PerformanceCounterLocalName $counterNameIds['Process']
Get-PerformanceCounterLocalName $counterNameIds['ID Process']
Get-PerformanceCounterLocalName $counterNameIds['% Processor Time']
Neikon commented 3 years ago

Did you execute the enable_performance_counter.bat in the /troubleshooting folder?

yes.

this is the output imagen

sp00n commented 3 years ago

Hm. It doesn't find the localized string for % Processor Time with the performance counter ID 6268. The other two are identified fine. It does find the English string within the the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009 Counter entry, from where it extracts the ID. But apparently it can't find the localized value for this ID within the Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage Counter entry.

I've never dealt with a Spanish(?) version before, only with English and German. Is there any chance you could post the content of these two registry keys here? You can use this script to dump the content into two files which you then can upload here:

$directory         = $env:TEMP
$fileEnglish       = $directory + '\_performanceCounters.English.txt'
$fileLocalized     = $directory + '\_performanceCounters.Localized.txt'
$keyEnglish        = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009'
$keyLocalized      = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage'
$countersEnglish   = (Get-ItemProperty -Path $keyEnglish -Name Counter).Counter
$countersLocalized = (Get-ItemProperty -Path $keyLocalized -Name Counter).Counter
cd $directory
Set-Content -Path $fileEnglish -Value $countersEnglish
Set-Content -Path $fileLocalized -Value $countersLocalized

The script dumps the content into two files, _performanceCounters.English.txt and _performanceCounters.Localized.txt and puts them into the TEMP directory, which you can access in Windows Explorer by entering %TEMP% into the address bar.

sp00n commented 3 years ago

I may have a possible fix for this issue. Can you run this script:

function Get-PerformanceCounterIDs {
    param (
        [Parameter(Mandatory=$true)]
        [Array]
        $englishCounterNames
    )

    $key          = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009'
    $allCounters  = (Get-ItemProperty -Path $key -Name Counter).Counter
    $numCounters  = $allCounters.Count
    $countersHash = @{}

    # The string contains two-line pairs
    # The first line is the ID
    # The second line is the name
    for ($i = 0; $i -lt $numCounters; $i += 2) {
        $counterId   = [Int]$allCounters[$i]
        $counterName = [String]$allCounters[$i+1]

        if ($englishCounterNames.Contains($counterName) -and !$countersHash.ContainsKey($counterName)) {
            $countersHash[$counterName] = $counterId
        }

    }

    return $countersHash
}

function Get-PerformanceCounterLocalName {
    param (
        [UInt32]
        $ID,
        $ComputerName = $env:COMPUTERNAME
    )

    try {
        $code = '[DllImport("pdh.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern UInt32 PdhLookupPerfNameByIndex(string szMachineName, uint dwNameIndex, System.Text.StringBuilder szNameBuffer, ref uint pcchNameBufferSize);'

        $Buffer = New-Object System.Text.StringBuilder(1024)
        [UInt32]$BufferSize = $Buffer.Capacity

        $t = Add-Type -MemberDefinition $code -PassThru -Name PerfCounter -Namespace Utility
        $rv = $t::PdhLookupPerfNameByIndex($ComputerName, $ID, $Buffer, [Ref]$BufferSize)

        'ID:           ' + $ID
        'Found String: ' + $Buffer

        if ($rv -eq 0) {
            'Final Result:'
            $Buffer.ToString().Substring(0, $BufferSize-1)
        }
        else {
            Throw 'Get-PerformanceCounterLocalName : Unable to retrieve localized name. Check computer name and performance counter ID.'
        }
    }
    catch {
        'ERROR!'
        $Error
        $Error.Clear()
    }
}

$englishCounterNames = @(
    'Process',
    'ID Process',
    '% Processor Time'
)
$counterNames = @{}
$counterNameIds = Get-PerformanceCounterIDs $englishCounterNames

''
''
'-----------------'
'Counter Name IDs:'
'-----------------'
$counterNameIds

''
''
'------------------------'
'Localized Counter Names:'
'------------------------'

Get-PerformanceCounterLocalName $counterNameIds['Process']
Get-PerformanceCounterLocalName $counterNameIds['ID Process']
Get-PerformanceCounterLocalName $counterNameIds['% Processor Time']

If this works without showing an error, you could change line 238 in script-corecycler.ps1 from if ($englishCounterNames.Contains($counterName)) { to if ($englishCounterNames.Contains($counterName) -and !$countersHash.ContainsKey($counterName)) { and hopefully it will work. I installed the Spanish language pack to do some tests and initially received the same error, but with that change it worked.

Neikon commented 3 years ago

Yes, new version works

claudjaris commented 2 years ago

Sorry to reopen this issue. But it seems like I have a very similar problem. grafik I tried the manual approach to enable the counter and your batch file. But I still have this result. And your suggested fix seems to be already included in the newer version. However, when I use Version 0.7.8.5 it does not throw an error. But 0.7.8.9 does throw the above error from user Neikon. I have German/English Windows 10.

sp00n commented 2 years ago

Sorry to reopen this issue. But it seems like I have a very similar problem. grafik I tried the manual approach to enable the counter and your batch file. But I still have this result. And your suggested fix seems to be already included in the newer version. However, when I use Version 0.7.8.5 it does not throw an error. But 0.7.8.9 does throw the above error from user Neikon. I have German/English Windows 10.

Can you try to run the script I posted above and post the results?

claudjaris commented 2 years ago

I tried, but gives me an error. Here you go: grafik

boucz commented 2 years ago

Hello, I have the same error as @claudjaris but my computer is in french. and running

$directory         = $env:TEMP
$fileEnglish       = $directory + '\_performanceCounters.English.txt'
$fileLocalized     = $directory + '\_performanceCounters.Localized.txt'
$keyEnglish        = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009'
$keyLocalized      = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage'
$countersEnglish   = (Get-ItemProperty -Path $keyEnglish -Name Counter).Counter
$countersLocalized = (Get-ItemProperty -Path $keyLocalized -Name Counter).Counter
cd $directory
Set-Content -Path $fileEnglish -Value $countersEnglish
Set-Content -Path $fileLocalized -Value $countersLocalized

works but the two files contain french counter names even _performanceCounters.english.txt I did a quick search in the registry but didn't find english counter names. any hint or need tu correct this issue?

sp00n commented 2 years ago

@boucz The English registry key contains French strings? That sounds really weird, Perflib\009 should always contain the English strings. I can only test this by installing language packs, so it's not the native & default Windows language, but for me this always was the case in this way.

Here's another howto on resetting the Performance Counters, which seems to be a bit more extended than my instructions, but I've never tried this approach, so can't comment on the effort or success of this one: https://docs.microsoft.com/en-us/troubleshoot/windows-server/performance/rebuild-performance-counter-library-values

playpun1 commented 1 year ago

any idea how to fix this? Screenshot_1

sp00n commented 1 year ago

Did you try to execute the enable_performance_counter.bat in the /tools directory?

If that doesn't work, from the error message it seems that the service is not started at all, did you maybe disable them at some point (I know I do disable services I deem unnecessary from time to time).

Maybe this can help, too, although it's for Windows Server: https://learn.microsoft.com/de-de/troubleshoot/windows-server/performance/manually-rebuild-performance-counters

playpun1 commented 1 year ago

yes i tried enable_performance_counter.bat already and also the other fix in the link, still doesnt work. i also ran the script from above if that any help for you here is the screenshot Screenshot_2

sp00n commented 1 year ago

Does the Windows tool perfmon.exe work? It accesses the same performance counters.

playpun1 commented 1 year ago

i get this when i open it Screenshot_4

sp00n commented 1 year ago

Yeah, something is wrong with the Performance Counters (Leistungsindikatoren) on your system. I can only point to batch file and the links in the readme.txt, as without working Performance Counters the script can't check the CPU usage for the stress test program.

Also as an additional note, I wouldn't run the script from C:\Program Files. It requires administrator privileges if you do so, because it wants to write config files to its directory.

boucz commented 1 year ago

As Spoon said you should try to rue-register perf counters following learn.Microsoft.com as he suggested. Even if the link talk about server version of os it should work ok for a workstation

tAz-07 commented 1 year ago

I had exactly the same issue and was able to fix it using the microsoft https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/manually-rebuild-performance-counters link provided by boucz.

I have opened a cmd command in administrator and type: cd c:\windows\system32 lodctr /R cd c:\windows\sysWOW64 lodctr /R WINMGMT.EXE /RESYNCPERF

then the Run CoreCycler.bat was working fine.

fractalzombie commented 1 year ago

If someone has not working with this error, i don't know how, but this script executes normally if you don't use .bat file, use ps1 and executes it in Visual Studio Code. Don't ask why, i don't know...

HerXayah commented 1 year ago

I mean the script that sp00n sent once helped me with this. When i run this again now, it doesnt work anymore and im stuck with the same as @playpun1

iDarkEmpire commented 10 months ago

enable_performance_counter.bat Does not help

image

image

sp00n commented 10 months ago

@iDarkEmpire I guess you'll have to try to manually restore the performance counters. Some possibly helpful links are provided in the readme.txt.

Also, sometimes the batch file cannot stop/restart all necessary services if additional ones are installed that depend on one of those that need to be stopped.

iDarkEmpire commented 10 months ago

https://leansentry.zendesk.com/hc/en-us/articles/360038645792-How-to-Fix-performance-counter-issues https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/manually-rebuild-performance-counters I tried to do as indicated here, but still the error is there

I by the way only version 0.7.8.5 works for me

iDarkEmpire commented 8 months ago

I had to change the Russian language in windows to English, only after that the error disappeared

sp00n commented 8 months ago

The Performance Counters, PowerShell and indeed Windows itself is a bit weird, because it does localize a couple of things that IMO really shouldn't be localized internally, like the names of the Performance Counters. And additionally they have no fixed id, so they can change at any time, which is why I have to do a more or less reliable matching process to find the correct localized performance counter entry.

This worked fine for a couple other languages, but I have not yet had any experience with non-latin character sets like Russian.

There's even a chance that it might miraculously work now if you switch back to Russian. It's just that weird.

The English names for the Performance Counter names are in the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter registry key, while the one for the currently selected language are in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage\Counter.

Maybe you could export both entries (English and Russian) and upload them here, so that I could take a look at what's failing there.

iDarkEmpire commented 8 months ago

Counter https://pastebin.com/k2e23jUk

the value is the same that in 009 that in CurrentLanguage

iDarkEmpire commented 8 months ago

Counter eng https://pastebin.com/AfLXLSXy

sp00n commented 8 months ago

So it should find

230
Процесс

which represents

230
Process

But apparently it doesn't?

I suspect it didn't suddenly start working when you switched back to Russian?

iDarkEmpire commented 8 months ago

No, when the interface is in Russian, CoreCycler gives an error, most likely there may be a problem in the Cyrillic alphabet in the values of the registry

Eddga commented 8 months ago

@iDarkEmpire could you please open PowerShell with Russian interface language, copy/paste the following code and share the results here? Feel free to try it on both Russian and English interface language but Russian would be the one that interests me more. 😊

(Get-Counter -ListSet Process | Select-Object -ExpandProperty Paths)[0]
(Get-Counter -ListSet Process | Select-Object -ExpandProperty PathsWithInstances | Where-Object {$_ -match '(?=.*explorer)(?=.*%)'})[0]
Get-Counter -Counter $((Get-Counter -ListSet Process | Select-Object -ExpandProperty Paths)[0])

Thanks in advance.

sp00n commented 8 months ago

That would be neat if index 14 would always represent the same entry. But counter names are localized, so the order may be different. Let's see. And actually I'm using % Processor Time, which would be [0] when set to English for me.

Eddga commented 8 months ago

Yeah I just realized that in the end it goes for % Processor Time. Just walked through the script and first stumbled upon it retrieving \Process(*)\ID Process as a counter string and then replacing \ID Process with \% Processor Time. I have a good feeling about this to be honest. Even though they badly mess up with things like unnecessarily localizing system relevant strings they tend to have such lists and items in general in the same order or place throughout different locales.

Eddga commented 8 months ago

My second idea to solve this: Ultimately Get-Counter should be retrieving the % Processor Time by simply calculating TotalProcessorTime\SampleInterval. According to the MS doc Get-Counter does this for one sample over a time span of one second if the according parameters aren't specified. Therefore we also could simply use (Get-Process <Process ID>).TotalProcessorTime to calculate it ourselves. Question is if there is a big difference in precision and if that would even matter that much. Going this route we could probably avoid the whole Get-Counter cmdlet. In theory this should do the same as the Get-Counter cmdlet:

$process = 'BoostTesterMannix'
$interval = 1000
$time = Get-Date
$tpt = ( Get-Process $process ).TotalProcessorTime.Ticks
Start-Sleep -Milliseconds $interval
$dtpt = ( Get-Process $process ).TotalProcessorTime.Ticks- $tpt
$ppt = ( $dtpt / ( New-TimeSpan $time ).Ticks ) * 100
Write-Host "% Processor Time: $ppt"
iDarkEmpire commented 8 months ago

@Eddga https://pastebin.com/tvfBVx9m

Eddga commented 8 months ago

Ahh stupid me... The last command didn't do what I wanted it to do and overwrote the content of the console. 😅 But still it seems to be working. Thank you very much @iDarkEmpire - this was with Russian interface language, right? Could you please again run only this code and share the result again?

(Get-Counter -ListSet Process | Select-Object -ExpandProperty Paths)[0]
(Get-Counter -ListSet Process | Select-Object -ExpandProperty PathsWithInstances | Where-Object {$_ -match '(?=.*explorer)(?=.*%)'})[0]

Thanks again.

iDarkEmpire commented 8 months ago

yes, russian interface language

https://i.imgur.com/fsKkez2.jpeg

Eddga commented 8 months ago

Thanks a lot @iDarkEmpire! 👌 It seems like either your system is installed with an English Windows image and has the Russian language package added afterwards OR some languages use the English counter names. Either way using the ListSet parameter to retrieve the names seems like a good idea. I will set up a few VMs with different language images and try the behavior in these environments. If it works I'll include it with my own version of CoreCycler and provide it for testing. It then could be integrated into the main version to hopefully resolve this issue for all languages.

Eddga commented 8 months ago

Ok so I set up a Russian VM and tried this.

(Get-Counter -ListSet **Process** | Select-Object -ExpandProperty Paths)[0]

... isn't working for the simple reason, that the ListSet parameter also wants the localized name... 🤦‍♂️ But indeed the first parameter of the process list set in Russian is the translated version of \Process(*)\% Processor Time. I will try it for two additional languages but this looks promising. And the correct list set can be found by regex matching the paths. This is what so far worked for me for both Russian and English Windows:

$processSet = (Get-Counter -ListSet * | Where-Object {$_.PathsWithInstances -match '(?=.*\(explorer\))(?=.*%)' -and -not ($_.PathsWithInstances -match '\/0\)' ) } )[0].CounterSetName
(Get-Counter -ListSet $processSet | Select-Object -ExpandProperty Paths)[0]

I have to say though that the original attempt from the current CoreCycler version (retrieving the names from the Perflib registry keys) works perfectly fine on my Russian Windows 10 VM and returns the correct Russian counter names. So I guess @iDarkEmpire's issue lies in the display language differing from OS installation language - Russian installation gives Russian reg keys but English interface language sets English counter names in the shell.

iDarkEmpire commented 8 months ago

yes, windows english image with russian localization package installed

Eddga commented 7 months ago

I was able to reproduce the issue. It is indeed the case that on an English Windows image with Russian language pack installed the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009 key gets replaced with the Russian counter names, too. There is not a single key present anymore that contains the English counter names. Running the enable_performance_counter.bat on this system also doesn't change anything about this. All keys are still in Russian after running it. Meanwhile on a Russian image this is not the case and 009 contains the English counter names. That means MS is messing it up even more when installing language packs and we can't depend on it at all. Even though I must say this really is an edge case.

I tried to simply replace how the strings are retrieved using my ListSet approach from above. But that didn't work out. We not only need the localized \Process(*)\% Processor Time for which it worked perfectly fine in EN, FR, RU, ES images. But we also need the localized ID Process string to retrieve the correct counter process ID/string of the stress test. This one turned out to be on position [14] in EN, RU and ES and looked promising to this point but is on position [12] of the ListSet array in FR. Dead end.

I then thought why don't we just use the IDs and use only the Get-PerformanceCounterLocalNames function as we know the IDs. I then stumbled upon this link which tells that you can use the IDs directly instead of the names. Get-Counter -Counter "\238(*)\6" works perfectly fine and is the equivalent of the counter path \Processor(*)\% Processor Time. But sadly Get-Counter "\230(*)\784" which should be \Process(*)\ID Process doesn't work. Right when I thought I understand how this counter construct works in Windows it again is not what I expected it to be. 😅 Get-Counter "\230(*)\ID Process" on the other hand works... It's just really weird.

Next I'll check if the IDs are the same for the relevant counter names in my 4 test languages. If they are this would be the way to go in my mind.

henk911 commented 3 months ago

O Allah...just change the language to English do your CPU stress test, then switch back to your language. Simple solution - sometimes not a wrost solution)) Yeah, It's cool when everything works out of the box But, code developer gave us a super solution that allows us to do such a stress test on a single core, which (For a second) no program on the market gives. Thank you so much for your tinder (This is for you, the developer of this code) But don't make a problem for yourself ))

henk911 commented 3 months ago

Okay, OCCT can do that too.Stupid user wrote a comment without thinking My bad...