Decipher Posted July 9, 2013 Posted July 9, 2013 (edited) I took a script written by Beege, optimized the code, and removed the global variables. Its one function, reliable, and very easy to use or modify to your needs. I decided to post it because I found it very hard to find a reliable way to get the cpu percentage of a given process. I know that there are a few different methods but I deem WMI unreliable and everything I tried before optimizing this function was to slow i.e. freezing when other CPU intensive processes were active. When monitoring a potentially CPU intensive process, I found it necessary to increase the script's priority otherwise the script/system would freeze which is exactly what I was attempting to prevent! See Beege's updated post: expandcollapse popup#NoTrayIcon #include <WinAPI.au3> HotKeySet('{ESC}', '_Exit') Func _Exit() Exit EndFunc ;==>_Exit _LimitProcessCPU("MediaServer.exe", 10, 1, 5) Func _LimitProcessCPU($vProcess, $nLimit, $nInterval = 1, $nScriptPriority = 2) Local $nProcessCpu = -1, $nOverLimit = 0 While ProcessExists($vProcess) $nProcessCpu = _ProcessCPU($vProcess, $nScriptPriority) If $nProcessCpu > 50 Then $nOverLimit += 1 Else $nOverLimit = 0 EndIf If $nOverLimit > $nLimit Then $nOverLimit = 0 ProcessClose($vProcess) ; < - Replace this with what you need. EndIf Sleep($nInterval*1000) WEnd EndFunc Func _ProcessCPU($nPID = @AutoItPID, $nScriptPriority = 2) ; Realtime priority = 5 ; Original Author: Beege -> http://www.autoitscript.com/forum/user/8949-beege/ $nPID = ProcessExists($nPID) If Not $nPID Then Return SetError(1, 0, "") EndIf Local Const $tagFILETIME = "struct;dword Lo;dword Hi;endstruct", $nStructs = 7 Local Static $aStruct[$nStructs], $aPointer[$nStructs], $aStat[4], $hProcess = _WinAPI_OpenProcess(0x1F0FFF, 0, $nPID, True), $bFirstRun = True Local Enum $nIDLETIME, $nKERNELTIME, $nUSERTIME, $nPCreationTime, $nPExitTime, $nPKernelTime, $nPUserTime, _ $nProcStartKern = 0, $nProcStartUser, $nStartKernel, $nStartUser If $bFirstRun Then For $i = 0 To $nStructs - 1 Step 1 $aStruct[$i] = DllStructCreate($tagFILETIME) $aPointer[$i] = DllStructGetPtr($aStruct[$i]) Next EndIf DllCall('Kernel32.dll', "int", "GetSystemTimes", "ptr", $aPointer[$nIDLETIME], "ptr", $aPointer[$nKERNELTIME], "ptr", $aPointer[$nUSERTIME]) DllCall('Kernel32.dll', "int", "GetProcessTimes", "hwnd", $hProcess, "ptr", $aPointer[$nPCreationTime], "ptr", $aPointer[$nPExitTime], "ptr", _ $aPointer[$nPKernelTime], "ptr", $aPointer[$nPUserTime]) Local $aTemp[4] = [DllStructGetData($aStruct[$nPKernelTime], 1), DllStructGetData($aStruct[$nPUserTime], 1), DllStructGetData($aStruct[$nKERNELTIME], 1), _ DllStructGetData($aStruct[$nUSERTIME], 1)], $tProcess, $tSystem If Not $bFirstRun Then $tProcess = ($aTemp[$nProcStartKern] - $aStat[$nProcStartKern]) + ($aTemp[$nProcStartUser] - $aStat[$nProcStartUser]) $tSystem = ($aTemp[$nStartKernel] - $aStat[$nStartKernel]) + ($aTemp[$nStartUser] - $aStat[$nStartUser]) Else ProcessSetPriority(@AutoItPID, $nScriptPriority) $bFirstRun = False EndIf $aStat = $aTemp Return Int(Round(($tProcess / $tSystem) * 100)) EndFunc ;==>_ProcessCPU Edit - To give credit for source. Anonymous Edited July 13, 2013 by Decipher Spoiler
Ascend4nt Posted July 9, 2013 Posted July 9, 2013 Interesting, I just developed a Process CPU usage tracker not long after my work on the CPU Usage tracker code. I liked the idea of keeping it more object-oriented, and so far my system works out pretty well. In the future I might expand it to more than one process per usage tracker, since it'd be a shame to call GetSystemTimes more times than is necessary. Ah, and the author you should be crediting is probably Beege - see his >Process CPU Usage script. My contributions: Performance Counters in Windows - Measure CPU, Disk, Network etc Performance | Network Interface Info, Statistics, and Traffic | CPU Multi-Processor Usage w/o Performance Counters | Disk and Device Read/Write Statistics | Atom Table Functions | Process, Thread, & DLL Functions UDFs | Process CPU Usage Trackers | PE File Overlay Extraction | A3X Script Extract | File + Process Imports/Exports Information | Windows Desktop Dimmer Shade | Spotlight + Focus GUI - Highlight and Dim for Eyestrain Relief | CrossHairs (FullScreen) | Rubber-Band Boxes using GUI's (_GUIBox) | GUI Fun! | IE Embedded Control Versioning (use IE9+ and HTML5 in a GUI) | Magnifier (Vista+) Functions UDF | _DLLStructDisplay (Debug!) | _EnumChildWindows (controls etc) | _FileFindEx | _ClipGetHTML | _ClipPutHTML + ClipPutHyperlink | _FileGetShortcutEx | _FilePropertiesDialog | I/O Port Functions | File(s) Drag & Drop | _RunWithReducedPrivileges | _ShellExecuteWithReducedPrivileges | _WinAPI_GetSystemInfo | dotNETGetVersions | Drive(s) Power Status | _WinGetDesktopHandle | _StringParseParameters | Screensaver, Sleep, Desktop Lock Disable | Full-Screen Crash Recovery Wrappers/Modifications of others' contributions: _DOSWildcardsToPCRegEx (original code: RobSaunder's) | WinGetAltTabWinList (original: Authenticity) UDF's added support/programming to: _ExplorerWinGetSelectedItems | MIDIEx UDF (original code: eynstyne) (All personal code/wrappers centrally located at Ascend4nt's AutoIT Code)
Decipher Posted July 9, 2013 Author Posted July 9, 2013 (edited) Ascend4nt, I've looked at a lot of your code recently, it looks great! Thanks for the info. If you like at this line: DllCall('Kernel32.dll', "int", "GetSystemTimes", "ptr", $aPointer[$nIDLETIME], "ptr", $aPointer[$nKERNELTIME], "ptr", $aPointer[$nUSERTIME]) You will see that the data structures that it populates are resuable for multiple calls to <GetProcessTimes> however I wouldn't know right off wether or not you could get around that function call or not as it requires you pass a process handle. Anonymous Edited July 9, 2013 by Decipher Spoiler
arandomguy01 Posted July 11, 2013 Posted July 11, 2013 How's it going? I'm using the _ProcessCPU function here in a script. However, occasionally I need to change the monitored Process. I'm not quite sure how to do this as it seems the Static variables don't allow it. Any advice on how to do this? Thank you for your time.
JohnOne Posted July 11, 2013 Posted July 11, 2013 Use a global variable instead. AutoIt Absolute Beginners Require a serial Pause Script Video Tutorials by Morthawt ipify Monkey's are, like, natures humans.
Decipher Posted July 12, 2013 Author Posted July 12, 2013 (edited) arandomguy01, Here is an example that complements JohnOne's answer. expandcollapse popupGlobal $nPID = "MyProcess.exe" While 1 ;_ProcessCPU(3) ; 3 = AboveNormal priority ConsoleWrite(_ProcessCPU(3) & "%" & @CRLF) If "condition" = "condition" Then $nPID = "MyOtherProcess.exe" EndIf Sleep(1000) WEnd Func _ProcessCPU($nScriptPriority = 2) ; Realtime priority = 5 ; Original Author: Beege -> http://www.autoitscript.com/forum/user/8949-beege/ $nPID = ProcessExists($nPID) If Not $nPID Then Return SetError(1, 0, "") EndIf Static $nLastProcess = $nPID If $nPID <> $nLastProcess Then $nLastProcess = $nPID $hProcess = _WinAPI_OpenProcess(0x1F0FFF, 0, $nPID, True) EndIf Local Const $tagFILETIME = "struct;dword Lo;dword Hi;endstruct", $nStructs = 7 Local Static $aStruct[$nStructs], $aPointer[$nStructs], $aStat[4], $hProcess = _WinAPI_OpenProcess(0x1F0FFF, 0, $nPID, True), $bFirstRun = True Local Enum $nIDLETIME, $nKERNELTIME, $nUSERTIME, $nPCreationTime, $nPExitTime, $nPKernelTime, $nPUserTime, _ $nProcStartKern = 0, $nProcStartUser, $nStartKernel, $nStartUser If $bFirstRun Then For $i = 0 To $nStructs - 1 Step 1 $aStruct[$i] = DllStructCreate($tagFILETIME) $aPointer[$i] = DllStructGetPtr($aStruct[$i]) Next EndIf DllCall('Kernel32.dll', "int", "GetSystemTimes", "ptr", $aPointer[$nIDLETIME], "ptr", $aPointer[$nKERNELTIME], "ptr", $aPointer[$nUSERTIME]) DllCall('Kernel32.dll', "int", "GetProcessTimes", "hwnd", $hProcess, "ptr", $aPointer[$nPCreationTime], "ptr", $aPointer[$nPExitTime], "ptr", _ $aPointer[$nPKernelTime], "ptr", $aPointer[$nPUserTime]) Local $aTemp[4] = [DllStructGetData($aStruct[$nPKernelTime], 1), DllStructGetData($aStruct[$nPUserTime], 1), DllStructGetData($aStruct[$nKERNELTIME], 1), _ DllStructGetData($aStruct[$nUSERTIME], 1)], $tProcess, $tSystem If Not $bFirstRun Then $tProcess = ($aTemp[$nProcStartKern] - $aStat[$nProcStartKern]) + ($aTemp[$nProcStartUser] - $aStat[$nProcStartUser]) $tSystem = ($aTemp[$nStartKernel] - $aStat[$nStartKernel]) + ($aTemp[$nStartUser] - $aStat[$nStartUser]) Else ProcessSetPriority(@AutoItPID, $nScriptPriority) $bFirstRun = False EndIf $aStat = $aTemp Return Int(Round(($tProcess / $tSystem) * 100)) EndFunc ;==>_ProcessCPU Edit - Do notice that I slightly modified the function for use with the global variable. Anonymous Edited July 12, 2013 by Decipher Spoiler
Beege Posted July 12, 2013 Posted July 12, 2013 (edited) Interesting, I just developed a Process CPU usage tracker not long after my work on the CPU Usage tracker code. I liked the idea of keeping it more object-oriented, and so far my system works out pretty well. In the future I might expand it to more than one process per usage tracker, since it'd be a shame to call GetSystemTimes more times than is necessary. Ah, and the author you should be crediting is probably Beege - see his >Process CPU Usage script. Thanks for the credit point out Ascend4nt. Ive thought about doing the same type of expansion myself with the code below and passing an array of process handles. My only worry was how far off would the system times be by the time I got to the last call of getprocesstimes. But might be over thinking it Decipher,Heres another example you could adapt from thats a little cleaner then I wrote back then with more of the updated syntax that autoit currently has. When I originally wrote it the main thought in my head was everything could be all in one function cause Im only monitoring the process itself. Hense the static var for the process handle. If the handle might change, I would recommend functions like below that except the handle as a parameter. I realize it was just a quick fix, but a global var that changes a local static var is not a good choice. Also note the use of the _ProcessExists function Im using. Its much more efficient than autoits built in one due to how autoit enumerates all processes, then goes throught the list looking for the pid. This returns both total cpu and process cpu. expandcollapse popup#AutoIt3Wrapper_UseX64=n #include <winapi.au3> #include <Timers.au3> ;Press ESC to Exit HotKeySet('{ESC}', '_Exit') ;Press Alt-L to generate a test load HotKeySet('!l', '_TestLoad') ;Define requested access rights depending on which os is being used Global $iAccess = 0x0400 ; PROCESS_QUERY_INFORMATION (xp) If (@OSBuild > 3790) Then $iAccess = 0x1000 ; PROCESS_QUERY_LIMITED_INFORMATION (Vista and up) ;Get Handle to autoit process Global $g_hAutoItProcess = _WinAPI_OpenProcess($iAccess, 0, @AutoItPID) If @error Then Exit (ConsoleWrite('Error getting handle' & @LF)) ;Call _GetProcessCPU() 1/sec AdlibRegister('_ProcessCPUExample', 1000) ;Sleep till exit While Sleep(600000) WEnd Func _ProcessCPUExample() Static Local $aEnd, $aStart = _GetProcessUsage($g_hAutoItProcess) $aEnd = _GetProcessUsage($g_hAutoItProcess) ;Get both total and process cpu usage Local $aCPU = _CalcUsage($aStart, $aEnd) ;Make sure cpu values are valid If $aCPU[0] >= 0 And $aCPU[0] <= 100 Then ConsoleWrite('Total CPU Usage = ' & $aCPU[0] & '%' & @TAB & @TAB & 'AutoIt CPU Usage = ' & $aCPU[1] & '%' & @LF) EndIf ;copy last cpu times to start. $aStart = $aEnd EndFunc ;==>_GetProcessCPU Func _GetProcessUsage($hProc) Static Local $tIdle = DllStructCreate("dword;dword"), $tUser = DllStructCreate("dword;dword"), $tKernel = DllStructCreate("dword;dword") Static Local $tCreation = DllStructCreate("dword;dword"), $tExit = DllStructCreate("dword;dword") ;Make sure process still exists If Not _ProcessExists($hProc) Then Return SetError(1, 0, -1) ;Array for holding idle, System and process cpu times Local $aTimes[3] ;Get system CPU times DllCall('Kernel32.dll', "int", "GetSystemTimes", "struct*", $tIdle, "struct*", $tUser, "struct*", $tKernel) $aTimes[0] = DllStructGetData($tIdle, 1) $aTimes[1] = DllStructGetData($tUser, 1) + DllStructGetData($tKernel, 1) ;Get Process cpu times DllCall('Kernel32.dll', "int", "GetProcessTimes", "handle", $hProc, "struct*", $tCreation, "struct*", $tExit, "struct*", $tKernel, "struct*", $tUser) $aTimes[2] = DllStructGetData($tUser, 1) + DllStructGetData($tKernel, 1) Return $aTimes EndFunc ;==>_GetProcessUsage Func _CalcUsage($aCpuStart, $aCpuEnd, $nPercent = True) ;Calculate time difference for idle, system, process Local $iIdle = ($aCpuEnd[0] - $aCpuStart[0]), $iSystem = ($aCpuEnd[1] - $aCpuStart[1]), $iProcess = ($aCpuEnd[2] - $aCpuStart[2]) ;Calculate Total and Process CPU values Local $aCPUTimes[2] $aCPUTimes[0] = ($iSystem - $iIdle) / $iSystem ; Total CPU Usage $aCPUTimes[1] = $iProcess / $iSystem ; Process CPU Usage ;Convert to percent If $nPercent Then $aCPUTimes[0] = Round($aCPUTimes[0] * 100) $aCPUTimes[1] = Round($aCPUTimes[1] * 100) EndIf Return $aCPUTimes EndFunc ;==>_CalcUsage Func _ProcessExists($hProc) Local $aRet = DllCall('kernel32.dll', 'bool', 'GetExitCodeProcess', 'handle', $hProc, 'dword*', 0) If @error Or $aRet[0] = 0 Then Return SetError(1, 0, 0) Return ($aRet[2] = 259) EndFunc ;==>_ProcessExists Func _TestLoad() Local $iA For $i = 1 To 500000 $iA = $iA / 3 Next EndFunc ;==>_TestLoad Func _Exit() _WinAPI_CloseHandle($g_hAutoItProcess) Exit EndFunc ;==>_ExitEdit: Changes to OpenProcess. Access rights should be set according to OS version used. Also debug privilages not needed in this case. Edited July 13, 2013 by Beege Synapsee 1 Assembly Code: fasmg . fasm . BmpSearch . Au3 Syntax Highlighter . Bounce Multithreading Example . IDispatchASMUDFs: Explorer Frame . ITaskBarList . Scrolling Line Graph . Tray Icon Bar Graph . Explorer Listview . Wiimote . WinSnap . Flicker Free Labels . iTunesPrograms: Ftp Explorer . Snipster . Network Meter . Resistance Calculator
Decipher Posted July 13, 2013 Author Posted July 13, 2013 Beege, Thanks for the awesome(efficient) code again! I'll definitely be updating my scripts. So the new dll call syntax removes the need to call DllStructGetPtr directly, hu? I had no idea that AutoIt's ProcessExists() function behaved that way. I'll be adding your functions to my reference list. Anonymous Spoiler
Beege Posted July 13, 2013 Posted July 13, 2013 Your very welcome. And yes, Struct* gives us the ability to pass a pointer to a struct now so no extra calls to DllStructGetPtr in most cases. One other tip, Static is very useful for calling function once, even if your not storing anything in the variable: Static Local $SetPrioityOnce = ProcessSetPriority(@AutoItPID, $nScriptPriority) Assembly Code: fasmg . fasm . BmpSearch . Au3 Syntax Highlighter . Bounce Multithreading Example . IDispatchASMUDFs: Explorer Frame . ITaskBarList . Scrolling Line Graph . Tray Icon Bar Graph . Explorer Listview . Wiimote . WinSnap . Flicker Free Labels . iTunesPrograms: Ftp Explorer . Snipster . Network Meter . Resistance Calculator
kevin3 Posted March 23, 2015 Posted March 23, 2015 When using the above I got occasional negative values. Found it was due to the idle time value rolling over (only the PCU percentage was checked not the Process Percentage). Also sometimes it would have no time elapse between measurements (Some OS caching the function call?) which resulted in incorrect 0% CPU time for the measured Process. Here is the error checking: Func _ProcessCPUExample() Static Local $aEnd, $aStart = _GetProcessUsage($g_hAutoItProcess) sleep(2000) ; make sure some time elapses between measurements. $aEnd = _GetProcessUsage($g_hAutoItProcess) ;Make sure time value did not roll over while $aEnd[0] <= $aStart[0] or $aEnd[1] <= $aStart[1] or $aEnd[2] <= $aStart[2] ConsoleWrite('ReTrying'&@CRLF) $aStart = _GetProcessUsage($g_hAutoItProcess) sleep(2000) $aEnd = _GetProcessUsage($g_hAutoItProcess) WEnd ;Get both total and process cpu usage Local $aCPU = _CalcUsage($aStart, $aEnd) ConsoleWrite('Total CPU Usage = ' & $aCPU[0] & '%' & @TAB & @TAB & 'AutoIt CPU Usage = ' & $aCPU[1] & '%' & @LF) ;copy last cpu times to start. $aStart = $aEnd EndFunc ;==>_GetProcessCPU
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now