Page 2 of 2

Re: CPU Usage Multi-core

Posted: Tue Oct 09, 2012 3:21 pm
by doctorized
This is my code. Test it please and tell me if it works fine.

Code: Select all

#COUNTERPERF_PROCESSOR = 238
#COUNTERPERF_PERCENTPROCESSORTIME = 6

Global NumCores.w
Global hQuery.l
Structure PDH_FMT_COUNTERVALUE
    CStatus.l; As Long
    ;  case Cardinal of
    ;    0: (longValue: Integer);
    ;    1: (doubleValue: Double);
    ;    2: (largeValue: Int64);
    ;    3: (AnsiStringValue: PChar);
    ;    4: (WideStringValue: PWideChar);
    padding.l; As Long
    ulValueLow.l; As Long
    ulValueHigh.l; As Long
EndStructure

Global Dim cpuCounters.l(1)
Global Dim dCPU.d(1)

#PDH_CSTATUS_VALID_DATA = 0
#PDH_CSTATUS_NEW_DATA = 1

Declare.s GetCPUCounter(strInstance.s)

Procedure Close_CPU_Usage()
    PdhCloseQuery_(hQuery) ; close cpu usage
EndProcedure

Procedure.l InitializeCPU()
    pdhStatus.l: SysInfo.SYSTEM_INFO: CPU_Obj.s
    hCounter.l: i.l
    ; get # of cpus
    GetSystemInfo_(@SysInfo)
    NumCores = SysInfo\dwNumberOfProcessors - 1
    ReDim dCPU.d(NumCores)
    ReDim cpuCounters.l(NumCores)
    
    pdhStatus = PdhOpenQuery_(0, 1, @hQuery)
    If pdhStatus = 0
        For i = 0 To NumCores
            CPU_Obj = GetCPUCounter(Trim(Str(i)))
            If PdhValidatePath_(CPU_Obj) <> #ERROR_SUCCESS
            	MessageRequester("Error","Unable To initialize CPU usage.")
            EndIf
            pdhStatus = PdhAddCounter_(hQuery, CPU_Obj, 0, @hCounter)
            If pdhStatus = #PDH_CSTATUS_VALID_DATA: cpuCounters(i) = hCounter: EndIf
        Next i
        
        If hCounter <> 0 
            ProcedureReturn 1
        EndIf
    EndIf

    MessageRequester("Error","Unable To initialize CPU usage.")

EndProcedure

Procedure Update_Cpu_Usage()
    ; get usage per core
    hIcon.l: pdhStatus.l
    i.l
    lpValue.PDH_FMT_COUNTERVALUE
    PdhCollectQueryData_(hQuery)
    For i = 0 To NumCores
        PdhGetFormattedCounterValue_(cpuCounters(i), $200, @dwType.l, @lpValue)
        CopyMemory(@lpValue\ulValueLow, @dCPU(i),8)
    Next

EndProcedure

Procedure.s GetCPUCounter(strInstance.s)
    ; get Object & Counter names for CPU Usage
    ; Different languages of windows use different names so we use a look-up!
	If OpenLibrary(0, "pdh.dll")  
		*F = GetFunction(0, "PdhLookupPerfNameByIndexA")
		If *F
			 NameLen.l: ObjectName.s: CounterName.s
		    
		    NameLen = #MAX_PATH
		    ObjectName = Space(NameLen)
		    i = CallFunctionFast(*F,#Null, #COUNTERPERF_PROCESSOR, @ObjectName, @NameLen)
       
		    NameLen = #MAX_PATH
		    CounterName = Space(NameLen)
		    
		    i = CallFunctionFast(*F,#Null, #COUNTERPERF_PERCENTPROCESSORTIME, @CounterName, @NameLen)
		    Debug "\" + ObjectName + "(" + strInstance + ")\" + CounterName
		    ProcedureReturn "\" + ObjectName + "(" + strInstance + ")\" + CounterName
		EndIf
		CloseLibrary(0)
	EndIf
EndProcedure



initializecpu()
For i=1 To 40
	update_cpu_usage()
	For ii=0 To NumCores
		tmp.s + "Core #" + Str(ii) + " usage = " + Str(dCPU(ii)) + "%     "
	Next
	Debug tmp
	tmp=""
	Delay(1000)
Next
close_cpu_usage()

Re: CPU Usage Multi-core

Posted: Tue Oct 09, 2012 3:27 pm
by jack
works OK on my vitual machine, windows 7x32

Re: CPU Usage Multi-core

Posted: Tue Oct 09, 2012 3:46 pm
by GG
Looks perfect for me, 2 cores detected, and CPU % for each one well performed. :)

Re: CPU Usage Multi-core

Posted: Tue Oct 09, 2012 9:22 pm
by doctorized
Visit the website of my signature to find a graphical way of my code. It looks like the Windows' way in task manager.
The code is writen in PB 4.31 but works fine with 4.61.

Re: CPU Usage Multi-core

Posted: Tue Oct 09, 2012 11:01 pm
by electrochrisso
Works fine on my Atom quad core N550, I can use this, thanks Doc. :)

