The macosxhints Forums

The macosxhints Forums (http://hintsforums.macworld.com/index.php)
-   Applications (http://hintsforums.macworld.com/forumdisplay.php?f=5)
-   -   AppleScript to display Safari's memory usage (http://hintsforums.macworld.com/showthread.php?t=64782)

hayne 12-22-2006 12:10 PM

I have edited the above (latest) version of the 'appInfoInTitle' script to add an error check on the result from 'getProcessInfo'.


And I'd encourage any users of this script to install Growl (http://growl.info/) and enable the 'appInfoInTitle' script to use Growl for warnings by setting the 'useGrowl' variable to true. Growl is very elegant.

NovaScotian 12-22-2006 02:32 PM

Works beautifully with Camino and NetNewsWire added to the mix.

mark hunte 12-27-2006 03:55 PM

Quote:

Originally Posted by hayne (Post 343600)
So you've tried quitting it and relaunching and it is always taking the same amount of RAM?

Thats correct, still it is always the same?


15190 Safram4g mark 0.60 1 14.73 MB 143.84 MB

hayne 01-02-2007 12:04 PM

Quote:

Originally Posted by mark hunte (Post 344793)
Thats correct, still it is always the same?
15190 Safram4g mark 0.60 1 14.73 MB 143.84 MB

Hmm, if it took 15 MB of RAM on my machine I would probably be reluctant to run it all the time. (It takes 5.1 MB of RAM on my iBook G4)

Have you tried quitting Script Editor and then running the script?
I never figured out what caused it to sometimes take more RAM sometimes but on my machine it now seems to be more consistently taking 5 MB.

And check that you have it compiled as an application bundle.

mark hunte 01-02-2007 01:25 PM

Ok tried again.
I did not have it as a Bundle.

It seems to take at least two new launches after quitting Script Editor to get it down from 14.. to 5..


I do sometimes get high Ram if I get a warning first thing after launch?
will try it with growl.

If I bring SE back up after launch then all is ok.

mark hunte 01-02-2007 01:52 PM

I have just go consistant results doing the below.

Open the Script (RAM app) in SE, Leave its window open
and then launch the RAM app (above 14mb)

Open the Script (RAM app) in SE, Edited it and Save it, Leave its window open
and then launch RAM app (above 14mb)

Open the Script (RAM app)in SE, Edited it and Save it, close its window.
Leaving SE running, and then launch RAM app. (above 14mb)

Open the Script (RAM app) in SE, Edited it and Save it, close its window.
Select a second script window and do a Save in that, leaving it open and then launch RAM app I get low ram. (under 4mb)

mark hunte 01-05-2007 08:34 PM

Hi, Hayne,
I noticed from the Hint you posted today that Some people had an issue with Growl, either they did not want it or they did not read the instruction correctly. and ran in to compiling issues.

So I changed the script so if you do not have growl then the the Growl property will be ignored when the script is run.
Also you will not be asked for growl when you first compile, as I have used a run script to call the growl app.
Code:

-- 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
property GrowlApp : "GrowlHelperApp" as string
global appInfoList -- list of AppInfo objects (added to via 'addAppInfo')
global G_chk
-- 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
        --try
       
        set G_chk to true
        tell application "System Events"
                -- Check to see if Growl is installed -- is not the growl app will not be called later in the script
                if useGrowl is true then
                        set G_chk to true
                        set thePathName to name of (current user) as string
                        set stp to name of (startup disk) as string
                       
                        set Lib to (":Library:PreferencePanes:")
                        tell application "Finder"
                                set gP to "Growl.prefPane"
                                set Syssetpane to stp & Lib as alias
                                set Usrsetpane to stp & ":Users:" & thePathName & Lib as alias
                                if (exists item gP of Syssetpane) then
                                else if (exists item gP of Usrsetpane) then
                                else
                                        set G_chk to false
                                end if
                        end tell
                end if
        end tell
        --end try
        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 growl is not installed but the "property useGrowl" is set to true then the warning will
        -- have "Growl is not installed" under it.
        if G_chk is true then
                if useGrowl then
                        try
                                displayWarningViaGrowl(appName, msg)
                        on error
                                displayWarningViaAlert(appName, msg & return & "(Growl is not installed)")
                        end try
                else
                        displayWarningViaAlert(appName, msg)
                end if
        else
                displayWarningViaAlert(appName, msg & return & "(Growl is not installed)")
        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)
        set the_command to "tell application " & "\"" & GrowlApp & "\" to notify with name \"MemoryUseWarning\" title \"Warning\" description " & "\"" & msg & "\"" & " application name \"" & scriptName & "\""
        run script the_command
end displayWarningViaGrowl

-- registerWithGrowl:
-- Registers our notifications with the "Growl" tool
on registerWithGrowl()
        set notifList to "{\"MemoryUseWarning\"}" as string
        get scriptName
        set T to "tell application " & "\"" & GrowlApp & "\" to register as application  \"" & scriptName & "\"  all notifications " & notifList & "  default notifications " & notifList
        if G_chk is true then
                run script T
        end if
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 (<PS>)
{
    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.


hayne 01-06-2007 03:06 AM

Mark:
Thanks for the idea of using a 'run script' to avoid compile-time issues with AppleScripts referring to apps that might not be installed.
I'll probably integrate this into my version of the script soon.


All times are GMT -5. The time now is 01:42 PM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Site design © IDG Consumer & SMB; individuals retain copyright of their postings
but consent to the possible use of their material in other areas of IDG Consumer & SMB.