-- appInfoInTitle: -- This AppleScript displays the amount of memory being used by designated applications -- in the title of their frontmost windows. -- It also displays the CPU usage of these apps if it exceeds 10%. -- And it gives a warning if the memory usage of an app exceeds a specified level. -- This was originally designed to keep watch on Safari's memory consumption -- but it can be used for various apps. -- It is intended to be saved as an application and left running all of the time. -- Cameron Hayne (macdev@hayne.net) December 2006 property scriptName : "appInfoInTitle" property useDelayLoop : false -- set this to true if you want to run from Script Editor property useGrowl : true -- set this to true to get warnings via Growl (http://growl.info/) property updateDelay : 5 -- seconds property cpuFloor : 10 -- % above which CPU usage will be shown in the title property warnIntervalIncrement : 120 -- seconds global appInfoList -- list of AppInfo objects (added to via 'addAppInfo') -- registerApps: -- Add a call to 'addAppInfo' for each app you want to watch on registerApps() addAppInfo given appName:"Safari", updatePeriod:5, warnMB:100 addAppInfo given appName:"Terminal", updatePeriod:60, warnMB:15 addAppInfo given appName:"Mail", updatePeriod:60, warnMB:30 end registerApps -- update: -- This subroutine is invoked every 'updateDelay' seconds on update() repeat with appInfo in appInfoList updateAppInfo(appInfo) end repeat end update -- the run handler (invoked at script startup) on run set appInfoList to {} registerApps() if useGrowl then registerWithGrowl() end if if useDelayLoop then repeat update() delay updateDelay end repeat end if end run -- the idle handler (invoked every 'updateDelay' seconds) on idle if useDelayLoop then -- we should never get here display alert "Internal error: idle handler called even though using delay loop" quit end if update() return updateDelay end idle -- addAppInfo: -- Creates a new 'appInfo' object and adds it to 'appInfoList' -- 'updatePeriod' is the time in seconds between updates for this app -- 'warnMB' is the memory usage (in megabytes) at which warnings will be issued on addAppInfo given appName:anAppName, updatePeriod:anUpdatePeriod, warnMB:aWarnMB --on addAppInfo(anAppName, anUpdatePeriod, aWarnMB) set prevTime to (current date) - 1000 script appInfo property appName : anAppName property updatePeriod : anUpdatePeriod property warnMB : aWarnMB property lastUpdateTime : prevTime property lastWarnTime : prevTime property numWarnings : 0 property numMB : 0 property percentCpu : 0 end script set appInfoList to appInfoList & appInfo end addAppInfo -- updateAppInfo: -- Displays the amount of memory being used by the app 'appName' -- Pops up an alert dialog if the amount of memory used exceeds 'warnMB' on updateAppInfo(appInfo) set elapsed to secondsSince(lastUpdateTime of appInfo) if elapsed < (updatePeriod of appInfo) then return set pid to pidOfRunningApp(appName of appInfo) if pid is -1 then -- app is not running, so nothing to do else set info to getProcessInfo(pid) if info is "" then return set numMB of appInfo to round (first word of info as real) set percentCpu of appInfo to round (second word of info as real) showAppInfoInTitle(appInfo) checkIfMemoryUseMeritsWarning(appInfo) end if set lastUpdateTime of appInfo to (current date) end updateAppInfo -- showAppInfoInTitle: -- Displays the amount of memory being used by the app 'appName' in the title -- of the frontmost window of that app. on showAppInfoInTitle(appInfo) set appName to (appName of appInfo) set numMB to (numMB of appInfo) set percentCpu to (percentCpu of appInfo) try tell application appName if (count of windows) > 0 then set title to name of window 1 set delim to " *** " set origTitle to my removeLastPart(title, delim) set title to origTitle & delim & numMB & " MB" if percentCpu > cpuFloor then set title to title & ", " & percentCpu & "% CPU" end if set name of window 1 to title end if end tell on error -- there shouldn't be any errors -- (assuming that the app responds to 'name of window') -- but if an error occurs, we ignore it end try end showAppInfoInTitle -- checkIfMemoryUseMeritsWarning: -- Puts up a warning dialog about 'appName's memory usage -- but avoids doing this too soon after the last warning -- (increases the interval between warnings each time) on checkIfMemoryUseMeritsWarning(appInfo) set appName to (appName of appInfo) set numMB to (numMB of appInfo) set warnMB to (warnMB of appInfo) set lastWarnTime to (lastWarnTime of appInfo) set numWarnings to (numWarnings of appInfo) if numMB > warnMB then set elapsed to secondsSince(lastWarnTime) set warnInterval to (numWarnings * warnIntervalIncrement) if elapsed > warnInterval then set msg to appName & " is taking more than " & warnMB & " MB" displayWarning(appName, msg) set lastWarnTime to (current date) set numWarnings to (numWarnings + 1) end if else if numMB > (0.95 * warnMB) then -- avoid continual warnings when oscillating around 'warnMB' level set numWarnings to 1 else set numWarnings to 0 end if set lastWarnTime of appInfo to lastWarnTime set numWarnings of appInfo to numWarnings end checkIfMemoryUseMeritsWarning -- displayWarning: -- Displays a warning relating to 'appName' on displayWarning(appName, msg) if useGrowl then try displayWarningViaGrowl(appName, msg) on error displayWarningViaAlert(appName, msg) end try else displayWarningViaAlert(appName, msg) end if end displayWarning -- displayWarningViaAlert: -- Displays a warning via an AppleScript alert dialog on displayWarningViaAlert(appName, msg) tell application appName display alert msg end tell end displayWarningViaAlert -- displayWarningViaGrowl: -- Displays a warning via "Growl" on displayWarningViaGrowl(appName, msg) tell application "GrowlHelperApp" notify with name "MemoryUseWarning" title "Warning" description msg application name scriptName end tell end displayWarningViaGrowl -- registerWithGrowl: -- Registers our notifications with the "Growl" tool on registerWithGrowl() set notifList to {"MemoryUseWarning"} tell application "GrowlHelperApp" register as application scriptName all notifications notifList default notifications notifList end tell end registerWithGrowl -- secondsSince: -- Returns the number of seconds since 'aDate' on secondsSince(aDate) set curr to (current date) set elapsed to (curr - aDate) return elapsed end secondsSince -- fixLineEndings: -- returns a string with Unix-style line endings on fixLineEndings(str) set oldTIDs to AppleScript's text item delimiters set theLines to paragraphs of str set AppleScript's text item delimiters to ASCII character 10 set fixedStr to theLines as string set AppleScript's text item delimiters to oldTIDs return fixedStr end fixLineEndings -- getProcessInfo: -- Returns info about the resource usage of the specified process. -- It gets this info by invoking the Unix 'ps' command via a Perl script. -- The return value gives the number of megabytes used -- followed by the percentage of CPU used on getProcessInfo(pid) set perlCode to fixLineEndings(" # This script gets the 'RSS' & '%CPU' of the process with the given 'pid' # The 'RSS' (resident set size) is a rough measure of how much RAM # the process is using. The value is converted to megabytes. open(PS, \"/bin/ps -p " & pid & " -o pid,rss,%cpu |\"); while () { if (/^\\s*" & pid & "\\s+(\\d+)\\s+([\\d.]+)\\s*$/) { my $rss = $1; my $cpu = $2; my $rssMB = sprintf(\"%.1f\", $rss / 1024); print \"$rssMB $cpu\"; last; } } close(PS); ") set info to do shell script "perl -e " & quoted form of perlCode return info end getProcessInfo -- removeLastPart: -- Returns the string 'str' without the last part starting with 'delim' on removeLastPart(str, delim) set oldTIDs to AppleScript's text item delimiters set AppleScript's text item delimiters to delim if (count str's text items) > 1 then set str to str's text 1 thru text item -2 end if set AppleScript's text item delimiters to oldTIDs return str end removeLastPart -- pidOfRunningApp: -- Returns the pid of the process if the app 'appName' is running, -- otherwise returns -1 on pidOfRunningApp(appName) tell application "System Events" try set pid to the unix id of process appName on error set pid to -1 end try end tell return pid end pidOfRunningApp -- Copyright 2006 Cameron Hayne - macdev@hayne.net -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2 of the License, or -- (at your option) any later version.