Description

A script that monitors the CPU use of a specified EXE, and will kill it if it exceeds a certain % CPU use for a configurable number of checks. Before you use this, you will need to create “config.ini” in the script directory, with the following contents:
"
[Settings]
Process_To_Monitor=prime95.exe
CPU_Alert_Percentage=75
Seconds_Between_Checks=3
Alerts_Before_Process_Close=15
Restart_Service_On_Close?=false
Service_To_Restart=
Use_Verbose_Logging?=false
[End]

"
Obviously changing the settings to meet your needs.

Source Code

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_Res_Comment=Potential fix for small memory leak
#AutoIt3Wrapper_Res_Description=Monitors CPU Usage of a running exe.
#AutoIt3Wrapper_Res_Fileversion=1.0.0.10
#AutoIt3Wrapper_Res_Fileversion_AutoIncrement=y
#AutoIt3Wrapper_Res_SaveSource=y
#AutoIt3Wrapper_Res_Language=1033
#AutoIt3Wrapper_Res_requestedExecutionLevel=highestAvailable
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
;Application to Monitor CPU Use of Specified Process
;And Kill Process if CPU Use Exceeds Specified level
;Written by Steve Pavolko
;Aug 2014
;Updated Nov 2018
#include <Array.au3>


;Establish Current DateTime and a function to update it as needed
Global $date1 = @YEAR & @MON & @MDAY & @HOUR & @MIN & @SEC & ":: "
Global $dayclean = @YEAR & '-' & @MON & '-' & @MDAY
Global $dateclean = @YEAR & '-' & @MON & '-' & @MDAY & '_' & @HOUR & '_' & @MIN & '_' & @SEC & ' '


;Establish Global Vars
Global $config = @ScriptDir & '\config.ini'
Global $debugfile = @ScriptDir & '\debug_' & $dayclean & '.txt'
Global $kill = 0
Global $verboselog = 'temp'
Global $CPU_check1
Global $process1
Global $CPUmax
Global $tick
Global $CPUmaxCount
Global $restart
Global $service
Global $verboselog
Global $CPU_Use = 'temp'
Global $cpu2 = 'temp'

;==
;====
;===========================================================================================================================================================
Func _GetDate()
	$date1 = @YEAR & '-' & @MON & '-' & @MDAY & '_' & @HOUR & ':' & @MIN & ':' & @SEC & ":: "
	$dayclean = @YEAR & '-' & @MON & '-' & @MDAY
	$dateclean = @YEAR & '-' & @MON & '-' & @MDAY & '_' & @HOUR & '_' & @MIN & '_' & @SEC & ' '
EndFunc
;===========================================================================================================================================================
;====
;==



;==
;====
;===========================================================================================================================================================
Func _Debug($dbgmsg)
	_GetDate()
		$debugfile = @ScriptDir & '\debug_' & $dayclean & '.txt'
	FileWrite($debugfile, $dateclean & " " & $dbgmsg & @CRLF)
EndFunc
;===========================================================================================================================================================
;====
;==
;Write Init to Log for Ease of interpreting logs
_Debug('')
_Debug('')
_Debug('')
_Debug('==============================================')
_Debug('===============PROGRAM INIT===================')
_Debug('==============================================')
_Debug('')
_Debug('')
_Debug('')




;Declare Breakout Function
;==
;====
;===========================================================================================================================================================
Func _EndofScript()
_Debug('')
_Debug('')
_Debug('')
_Debug('==============================================')
_Debug('===============PROGRAM DONE===================')
_Debug('==============================================')
_Debug('')
_Debug('')
_Debug('')
EndFunc
;===========================================================================================================================================================
;====
;==

;Build out Function To get Process Properties
;==
;====
;===========================================================================================================================================================
Func _ProcessListProperties($Process = "", $sComputer = ".")
    Local $sUserName, $sMsg, $sUserDomain, $avProcs, $dtmDate
    Local $avProcs[1][2] = [[0, ""]], $n = 1

    ; Convert PID if passed as string
    If StringIsInt($Process) Then $Process = Int($Process)
