Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
MetricKitで予期せぬ終了を検知する話 / Detect unexpected term...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
nekowen
April 18, 2024
Programming
1.4k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
MetricKitで予期せぬ終了を検知する話 / Detect unexpected termination with MetricKit
Ebisu.mobile #5〜モバイルアプリの品質改善どうしてる?〜
https://hey.connpass.com/event/313395/
nekowen
April 18, 2024
More Decks by nekowen
See All by nekowen
AccessorySetupKitで実現するシームレスなペアリング体験 / Seamless pairing with AccessorySetupKit
nekowen
0
740
アプリ起動時間を80%高速化した話 / Talk about 80% faster app startup time.
nekowen
0
260
健康第一!MetricKitで始めるアプリの健康診断 / App Health Checkups Starting with MetricKit
nekowen
5
6.9k
5分で理解するAccessorySetupKit / Understanding AccessorySetupKit in 5 minutes
nekowen
0
700
SwiftUI/Jetpack Composeを採用してよかったこと悪かったこと
nekowen
2
1.5k
iOS13向けに位置情報周りを対応しようとしたら苦労した話
nekowen
1
560
Other Decks in Programming
See All in Programming
How I Won Prize Money at a Hackathon Using Codex and Symphony Alpha
yasei_no_otoko
0
130
信頼性の目標を誰も求めてない
shubox
0
450
VibeCodingからAgenticWorkflowへ
starfish719
0
1k
30年振りにコンパイラの定数整数除算を改善した
herumi
9
4.4k
仕様駆動開発の消費期限
watany
20
9.2k
Hello, Hiroshima Geospatial Data! — Exploring DoboX with Python
ra0kley
0
140
Hono + Inertia + React で LP を構築した話
oukayuka
2
190
Can LLMs Replicate 4 Years of Compose Migration? Exploring the boundaries of automation with 279 XML files from a real product
makun
0
110
Gradle-Plugin-in-2026.pdf
ryunen344
1
120
ソフトウェアラスタライザ
fadis
1
750
iOS開発×AI駆動開発 〜最近使って便利だったスキルの話〜
nogu66
0
120
Swift愛好会と私(ウホーイ) / Swift Fan Club and Uhooi
uhooi
0
100
Featured
See All Featured
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.5k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.4k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Exploring anti-patterns in Rails
aemeredith
3
480
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
830
Believing is Seeing
oripsolob
1
200
Paper Plane (Part 1)
katiecoart
PRO
1
10k
sira's awesome portfolio website redesign presentation
elsirapls
0
360
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
220
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
How to Think Like a Performance Engineer
csswizardry
28
2.8k
Transcript
STORES 株式会社 @nekowen Ebisu.mobile #5〜モバイルアプリの品質改善どうしてる?〜 2024年4月19日(金) 12:00 - 13:00 MetricKitで予期せぬ終了を
検知する話
自己紹介 Ryoya Kobitsu / @nekowen STORESレジ・予約開発グループ WWDC24に参加します(ドキドキ) X: @n3k0w3n GitHub:
@nekowen 2
3 ある日の出来事…
ある日の出来事… 4 QAチームから 「1日に数回アプリが突然クラッシュする」 という報告をもらう
5 何が起きていたか
何が起きていたか 6 - Crashlytics, TestFlightにレポートが上がっていなかった🤔 - システムによって強制終了された可能性 - 問題が起きた端末でJetsamイベントが記録されていた -
メモリの使いすぎでアプリが強制終了されていた - → 特定の条件でメモリを消費し続けるバグが見つかった
何が起きていたか 7 - watchdogに引っかかった - 無効なコード署名 - CPUへ負荷をかけオーバー ヒートした -
大量にメモリを使用して Jetsamイベントを引き起こ した 引用 https://developer.apple.com/documentation/xcode/acquiring-crash-reports-and-d iagnostic-logs#Locate-crash-reports-and-memory-logs-on-the-device
何が起きていたか 8 - watchdogに引っかかった - 無効なコード署名 - CPUへ負荷をかけオーバー ヒートした -
大量にメモリを使用して Jetsamイベントを引き起こ した 引用 https://developer.apple.com/documentation/xcode/acquiring-crash-reports-and-d iagnostic-logs#Locate-crash-reports-and-memory-logs-on-the-device
何が起きていたか 9 - Crashlytics, TestFlightにレポートが上がっていなかった🤔 - システムによって強制終了された可能性 - 問題が起きた端末でJetsamイベントが記録されていた -
メモリの使いすぎでアプリが強制終了されていた - → 特定の条件でメモリを消費し続けるバグが見つかった
何が起きていたか 10 - Crashlytics, TestFlightにレポートが上がっていなかった🤔 - システムによって強制終了された可能性 - 問題が起きた端末でJetsamイベントが記録されていた -
メモリの使いすぎでアプリが強制終了されていた - → 特定の条件でメモリを消費し続けるバグが見つかった🐛
何が起きていたか 11 - Crashlytics, TestFlightにレポートが上がっていなかった🤔 - システムによって強制終了された可能性 - 問題が起きた端末でJetsamイベントが記録されていた -
メモリの使いすぎでアプリが強制終了されていた - → 特定の条件でメモリを消費し続けるバグが見つかった 何らかの方法で検知できないか?🤔
12 MetricKitとは何か
MetricKitとは何か 13 - システムがキャプチャしたアプリの メトリクスや診断レポートをデバイ ス上で受け取れるフレームワーク - Xcode Organizerより詳細なレポー トが得られる
- iOS13から提供されている 引用 https://developer.apple.com/videos/play/wwdc 2020/10081
MetricKitとは何か 14 エネルギー効率 パフォーマンス 応答性 ディスクアクセス クラッシュレポート 応答性 ディスクアクセス メトリクス
診断データ
MetricKitとは何か - 全体で得られる情報 15 エネルギー効率 パフォーマンス 応答性 ディスクアクセス クラッシュレポート 応答性
ディスクアクセス メトリクス 診断データ アプリの起動時間 CPU使用率 メモリ使用量 位置情報取得に使った時間 ネットワーク通信量etc… クラッシュ全般 アプリの起動時間 アプリのハングetc…
MetricKitとは何か - 配信されるタイミング 16 エネルギー効率 パフォーマンス 応答性 ディスクアクセス クラッシュレポート 応答性
ディスクアクセス メトリクス 診断データ iOS15 / macOS12以降の場合 レポートがあれば即時配信される 1日に最大1回アプリに対し てレポートが配信される
MetricKitとは何か - メトリクス/パフォーマンスで得られる情報 17 エネルギー効率 パフォーマンス 応答性 ディスクアクセス クラッシュレポート 応答性
ディスクアクセス メトリクス 診断データ アプリ起動にかかった時間 アクティブ時間 アプリ終了回数・メモリ使用量
MetricKitとは何か - メトリクス/パフォーマンスで得られる情報 18 エネルギー効率 パフォーマンス 応答性 ディスクアクセス クラッシュレポート 応答性
ディスクアクセス メトリクス 診断データ アプリ起動にかかった時間 アクティブ時間 アプリ終了回数・メモリ使用量 回数の変化を追っていけば 異常が起きていることがわかりそう👀
19 MXAppExitMetricから アプリの終了回数を知る
MXAppExitMetricからアプリの終了回数を知る 20 - cumulativeNormalAppExitCount - cumulativeAbnormalExitCount - cumulativeAppWatchdogExitCount - cumulativeCPUResourceLimitExitCount
- cumulativeMemoryResourceLimitExitCount - cumulativeMemoryPressureExitCount - cumulativeSuspendedWithLockedFileExitCount - cumulativeBadAccessExitCount - cumulativeIllegalInstructionExitCount - cumulativeBackgroundTaskAssertionTimeoutExitCount
MXAppExitMetricからアプリの終了回数を知る 21 - cumulativeNormalAppExitCount - cumulativeAbnormalExitCount - cumulativeAppWatchdogExitCount - cumulativeCPUResourceLimitExitCount
- cumulativeMemoryResourceLimitExitCount - cumulativeMemoryPressureExitCount - cumulativeSuspendedWithLockedFileExitCount - cumulativeBadAccessExitCount - cumulativeIllegalInstructionExitCount - cumulativeBackgroundTaskAssertionTimeoutExitCount 正常・異常終了の累積数
MXAppExitMetricからアプリの終了回数を知る 22 - cumulativeNormalAppExitCount - cumulativeAbnormalExitCount - cumulativeAppWatchdogExitCount - cumulativeCPUResourceLimitExitCount
- cumulativeMemoryResourceLimitExitCount - cumulativeMemoryPressureExitCount - cumulativeSuspendedWithLockedFileExitCount - cumulativeBadAccessExitCount - cumulativeIllegalInstructionExitCount - cumulativeBackgroundTaskAssertionTimeoutExitCount システムによって 強制終了された累積数
MXAppExitMetricからアプリの終了回数を知る 23 - cumulativeNormalAppExitCount - cumulativeAbnormalExitCount - cumulativeAppWatchdogExitCount - cumulativeCPUResourceLimitExitCount
- cumulativeMemoryResourceLimitExitCount - cumulativeMemoryPressureExitCount - cumulativeSuspendedWithLockedFileExitCount - cumulativeBadAccessExitCount - cumulativeIllegalInstructionExitCount - cumulativeBackgroundTaskAssertionTimeoutExitCount クラッシュの累積数
MXAppExitMetricからアプリの終了回数を知る 24 - cumulativeNormalAppExitCount - cumulativeAbnormalExitCount - cumulativeAppWatchdogExitCount - cumulativeCPUResourceLimitExitCount
- cumulativeMemoryResourceLimitExitCount - cumulativeMemoryPressureExitCount - cumulativeSuspendedWithLockedFileExitCount - cumulativeBadAccessExitCount - cumulativeIllegalInstructionExitCount - cumulativeBackgroundTaskAssertionTimeoutExitCount BackgroundTaskのタイムアウトによって 終了させられた累積数
MXAppExitMetricからアプリの終了回数を知る 25 - cumulativeNormalAppExitCount - cumulativeAbnormalExitCount - cumulativeAppWatchdogExitCount - cumulativeCPUResourceLimitExitCount
- cumulativeMemoryResourceLimitExitCount - cumulativeMemoryPressureExitCount - cumulativeSuspendedWithLockedFileExitCount - cumulativeBadAccessExitCount - cumulativeIllegalInstructionExitCount - cumulativeBackgroundTaskAssertionTimeoutExitCount メモリに関するシステム終了の 累積カウント ※分析したい数値
26 実装してみよう
実装してみよう 27 class AppMetrics: NSObject { let shared = MXMetricManager.shared
func startReceiveReport() { shared.add(self) } func pauseReceiveReport() { shared.remove(self) } }
実装してみよう 28 class AppMetrics: NSObject { let shared = MXMetricManager.shared
func startReceiveReport() { shared.add(self) } func pauseReceiveReport() { shared.remove(self) } } MXMetricManagerの共有インスタンスに 通知先のオブジェクトをaddする
実装してみよう 29 class AppMetrics: NSObject { let shared = MXMetricManager.shared
func startReceiveReport() { shared.add(self) } func pauseReceiveReport() { shared.remove(self) } } 通知を止めたいときは removeメソッドを呼ぶ
実装してみよう 30 extension AppMetrics: MXMetricManagerSubscriber { func didReceive(_ payloads: [MXMetricPayload])
{ payloads.forEach { if let applicationExitMetrics = $0.applicationExitMetrics { // バックグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryResourceLimitExitCount) logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryPressureExitCount) // フォアグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.foregroundExitData.cumulativeMemoryResourceLimitExitCount) } // まるっとPayloadをサーバーに送りたい場合 sendPayloadToServer($0.jsonRepresentation()) } } func didReceive(_ payloads: [MXDiagnosticPayload]) { payloads.forEach { sendPayloadToServer($0.jsonRepresentation()) } } }
実装してみよう 31 extension AppMetrics: MXMetricManagerSubscriber { func didReceive(_ payloads: [MXMetricPayload])
{ payloads.forEach { if let applicationExitMetrics = $0.applicationExitMetrics { // バックグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryResourceLimitExitCount) logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryPressureExitCount) // フォアグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.foregroundExitData.cumulativeMemoryResourceLimitExitCount) } // まるっとPayloadをサーバーに送りたい場合 sendPayloadToServer($0.jsonRepresentation()) } } func didReceive(_ payloads: [MXDiagnosticPayload]) { payloads.forEach { sendPayloadToServer($0.jsonRepresentation()) } } }
実装してみよう 32 extension AppMetrics: MXMetricManagerSubscriber { func didReceive(_ payloads: [MXMetricPayload])
{ payloads.forEach { if let applicationExitMetrics = $0.applicationExitMetrics { // バックグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryResourceLimitExitCount) logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryPressureExitCount) // フォアグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.foregroundExitData.cumulativeMemoryResourceLimitExitCount) } // まるっとPayloadをサーバーに送りたいとき sendPayloadToServer($0.jsonRepresentation()) } } func didReceive(_ payloads: [MXDiagnosticPayload]) { payloads.forEach { sendPayloadToServer($0.jsonRepresentation()) } } } メトリクスを受け取るときは func didReceive([MXMetricPayload]) を定義する
実装してみよう 33 extension AppMetrics: MXMetricManagerSubscriber { func didReceive(_ payloads: [MXMetricPayload])
{ payloads.forEach { if let applicationExitMetrics = $0.applicationExitMetrics { // バックグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryResourceLimitExitCount) logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryPressureExitCount) // フォアグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.foregroundExitData.cumulativeMemoryResourceLimitExitCount) } // まるっとPayloadをサーバーに送りたいとき sendPayloadToServer($0.jsonRepresentation()) } } func didReceive(_ payloads: [MXDiagnosticPayload]) { payloads.forEach { sendPayloadToServer($0.jsonRepresentation()) } } } 診断データを受け取るときは func didReceive([MXDiagnosticPayload]) を定義する
実装してみよう 34 extension AppMetrics: MXMetricManagerSubscriber { func didReceive(_ payloads: [MXMetricPayload])
{ payloads.forEach { if let applicationExitMetrics = $0.applicationExitMetrics { // バックグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryResourceLimitExitCount) logger.info(applicationExitMetrics.backgroundExitData.cumulativeMemoryPressureExitCount) // フォアグラウンドでアプリが終了したとき logger.info(applicationExitMetrics.foregroundExitData.cumulativeMemoryResourceLimitExitCount) } // まるっとPayloadをサーバーに送りたいとき sendPayloadToServer($0.jsonRepresentation()) } } func didReceive(_ payloads: [MXDiagnosticPayload]) { payloads.forEach { sendPayloadToServer($0.jsonRepresentation()) } } } PayloadをJSONに変換するメソッドが用意されて いるので、まるっとサーバーに送る場合は使う
まとめ 35 - MetricKitを使うことでアプリの健康状態をレポートベースで 受け取れて独自に分析ができます - MXAppExitMetricを見ることで検知しづらいシステムによる 強制終了の詳細を数値で追える - 改善やっていきましょう!
36 ご清聴ありがとうございました