Re: CPU Usage Multi-core

Posted: Sat Apr 29, 2023 10:49 pm
by Michael Vogel
Didn't find the correct structure for the return values when using query functions of the PDH library. The following code works fine for 64 bit machines, but it does not work when using the x86 compiler. (It does not show the values for each core but only the total CPU load)

The problem should be somewhere in the structure of PdhCounterType and as a result the line If \Buffer\Error=#Null fails. Does anyone know how to change the structure for working in both modes, 32 and 64 bits?

Changing the structure to something like below does work but I'd be more happy if someone could double check if this is valid.
  • Structure PdhCounterType
    Code.l; +0
    Error.i; +4
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 : Dummy.l : CompilerEndIf
    Value.d; +12
    EndStructure

Code: Select all

; Define

	Prototype.l ProtoOpenQuery(Para1.l, Para2.l, Para3.i)
	Prototype.l ProtoCloseQuery(Para1.l)
	Prototype.l ProtoCollectData(Para1.l)
	Prototype.d ProtoPerformance(Para1.l, Para2.l, Para3.l, Para4.i)
	Prototype.l ProtoAddCounter(Para1.l, Para2.l, Para3.l, Para4.i)

	Structure PdhCounterType
		Code.l;			+0
		Error.q;			+4
		Value.d;		+12
	EndStructure

	Structure CpuLoadType
		QueryActive.i
		HandleQuery.l
		Buffer.PdhCounterType
		OpenQuery.ProtoOpenQuery
		AddCounter.ProtoAddCounter
		CollectData.ProtoCollectData
		Performance.ProtoPerformance
		CloseQuery.ProtoCloseQuery
	EndStructure

	Global CpuLoad.CpuLoadType

; EndDefine

Procedure GarbageCollection()

	Protected lib,fnc

	lib=OpenLibrary(#PB_Any,"Psapi.dll")
	If lib
		fnc=GetFunction(lib,"EmptyWorkingSet")
		If fnc
			CallFunctionFast(fnc,GetCurrentProcess_())
		EndIf
		CloseLibrary(lib)
	EndIf

EndProcedure

Procedure InitCpuLoad()

	#PDH_FMT_DOUBLE=512

	Protected CounterDefinition.s

	With CpuLoad

		\QueryActive=#Null

		If OpenLibrary(0,"PDH.DLL")

			\OpenQuery.ProtoOpenQuery=	GetFunction(0, "PdhOpenQuery")
			\CloseQuery.ProtoCloseQuery=	GetFunction(0, "PdhCloseQuery")
			\CollectData.ProtoCollectData=	GetFunction(0, "PdhCollectQueryData")
			\AddCounter.ProtoAddCounter=	GetFunction(0, "PdhAddEnglishCounterW");	
			\Performance.ProtoPerformance=GetFunction(0, "PdhGetFormattedCounterValue")

			If \OpenQuery(0,1,@\HandleQuery)=#Null
				CounterDefinition="\Processor(_Total)\% Processor Time"
				If \AddCounter(\HandleQuery,@CounterDefinition,1,\Buffer)=#Null
					\QueryActive=#True
				EndIf
			EndIf
		EndIf

		ProcedureReturn \QueryActive

	EndWith

EndProcedure
Procedure ShowCpuLoad()

	With CpuLoad

		\CollectData(\HandleQuery)
		\Performance(\Buffer\Code, #PDH_FMT_DOUBLE, 0,@\Buffer\Error)
		If \Buffer\Error=#Null
			Debug StrD(\Buffer\Value,1)+"%"
		EndIf
	EndWith

EndProcedure
Procedure StopCpuLoad()

	With CpuLoad
		If \QueryActive
			\CloseQuery(\HandleQuery)
			CloseLibrary(0)
		EndIf
	EndWith

EndProcedure

If InitCpuLoad()

	GarbageCollection()

	For i=0 To 10
		ShowCpuLoad()

		Delay(1000)
	Next i
	StopCpuLoad()
EndIf

Re: CPU Usage Multi-core

Posted: Mon May 01, 2023 9:14 am
by Fred
What you need is 'Structure PdhCounterType Align #PB_Structure_AlignC' to handle these alignment padding automatically