;~ 		If $verboselog = 'true' Then
;~ 		_Debug('Process1 ' & $Process)
;~ 		EndIf

    ; Connect to WMI and get process objects
    $oWMI = ObjGet("winmgmts:{impersonationLevel=impersonate,authenticationLevel=pktPrivacy, (Debug)}!\\" & $sComputer & "\root\cimv2")
    If IsObj($oWMI) Then
        ; Get collection processes from Win32_Process
        If $Process == "" Then
            ; Get all
            $colProcs = $oWMI.ExecQuery("select * from win32_process")
        ElseIf IsInt($Process) Then
            ; Get by PID
            $colProcs = $oWMI.ExecQuery("select * from win32_process where ProcessId = " & $Process)
        Else
            ; Get by Name
            $colProcs = $oWMI.ExecQuery("select * from win32_process where Name = '" & $Process & "'")
        EndIf
;~ 			If $verboselog = 'true' Then
;~ 			_Debug('Process2 ' & $Process)
;~ 			EndIf
        If IsObj($colProcs) Then
            ; Return for no matches
            If $colProcs.count = 0 Then Return $avProcs

            ; Size the array
            ReDim $avProcs[$colProcs.count + 1][10]
            $avProcs[0][0] = UBound($avProcs) - 1

            ; For each process...
            For $oProc In $colProcs
                ; [n][0] = Process name
                $avProcs[$n][0] = $oProc.name
                ; [n][1] = Process PID
                $avProcs[$n][1] = $oProc.ProcessId
                ; [n][2] = Parent PID
                $avProcs[$n][2] = $oProc.ParentProcessId
                ; [n][3] = Owner
                If $oProc.GetOwner($sUserName, $sUserDomain) = 0 Then $avProcs[$n][3] = $sUserDomain & "\" & $sUserName
                ; [n][4] = Priority
                $avProcs[$n][4] = $oProc.Priority
                ; [n][5] = Executable path
                $avProcs[$n][5] = $oProc.ExecutablePath
                ; [n][8] = Creation date/time
                $dtmDate = $oProc.CreationDate
                If $dtmDate <> "" Then
                    ; Back referencing RegExp pattern from weaponx
                    Local $sRegExpPatt = "\A(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:.*)"
                    $dtmDate = StringRegExpReplace($dtmDate, $sRegExpPatt, "$2/$3/$1 $4:$5:$6")
                EndIf
                $avProcs[$n][8] = $dtmDate
                ; [n][9] = Command line string
                $avProcs[$n][9] = $oProc.CommandLine

                ; increment index
                $n += 1
            Next
        Else
            SetError(2); Error getting process collection from WMI
        EndIf
        ; release the collection object
        $colProcs = 0

        ; Get collection of all processes from Win32_PerfFormattedData_PerfProc_Process
        ; Have to use an SWbemRefresher to pull the collection, or all Perf data will be zeros
        Local $oRefresher = ObjCreate("WbemScripting.SWbemRefresher")
        $colProcs = $oRefresher.AddEnum($oWMI, "Win32_PerfFormattedData_PerfProc_Process" ).objectSet
        $oRefresher.Refresh

        ; Time delay before calling refresher
        Local $iTime = TimerInit()
        Do
            Sleep(20)
        Until TimerDiff($iTime) >= 100
        $oRefresher.Refresh

        ; Get PerfProc data
        For $oProc In $colProcs
            ; Find it in the array
            For $n = 1 To $avProcs[0][0]
                If $avProcs[$n][1] = $oProc.IDProcess Then
                    ; [n][6] = CPU usage
                    $avProcs[$n][6] = $oProc.PercentProcessorTime
					$CPU_check1 = $oProc.PercentProcessorTime

                    ; [n][7] = memory usage
                    $avProcs[$n][7] = $oProc.WorkingSet
                    ExitLoop

                EndIf
            Next
        Next
    Else
        SetError(1); Error connecting to WMI
    EndIf
	$cpu2 = $avProcs[1][6]

;~ 			If $verboselog = 'true' Then
;~ 			_Debug('Process3 ' & $Process)
;~ 			EndIf
	If $verboselog = 'true' Then
	_Debug('CPU Time = ' & $CPU_check1)
	EndIf
    ; Return array
	$oWMI = 0
	_ReduceMemory()
    Return $avProcs
