08月30日
AppNapライクにSafari,Firefoxをコントロール(cputhrottle & applescript)v3.0
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
これ以外に、いくつか事象・現象が発生していました。
コマンド位置の変更と、cputhrottleの存在確認の修正を合わせて行いました。
cputhrottleは、こちら(AppNapライクにWebProcessをコントロール(cputhrottle & applescript))を参考にしてください。
以下ソースをAppleScriptエディタにコピペします。
コピペ後、以下設定を書き換えてください。
ファイルフォーマット:アプリケーション、オプションとして「ハンドラの実行時に終了しない」を選んで、書き出します。
(ここではファイル名:appnap-browser.appを想定しています)
起動するとdockにappnap-browserアイコンが存在しています。
appnap-browserアイコンを選択後、
メニューから終了を選んでください。
(appnap-browser -> appnap-browserを終了)
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を終了)
コメントシステムを利用したくない方はお問い合わせからお願いします。
2013.8.19 DISQUS(外部コメントサービス)の利用を開始しました。
Facebook, google, Twitter等のアカウントで投稿可能です。