I’ve been using a modified form of Nigel Garvey’s ChimeHours script from a thread started by Kel for a long time now. I rarely reboot or log out, but occasionally notice that the script below (my version) gets huge (over 100 MB). Stopping and restarting it cures that, of course, but I’m wondering how to find out what part of it is causing the problem.
The Script
on idle
local h
tell (time of (current date))
set h to ((it div hours + 11) mod 12) + 1
tell (it mod hours)
if (it < 10) then
-- If it's now less than ten seconds after an hour, chime.
my chime(h)
else if (it < 300) then
-- Otherwise, if it's within the first five minutes, make a belated announcement.
my waffle(h)
end if
end tell
-- Idle until the next five-minute boundary.
return (86399 - it) mod 300 + 1 -- ie. (days - (time of (current date)) - 1) mod (5 * minutes) + 1
end tell
end idle
to chime(h)
tell application "Play Sound"
«event µMCSplay» (("" & (path to "dlib" from system domain) & "Sounds:" & "Glass.aiff") as alias) given «class repe»:(h - 1)
quit
end tell
end chime
on waffle(h)
say "Oops! It's a little past " & h & " o'clock."
end waffle
Been out; sorry for the delay. The figure I quoted was the resident number, the virtual number was even larger. Since this thing only fires once an hour, it takes quite a while.
Crap. An idling AppleScript will crash when you record its memory allocation activity with Sampler. ObjectAlloc is similarly problematic, though not crashing, with a vanilla idling AppleScript, it seems. Hangs. [Edit: See below regarding memory analysis tools and AppleScript.]
Hmm. It’s your script that is showing the huge memory usage, not “Play Sound”, right?
From the Play Sound page: Play Sound is a simple, no-fuss sound player for Mac OS X. It supports QuickTime sound files (for example, AIFF or MP3 files), Classic Mac OS System 7 sound files (files with a file type of ‘sfil’ that contain 'snd ’ resources), or any 'snd ’ resources embedded into any file. You can play an unlimited number of sounds concurrently or one at a time. You can loop sounds, repeat sounds, play specified portions of sounds, control the volume of sounds played, pause and continue sound play, and press the Escape or Command-Period keys to abort sound play.
Notice that I quit it in the script. Would it be leaving resources behind?
Play Sound can’t leave anything unfreed when it terminates. That memory is reclaimed by the system.
I’m running a version of your script, modified not to talk to other applications or send raw Apple Events, in a tight idle loop. Let this idle for a few hours and report what you see:
on idle
local h
tell (time of (current date))
set h to ((it div hours + 11) mod 12) + 1
tell (it mod hours)
if (it < 10) then
my testCall1()
else if (it < 300) then
my testCall2()
end if
end tell
return 1
end tell
end run
on testCall1()
log "test call 1"
end testCall1
on testCall2()
log "test call 2"
end testCall2
I don’t know if this helps because I never used it, and I don’t know how to use it, but I came across something recently that sounds like it might help. It’s a unix command called “leaks”. The man page for it seems detailed enough so maybe it can be of use to you.
MallocDebug and ObjectAlloc freak out over AppleScript applications that are not Mach-O bundles. (The leaks utility also requires that AppleScript applications are Mach-O bundles as well. Otherwise, it will report that it doesn’t know how to inspect the target’s allocation zone.)
I’m not finding any leaks with the test case I posted. Compile your original script in Script Editor and save it as an application bundle. Launch the bundle with MallocDebug and let it run for a bit, then start hunting for leaks.
I’m running this script every hour with cron and leave Play Sound running.
set cur_hour to ((((time of (current date)) div hours) + 11) mod 12) + 1
repeat cur_hour times
tell application "Play Sound"
play alias "Macintosh HD:System:Library:Sounds:Purr.aiff"
end tell
end repeat
You probably know this, but in previous AppleScript versions, there was a known memory leak with idle handlers. Maybe they didn’t get all the kinks out of it.
I was thinking that your script might be erroring. Wierd things might happen when idle handlers error.
I’d take the return statements out of the tell blocks and use one return statement as the last statement of every handler.
Thanks, Kel. I’ll look into running it with cron (or launchd). I really don’t want to give it up - it keeps me from getting so absorbed that I forget what time it is.
Yeah, although I have lots of memory (2GB) and the ChimeHours idler stay-open doesn’t interfere with anything, it is up to 102/462 MB since I quit it and restarted it yesterday. Interesting because it doesn’t get much larger than that - those were the figures (roughly) after several weeks. This may not be a leak at all - the system might just be keeping its parts because there’s lots of room. In addition to Play Sound, there’s also Cepstral Voices (David) for the “Oops” - best speaking voice I’ve heard on a computer (though not free). I’ll continue to monitor it and see if it grows further.
I agree totally, I bought the voice a few months ago.
The difference to the built-in voices is like the difference between pixel and vector graphics
Since Tiger launchd has been introduced, there is a easy way to define scheduled tasks,
which can e.g. trigger a script. Based on Craig’s Using launchd with AppleScript… article,
I wrote an installer, which creates the .plist file at the right place and loads the task via launchctl.
An unload function is also included, which unloads the task and removes the .plist file.
The script to be triggered must be in ~/Library/Scripts, but you can change the parameters in the first three lines.
The task runs on every clock hour:
property pListLabel : "com.chimehours"
property triggeredScriptName : "ChimeHours.scpt"
set triggeredScriptFolder to path to scripts folder as Unicode text
set userLibraryPath to (path to home folder as Unicode text) & "Library:"
set LaunchAgentsFolder to userLibraryPath & "LaunchAgents:"
set pListfile to LaunchAgentsFolder & pListLabel & ".plist"
try
tell application "Finder" to make new folder at userLibraryPath with properties {name:"LaunchAgents"}
end try
set PLIST_data to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
<key>Label</key>
<string>" & pListLabel & "</string>
<key>LowPriorityIO</key>
<true/>
<key>Program</key>
<string>/usr/bin/osascript</string>
<key>ProgramArguments</key>
<array>
<string>osascript</string>
<string>" & POSIX path of triggeredScriptFolder & triggeredScriptName & "</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>
"
set b to button returned of (display dialog "Load or Unload " & quote & pListLabel & ".plist" & quote buttons {"Cancel", "Unload", "Load"})
if b is "Load" then
try
set ff to open for access file pListfile with write permission
write PLIST_data to ff as «class utf8»
close access ff
on error
try
close access file target
end try
display dialog pListLabel & " couldn't be created" buttons {"Cancel"} default button 1
end try
launchctl("load", pListfile)
else
launchctl("unload", pListfile)
end if
----------------------
on launchctl(load, pListfile)
try
set launchctlCmd to "launchctl " & load & " -w " & quoted form of POSIX path of pListfile
do shell script launchctlCmd
if load is "unload" then do shell script "rm " & quoted form of POSIX path of pListfile
display dialog quote & pListLabel & "\" successfully " & load & "ed" buttons {"OK"} default button 1
on error
display dialog load & " failed" buttons {"Cancel"} default button 1 with icon stop
end try
end launchctl
PS: loading the task can take a while. In every case a status message will be displayed