EndFunc  ;==>_ProcessListProperties
;===========================================================================================================================================================
;====
;==



;==
;====
;===========================================================================================================================================================
Func _ReadConfig()

	$process1 = IniRead($config, "Settings", "Process_To_Monitor", "NotFound")
	$CPUmax = IniRead($config, "Settings", "CPU_Alert_Percentage", "NotFound")
	$tick = IniRead($config, "Settings", "Seconds_Between_Checks", "NotFound")
	$CPUmaxCount = IniRead($config, "Settings", "Alerts_Before_Process_Close", "NotFound")
	$restart = IniRead($config, "Settings", "Restart_Service_On_Close?", "NotFound")
	$service = IniRead($config, "Settings", "Service_To_Restart", "NotFound")
	$verboselog = IniRead($config, "Settings", "Use_Verbose_Logging?", "NotFound")

	If $restart = 'True' Or $restart = 'true' Or $restart = 'Yes' Or $restart = 'yes' Then
		$restart = 'True'
	EndIf

	If $verboselog = 'True' Or $verboselog = 'true' Or $verboselog = 'Yes' Or $verboselog = 'yes' Then
			_Debug('Monitoring '&$process1)
			_Debug($CPUmax&'% Considered to be high CPU use.')
			_Debug('Waiting '&$tick & ' seconds between checks.')
			_Debug($process1 & ' Can hit ' & $CPUmax& '% ' &$CPUmaxCount & ' times before being terminated.')
			_Debug('Will we restart a service after we close ' & $process1& '? '&$restart)
			If $restart = 'True' Then
			_Debug('Will restart '&$service)
			EndIf

	EndIf

	If $verboselog = 'True' Or $verboselog = 'true' Or $verboselog = 'Yes' Or $verboselog = 'yes' Then
		$verboselog ='true'
	EndIf

	If $verboselog = 'true' Then
	_Debug('Config Read Complete')
	EndIf

EndFunc
;===========================================================================================================================================================
;====
;==

_Debug('Initial Config Read.')
_ReadConfig()




;==
;====
;===========================================================================================================================================================
Func _CheckCPUofProcess()

	$CPU_check1 = 'Default'
	 $CPU_use = _ProcessListProperties($process1,".")
	 If $verboselog = 'true' Then
	 _Debug('Check of CPU directly after function call: ' & $CPU_check1)
	 EndIf

	 $CPU_Use = $CPU_check1
	 If $verboselog = 'true' Then
	_Debug($CPU_Use)
		EndIf

EndFunc
;===========================================================================================================================================================
;====
;==




_Debug('Initial Process Check.')
_CheckCPUofProcess()
_Debug('Finished Initial Process Check.')





;==
;====
;===========================================================================================================================================================
Func _RestartService()

	If $verboselog = 'true' Then
		_Debug('Calling RestartService Function')
	EndIf


	If $restart = 'True' Then
		RunWait("net stop" & $service, @SW_HIDE)
		_Debug($service & ' stopped.')
		RunWait("net start" & $service, @SW_HIDE)
		_Debug($service & ' started.')
	Else
		If $verboselog = 'true' Then
		_Debug('Not set to restart a service.')
		EndIf
	EndIf




EndFunc
;===========================================================================================================================================================
;====
;==




;==
;====
;===========================================================================================================================================================
;~ Func _KillProcessCall()
;~ 	_KillProcess()
;~ EndFunc
;===========================================================================================================================================================
;====
;==




;==
;====
;===========================================================================================================================================================
Func _ReduceMemory()
    Local $ai_GetCurrentProcessId = DllCall('kernel32.dll', 'int', 'GetCurrentProcessId')
    Local $ai_Handle = DllCall("kernel32.dll", 'int', 'OpenProcess', 'int', 0x1f0fff, 'int', False, 'int', $ai_GetCurrentProcessId[0])
    Local $ai_Return = DllCall("psapi.dll", 'int', 'EmptyWorkingSet', 'long', $ai_Handle[0])
    DllCall('kernel32.dll', 'int', 'CloseHandle', 'int', $ai_Handle[0])
    Return $ai_Return[0]
