Firefox, Safariが未使用時、CPU使用率を下げて発熱を抑えるAppNapもどき
スクリプトの改善版です。
AppNapライクにWebProcessをコントロール(cputhrottle & applescript)
AppNapライクにWebProcessをコントロール(cputhrottle & applescript)v2.0
v2.0 不定期にエラーが発生しています。
sh: -c: line 0: syntax error near unexpected token '(' sh: -c: line 0: 'logger -t appnap-browser -i errorMessage: /bin/sh: line 0: kill: (1553) - No such process, errorNumber: 1'
エラーの内容からすると、
loggerコマンドでログを出力する際に
指定したログメッセージに誤りがあるようです。
(とかあるとエスケープされずにそのまま出力されてエラーになると予想できます。
quoted fromでログメッセージをエスケープ処理するように修正します。
do shell script “logger -t appnap-browser -i ” & quoted form of logmsg
これ以外に、いくつか事象・現象が発生していました。
- /tmp/にコマンドを置いていましたが、しばらくすると/tmp/のクリア処理が動作し、消去されて起動できなくなる事象
- sleep復帰後、たまにcputhrottleが落ちている現象
コマンド位置の変更と、cputhrottleの存在確認の修正を合わせて行いました。
appnap-webbrowser.app(applescript) v3.0
前提
- cputhrottleコマンドがインストールされている。
- スクリプトを実行するユーザは管理者権限を持っている
cputhrottleは、こちら(AppNapライクにWebProcessをコントロール(cputhrottle & applescript))を参考にしてください。
ソース
以下ソースをAppleScriptエディタにコピペします。
コピペ後、以下設定を書き換えてください。
- myPassword to “– 管理者パスワードに置き換えてください –” の行を修正してください。
- CPU_SLOW_COMMAND to “/path/to/unix”の行を修正してください。
cputhrottleコマンドをご自身の環境に合わせて修正してください。
ex) cputhrottleコマンドが/Users/Username/command/cputhrottleにある場合
set CPU_SLOW_COMMAND to “/Users/Username/command/cputhrottle”
と指定します。
ファイルフォーマット:アプリケーション、オプションとして「ハンドラの実行時に終了しない」を選んで、書き出します。
(ここではファイル名:appnap-browser.appを想定しています)
global CPU_SLOW_PERCENTAGE global CPU_SLOW_COMMAND global myProcessDataList global myPassword global process_monitor_interval global process_monitor_counter set myPassword to "-- 管理者パスワードに置き換えてください --" -- <- 管理者のパスワード set CPU_SLOW_PERCENTAGE to "5" -- <- フォーカスがない場合:プロセスのCPU使用率をpercentで指定 -- cputhrottleコマンドの場所を指定 set CPU_SLOW_COMMAND to "/path/to/unix/cputhrottle" -- cputhrottleコマンドの生存確認間隔を指定(秒) set process_monitor_interval to 60 -- On idle が60回呼び出されたら set myProcessDataList to {} set process_monitor_counter to 0 -- =========================== -- ProcessData -- クラス script ProcessData property pid : -1 property procName : "" on new(thePid, theProcName) copy me to theData set_pid(thePid) of theData set_procName(theProcName) of theData theData end new on set_pid(value) set pid to value end set_pid on set_procName(value) set procName to value end set_procName end script -- =========================================== -- FindProcessData -- theList配列中のtheProcNameを検索する -- 入力: theList ProcessDataの配列 -- theProcName 検索するプロセス名を指定 -- 出力: retList 検索した結果(ProcessData配列) -- 返り値: 検索した結果(ProcessData配列) on FindProcessDataList(theList, theProcName, retList) repeat with v in theList if procName of v is equal to theProcName then set end of retList to v end if end repeat return retList end FindProcessDataList -- =========================================== -- DeleteProcessDataList -- theList配列中のtheProcNameを配列から削除する -- 入力: theList ProcessDataの配列 -- theProcName 削除するプロセス名を指定 -- 出力: retList 削除されなかったデータ配列(ProcessData配列) -- 返り値: 削除されなかったデータ配列(ProcessData配列) on DeleteProcessDataList(theList, theProcName) set retList to {} repeat with v in theList if procName of v is not equal to theProcName then set end of retList to v end if end repeat return retList end DeleteProcessDataList -- =========================================== -- StartBackgroundCpuControl -- プロセスの使用率を下げる -- 入力: theProcName 使用率を下げるプロセス名を指定 -- 出力: theControledList コントロール中のデータ配列(ProcessData配列) -- 返り値: なし on StartBackgroundCpuControl(theProcName, theControledList) tell application "System Events" set pList to (unix id of every process whose name contains theProcName) if pList is not equal to {} then -- syslog("StartBackgroundCpuControl " & theProcName & "") of me repeat with unixID in pList do shell script CPU_SLOW_COMMAND & " " & (unixID as string) & " " & CPU_SLOW_PERCENTAGE & " >/dev/null 2>&1 & echo $!" password myPassword with administrator privileges set pid to result syslog("start " & theProcName & " cputhrottle " & (unixID as string) & " " & CPU_SLOW_PERCENTAGE & ", PID=" & pid) of me set end of theControledList to new(pid, theProcName) of ProcessData end repeat end if end tell end StartBackgroundCpuControl -- =========================================== -- StopBackgroundCpuControl -- プロセスの使用率をもとに戻す -- 入力: theControledList もとに戻すデータ配列(ProcessData配列) -- 出力: なし -- 返り値: なし on StopBackgroundCpuControl(theControledList) -- syslog("StopBackgroundCpuControl targetCount=" & (length of theControledList as string) & "") of me repeat with v in theControledList try if IsExistsProcess(pid of v) of me is equal to true then do shell script "kill -INT " & (pid of v as string) password myPassword with administrator privileges syslog("stop " & (procName of v) & " cputhrottle , PID=" & (pid of v as string)) of me end if on error errorMessage number errorNumber syslog("StopCpuControl errorMessage: " & errorMessage & ", errorNumber: " & errorNumber) of me end try end repeat end StopBackgroundCpuControl -- =========================================== -- IsExistsProcess -- プロセスの存在確認 -- 入力: thePid プロセスID -- 出力: なし -- 返り値: true 存在 false 存在しない on IsExistsProcess(thePid) try set the_result to do shell script "ps -p " & (thePid as string) & " | awk '{print $1}' | grep " & (thePid as string) if the_result is equal to (thePid as string) then return true else return false end if on error errorMessage number errorNumber return false end try end IsExistsProcess -- =========================================== -- CheckingBackgroundCpuProcess -- プロセスの存在確認 -- 入力: theList 存在を確認するデータ配列(ProcessData配列) -- 出力: なし -- 返り値: 存在を確認したデータ配列(ProcessData配列) on CheckingBackgroundCpuProcess(theList) set retList to {} repeat with v in theList if IsExistsProcess(pid of v) of me is equal to false then syslog("detect CPUSlowControlProcess down. pid=" & (pid of v as string) & " origin=" & (procName of v)) of me else set end of retList to v end if end repeat return retList end CheckingBackgroundCpuProcess on idle try tell application "System Events" set activeProcs to every process whose frontmost is true set activeProc to item 1 of activeProcs tell activeProc set activeProcName to name end tell end tell if process_monitor_counter = process_monitor_interval then set process_monitor_counter to 0 set myProcessDataList to CheckingBackgroundCpuProcess(myProcessDataList) else set process_monitor_counter to process_monitor_counter + 1 end if if activeProcName is equal to "Safari" then set targetProcess to {} set targetProcess to FindProcessDataList(myProcessDataList, "WebProcess", targetProcess) if length of targetProcess is not equal to 0 then StopBackgroundCpuControl(targetProcess) set myProcessDataList to DeleteProcessDataList(myProcessDataList, "WebProcess") end if else if activeProcName is equal to "Firefox" then set targetProcess to {} set targetProcess to FindProcessDataList(myProcessDataList, "Firefox", targetProcess) if length of targetProcess is not equal to 0 then StopBackgroundCpuControl(targetProcess) set myProcessDataList to DeleteProcessDataList(myProcessDataList, "Firefox") end if else set targetProcess to {} set targetProcess to FindProcessDataList(myProcessDataList, "WebProcess", targetProcess) if length of targetProcess is equal to 0 then -- 制御中は、再実行しない StartBackgroundCpuControl("WebProcess", myProcessDataList) end if set targetProcess to {} set targetProcess to FindProcessDataList(myProcessDataList, "Firefox", targetProcess) if length of targetProcess is equal to 0 then -- 制御中は、再実行しない StartBackgroundCpuControl("Firefox", myProcessDataList) end if end if return 1 on error errorMessage number errorNumber syslog("onIdle errorMessage: " & errorMessage & ", errorNumber: " & errorNumber) of me error end try end idle on quit set targetProcess to {} set targetProcess to FindProcessDataList(myProcessDataList, "WebProcess", targetProcess) set targetProcess to FindProcessDataList(myProcessDataList, "Firefox", targetProcess) if length of targetProcess is not equal to 0 then StopBackgroundCpuControl(targetProcess) set myProcessDataList to {} end if syslog("appnap-browser closed.") of me continue quit end quit on syslog(logmsg) do shell script "logger -t appnap-browser -i " & quoted form of logmsg end syslog
終了方法
起動するとdockにappnap-browserアイコンが存在しています。
appnap-browserアイコンを選択後、
メニューから終了を選んでください。
(appnap-browser -> appnap-browserを終了)