株式会社サムザップで SRE をしているれおすけです。サムザップ AdventCalendar 2022 の6日目の記事です。
昨日は @ohbashunsuke さんの【知らないと損する】デザイナー向け5つのUnityテクニックでした。
背景
スマートフォンでのアプリ開発をしていると何かと気になるのが端末温度です。
第四世代以降の iPhone/iPad/iPod touch は高温になりすぎた場合に本体を保護する機能が組み込まれており、温度が上限を超えると警告メッセージが表示されます。その状態になると、本体の電源を切って、端末温度が冷えるまで使うことができません。
もともと、カクカクした描写にならないように処理の負荷を考慮したり、パフォーマンスを計測することはやってきています。しかし、一つ一つは負荷のかからない処理でも続けて長く使用することで、端末の温度が上がってしまうケースはあると思います(私自身もスマートフォンでゲームをしていて、端末が熱くなったことが何回もあります。)
そこで、社内で課題として上がっていた iPhone の温度計測を MacOS が動くデスクトップやラップトップ(以降、PCと記載)から観測する方法を実現した話を書こうと思います。
※端末温度と記載していますが、正確にはデバイスのバッテリ温度を取得しています。
目標
JailBreak なしで iPhone の端末温度を記録して、分析、グラフ化するところまでをゴールとして設定しました。実行時に観測する間隔を設定できるようにすることと、分析、グラフ化しやすい形で記録することが必要になります。
作成したスクリプト
1 #!/usr/bin/env bash
2 ## for safety
3 set -u # undefined variable check
4 set -e # interupt at errors
5 set -o pipefail # interupt at pipe errors
6 ## function
7 usage () {
8 echo "Usage: $COMMAND [-i INTERVAL(seconds)]" 1>&2
9 }
10 summarize () {
11 FINISH_AT=`date +%s`
12 DIFFERENCE=$( echo "${FINISH_AT} - ${START_AT}" | bc -l )
13 echo 'Interrupted!'
14 echo 'start:' ${START_AT}
15 echo 'finish:' ${FINISH_AT}
16 echo 'diff:' $DIFFERENCE
17 cat "./output/${ID}"* | st --N --mean --median --max --min
18 }
19 ## options
20 COMMAND=`basename $0`
21 INTERVAL=5
22 while getopts i: OPT
23 do
24 case $OPT in
25 "i" ) INTERVAL="$OPTARG" ;;
26 * ) usage
27 exit 1 ;;
28 esac
29 done
30 ## logging
31 START_AT=`date +%s`
32 ## analyser
33 DEVICE_IDS=`idevice_id --list`
34 trap summarize SIGINT
35 while true; do
36 for ID in "${DEVICE_IDS[@]}"; do
37 idevicediagnostics -u ${ID} ioregentry AppleSmartBattery | plutil -p - | grep '"Temperature"' | awk '{print $3}'| tee -a "./output/"`date +"${ID}_%Y%m%d_%H%.txt"`
38 done
39 sleep $INTERVAL
40 done
実行結果
デバイスの温度を計測して、Terminal と ファイルへ出力しています
プログラムを停止すると動いていた時間(開始、終了、差分)とこれまでその端末で観測した値(これまでの実行全部)の分析(サンプル数、最小値、中央値、最大値、平均値)を出力します。
工夫した点
- プログラムをできるだけ書かずにコマンドを組み合わせて作り、応用物をどんどん作り出せるように簡易的に済ませました。分析は
st
コマンドで、グラフ描写はgnuplot
、iPhone からの情報取得はlibimobiledevice
というライブラリのコマンドidevicediagnostics
を用いました。 - 実行間隔をオプションで指定できるようにして、出力量を調整できるようにしました。コマンド実行して、スリープという形をとっているので、あくまでも実行間隔であり、N秒に1回計測という形ではないところに注意が必要です。私の環境では、0.4 を指定すると大体1秒に1回計測していました。
- 停止した時にも簡易的に統計情報を出力するように
SIGINT
を trap して実行してみました。フォアグラウンドプロセスとして動かすので、停止する時にはCtrl + C
で停止する必要があります。他のシグナルも trap することを考えたのですが、シンプルにSIGINT
のみにしました。 - 出力ファイル名に端末IDと日時(
YYYY年MM月DD日HH時
)を用いました。st
コマンドはファイルに記載された数値を読み取リます。この時ファイルは複数指定可能です。[DEVICE_ID]_YYYYMMDD_HH.txt
というファイル名に設定すれば、[DEVICE_ID]_YYYYMMDD_HH.txt
と指定すれば1時間単位の統計情報が簡単に表示することができます。
[DEVICE_ID]_YYYYMMDD_*
と指定すれば日単位、[DEVICE_ID]_YYYYMM*
と指定すれば月単位、[DEVICE_ID]_*
で指定すれば今までの実行全てでの統計になります。
st コマンドについて
簡単な統計を出力してくれます。実行速度が速く、MacOS には標準で perl
がインストールされているので手間なく導入できるため、採用しました。
使用しているのは、サンプル数、最小値、最大値、中央値、平均値ですが、他に標準偏差や各種四分位(quartile)、百分位(percentile)などが使用できます。
libimobiledevice について
iOS上の Service に対して Native Protocol を用いて、デバイスが管理している情報を取得することができます。
idevicediagnostics
コマンドで AppleSmartBattery
というI/O Registory のエントリーを読み込んでいます。idevicediagnostics
コマンドには端末の UUID をオプションで指定する必要があります。それは idevice_id --list
を実行して取得します。以下の説明では ${DEVICE_ID}
に UUID が設定されているとします。
idevicediagnostics -u ${DEVICE_ID} ioregentry AppleSmartBattery
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IORegistry</key>
<dict>
<key>AbsoluteCapacity</key>
<integer>1127</integer>
<key>AdapterDetails</key>
<dict>
<key>AdapterID</key>
<integer>0</integer>
<key>AdapterVoltage</key>
<integer>5000</integer>
<key>Current</key>
<integer>500</integer>
...(以下、省略)
plist形式では扱いづらいので、putil コマンドで書式を変換しています。
※なぜか json にうまく変換できなかったので、簡易的な表示用変換後に grep で抜き出し、awk で切り出しています。
idevicediagnostics -u ${DEVICE_ID} ioregentry AppleSmartBattery | plutil -p -
{
"IORegistry" => {
"AbsoluteCapacity" => 1212
"AdapterDetails" => {
I/O Registory の Entry を確認数方法が分からなかったので、私がどう調べたかを書きます。
MacOS用の IO Registory Explorer で該当する項目を探し、iOS でも使用できるかコマンドを実際に実行して確認しながら進めました。IO Registory Explorer は XCode の Additional Tools に含まれているので XCode とは別にインストールが必要になります。
※どこかに I/O Registory の Entry 一覧があれば教えてほしいです。
AppleSmartBattery の Property を確認し値を確認
まとめと感想
libimobiledevice
のおかげでコマンドだけで完結できたのがとても良かったなと思います。また、今後の課題として以下の点があり、改善していく予定です。
- 現在は出力のフォーマットが適当に値の記録だけおこなっているので、記録日時を記載して後からグラフ化した時にわかりやすいようにしたい
-
libimobiledevice
が Cross-Platform対応なので、Windows でも実行できるように WSLでの検証を進めたい - iPhone7以前が動作保証端末になるプロジェクトもあるかもしれないので対応したい
- putil で json 変換できず応用しづらいので、変換できなかった理由などを調べて改善したい
久しぶりに ユニケージ開発(UNIX系OSにおいてコマンドとシェルスクリプトでシステムを開発する手法)もどきでツールを作成しましたが、とても楽しかったです。また、iOS の IORegistory 周りを調べるのが初めてだったのでとても苦労しましたが、短時間で作成することができて良かったです。
今後も課題をサクッと解決するツールを作って提供し、プロジェクトや会社の課題を解決していきたいと思います!
サムザップ AdventCalendar 2022 の明日の記事はは @RyotoKitajimaさん担当です。