EndFunc
;===========================================================================================================================================================
;====
;==








;==
;====
;===========================================================================================================================================================
Func _KillProcess()


;~ 	_CheckCPUofProcess()
	$use1 = $CPU_Use
	If $verboselog = 'true' Then
	_Debug('CPU Use: ' & $use1 & '%')
	EndIf

	If $use1 = 'Default' or $use1 = 'default' Then
		If $verboselog = 'true' Then
		_Debug('Process likely not running. Sleeping for an interval.')
		FileWrite($debugfile, @CRLF)
		FileWrite($debugfile, @CRLF)
		FileWrite($debugfile, @CRLF)
		EndIf
		$kill = 0
		sleep($tick*1000)



	EndIf

	$use1 = Int($use1)
	$CPUmax = Int($CPUmax)
	$kill = Int($kill)


	If $use1 >= $CPUmax Then
		$kill = $kill + 1
		If $verboselog = 'true' Then
		_Debug('Current Kill Count: ' & $kill)
		EndIf

	ElseIf $use1 < $CPUmax Then
		$kill = Int(0)
		If $verboselog = 'true' Then
		_Debug('Current Kill Count: ' & $kill)
		EndIf
	EndIf

	If $kill >= $CPUmaxCount Then
				_Debug($process1 & ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
				_Debug($process1 & ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
				_Debug($process1 & ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
				_Debug($process1 & ' Has hit CPU Alert threshold and is being terminated.')
				_Debug($process1 & ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
				_Debug($process1 & ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
				_Debug($process1 & ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
				_Debug($use1)
				$kill = 0
				If $verboselog = 'true' Then
				_Debug('Current Kill Count: ' & $kill)
				EndIf
				ProcessClose($process1)   ;<----------------------------------------------------------------------------------------------------------------------------------------- THIS LINE KILLS THE EXE
				_RestartService()

				FileWrite($debugfile, @CRLF)
				FileWrite($debugfile, @CRLF)
				FileWrite($debugfile, @CRLF)

	EndIf

	sleep($tick*1000)

EndFunc
;===========================================================================================================================================================
;====
;==

;==
;====
;===========================================================================================================================================================
While True

	If $verboselog = 'true' Then
		_Debug('===================================')
		_Debug('=======THATS A START, FOLKS!=======')
		_Debug('===================================')
	EndIf

		If $verboselog = 'true' Then
			_Debug('Loop Begin')
		EndIf

		If $verboselog = 'true' Then
			_Debug('Reading Config')
		EndIf

		_ReadConfig()
		sleep(50)

		If $verboselog = 'true' Then
			_Debug('Calling CheckCPUofProcess Function')
		EndIf

		_CheckCPUofProcess()
		sleep(1000)

		If $verboselog = 'true' Then
			_Debug('Calling KillProcess Function')
		EndIf

		_KillProcess()
		sleep(20)

;~ 		If $verboselog = 'true' Then
;~ 			_Debug('Calling RestartService Function')
;~ 		EndIf

;~ 		_RestartService()

		If $verboselog = 'true' Then
			_Debug('Calling Memory Reduction Function')
		EndIf

		_ReduceMemory()

		If $verboselog = 'true' Then
			_Debug('Loop End')
		EndIf

	If $verboselog = 'true' Then
		_Debug('===================================')
		_Debug('=======THATS A WRAP, FOLKS!========')
		_Debug('===================================')
		_Debug(@CRLF)
		_Debug(@CRLF)
		_Debug(@CRLF)
	EndIf

WEnd
;===========================================================================================================================================================
;====
;==
FileWrite($debugfile, @CRLF)
FileWrite($debugfile, @CRLF)
FileWrite($debugfile, @CRLF)
OnAutoItExitRegister(_EndofScript())





1 Spice up

Works very well but has a slow memory leak. We use it to handle a process that has a known bug from killing the server. Was a good laugh when CPUmon killed the server. With 30-second check intervals, it will consume about 10MB per hour.

Put in a potential fix. If you want to test and let me know results, that would be great.