Search Unity

BacktraceでANRの検出

  • Backtrace
  • 新製品
  • 開発者向け

<このページで学べる内容>

Backtraceは開発中及びリリース後のゲームやアプリのフリーズ、クラッシュ、エラーなどの様々なエラーを検出してそのデータを収集することができます。このページではスマートフォンにおけるANR(Application Not Responding) をBacktraceで検出する様子を紹介します。

使用環境を以下に示します。

  • Unity 2020.3.19f1
  • backtrace-unity ライブラリ 3.6.1
  • Android実機 (筆者はPixel 4 XLを使用)

Backtraceとは?

Backtraceに関する概要は前回の記事をご参照ください。

https://forpro.unity3d.jp/unity_pro_tips/2021/10/01/2603/

記事を読んでいただくと分かりますが、Backtraceは様々なプラットフォームにおいてクラッシュログやエラーログなど様々なエラー情報を検出することができるツールと言うのが分かると思います。

今回はBacktrace 3.4からの新機能である「ANRの検出」をご紹介します。

ANRはApplication Not Respondingの略で、アプリケーションが長時間無反応になった状態を示す言葉です。これはスマートフォンアプリにおいてエラーやクラッシュと同様に不具合として挙げられる問題の一つです。しかも、症状としてアプリが無反応になるだけのため、そこから原因を探るのは非常に困難な点も厄介なところです。

BacktraceはANRを検出したら、速やかにANRの原因と思われる情報を検出し記録することができます。これによりANRの原因の特定が容易になります。

このページではBacktraceを利用するための環境構築が終わっていることを前提に、実際にANRを発生させるアプリケーションを用いて、BacktraceでANR情報を検出するための設定方法の紹介およびその時のログの内容をご紹介します。

ANRとは?

改めてANRにつて説明します。ANR(Application Not Responding)とはスマートフォンのアプリケーションのUIスレッドが長時間ブロックされる症状に対する名称です。

詳しい説明はAndroidディベロッパー向けドキュメントをご参照ください。

https://developer.android.com/topic/performance/vitals/anr?hl=ja

https://developer.android.com/training/articles/perf-anr?hl=ja

ここでポイントとなるのは以下の2点です。

  • 入力イベント(キーの押下や画面タッチなどのイベント)に対する応答が 5 秒以内にない。
  • BroadcastReceiver の実行が 10 秒以内に終了しない。

まず、一般的なAndroidアプリケーションにおいてUIスレッドと言うのはメインスレッドのことを示しています。これはAndroid SDKのフレームワークにおける仕様で、UIの更新はメインスレッドで行うこととなっています。そのため比較的短時間で終わる処理はメインスレッドで行い、ネットワークアクセスやファイルの入出力のように長時間かかる処理は別スレッドで行い、処理が終わったらメインスレッドで処理結果を元にUIを更新するということをしています。

しかし、UnityではこのAndroidのメインスレッド(=UIスレッド)をUnityのアプリケーションでは使用せず、別スレッドで描画処理を行っています。

例えばUnityにおいて以下の様なコードを書いたとしても、Unityが表示している画面更新は止まりますがANRは発生しません。

void Update()
{
    while(true) {}
}

つまり、UnityでANRが発生するケースと言うのは、Androidネイティブコード(JavaやKotlin、C言語など)が何らかの理由によりANRになる可能性が非常に高いと言えます。逆に言うとUnity側のコードに問題がなくても外部のネイティブライブラリ内で問題が発生する可能性もあり、ANRが発生した場合は原因を探るのは非常に困難であるともいえます。

しかし、Backtraceを利用するとANRが発生したと思われる個所を記録し報告するため、デバッグ困難なANRの原因究明に一歩近づけると思います。

Backtrace側の準備

それではBacktraceにログインしてください。

https://<<アカウント作成時に指定したサブドメイン名>>.sp.backtrace.io/

今回は既存のプロジェクトは使用せずに新規に作成したプロジェクトを使用する方法を紹介します。

右上のユーザー名(この資料では「Z」となっています)をクリックして「プロジェクトの作成」を選びます。

するとプロジェクト名を入力する画面が表示されますので、何か適当なプロジェクト名を入力してください。この資料では「ANR3」とします(執筆時点ではプロジェクトの削除機能が上手く動かないため、資料作成のためにプロジェクトいくつか作ったため後ろに数字が増えています)。 

問題がなければ「成功!」と表示されます。そうしたら「始める」ボタンをクリックします。

するとプロジェクトの設定画面の「エラー送信」>「送信トークン」画面に遷移します。ここで右上の「+」ボタンをクリックして送信トークンを作成します。送信トークンは説明文にも書かれていますが、エラーレポートの認証に使用され、これをアプリケーションに統合することでエラー送信が可能になります。

問題がなければ「最初のトークンが作成されました」ダイアログが表示されます。

このダイアログを閉じると図のようにトークンが表示されます。そうしたらダイアログに書かれていたように「インテグレーションドキュメント」に進みます。

インテグレーションドキュメントは左側のメニューにある「インテグレーションガイド」>「Unity」にあります。

ここから先は前回ご紹介した内容とほとんど同じであるためポイントだけ紹介します。

  • Unityで使用する情報
    • 1.のBacktraceライブラリ
      • Name:Backtrace
      • URL:https://package.openupm.com
      • Scope(s):io.backtrace.unity
    • 2.のサーバーアドレス

「3.設定を完了する」ですが、入力フィールドの詳細について知りたい場合に参照してください。

https://support.backtrace.io/hc/en-us/articles/360040515991-Unity-Integration-Guide

「4.例外を送信する」ですが、前回例外発生のためのサンプルプログラムとして利用しましたが、今回はANRを発生させるプロジェクトを作成するため、ここでは使用しません。

Unity側の設定

前半部分は前回ご紹介した内容と同じためポイントだけ紹介します。

  1. Projectビューにて、マウス右クリックをして、[Backtrace]-[Configuration]を実行しBacktraceコンフィグレーションファイルを作成します。
  2. コンフィグレーションファイルの「Server Address」にインテグレーションドキュメントの「2.のサーバーアドレス」を設定
  3. 空のゲームオブジェクトを作成して名前を「Backtrace」に変更し、「BacktraceClient」スクリプトをアタッチします。
  4. Backtrace ClientスクリプトのBacktrace Configrationフィールドに、BacktraceConfigrationファイルを設定します。

最後にBacktraceのANRの検出設定をします。ANR検出の初期設定は無効となっていますので、有効化する必要があります。

プロジェクトビューのBacktraceコンフィグレーションファイルをクリックするとインスペクターに詳細情報が表示されます。下の方にある「Backtrace database configuration」のEnable Databaseをチェックしてください。

すると、さらに下Backtrace Database Pathの入力欄が表示されます。

そこ入力欄に以下の値を入力してください。

${Application.persistentDataPath}/.backtraceio/

これはアプリケーション専用のデータ領域(${Application.persistentDataPath})の下にディレクトリを作成してそこに様々な障害レポートを一時保存する場所を指定しています。

次にANRの設定を確認します。

  • Native crashes
    • Capture native crashesをチェック
    • Capture ANR(application not responding)をチェック
  • 「Create database directory」をチェック

Native crashesの2つのチェックはANR検出のための必須項目です。Create database directoryのチェックはデータベースパスに含まれるディレクトリを作成するために指定します(これをチェックし忘れるとデータベースが保存できずに通知もされない)。

ANR発生プログラムについて

ANRを発生させるコードはJavaで記述します。

Unity 2018.2からJavaのソースコードを配置することができます。ドキュメントによると、任意の場所に配置できるとのことです。

https://docs.unity3d.com/ja/2018.4/Manual/AndroidJavaSourcePlugins.html

拡張子JavaのファイルをUnity Editorで作成するのは難しいため、適当なテキストエディタで以下のファイルを作成し、Unityのプロジェクトビューにドラッグアンドドロップして追加してください。

HelloAndroid.java

package jp.zabaglione.anrsample;

import android.content.Context;

import com.unity3d.player.UnityPlayer;
import android.widget.Toast;

public class HelloAndroid {

    private Context mContext;

    HelloAndroid() {
        mContext = UnityPlayer.currentActivity;
    }

    public void StartANR() {
        Toast.makeText(mContext, "Start ANR...", Toast.LENGTH_LONG).show();
        UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                }
            }
        });
    }
}

さらに上記Javaを呼び出すC#のコードはこちらです。

ANR.cs

using UnityEngine;
using System.Threading;

public class ANR : MonoBehaviour
{
    public void OnClick()
    {
        var helloAndroid = new AndroidJavaObject("jp.zabaglione.anrsample.HelloAndroid");
        helloAndroid.Call("StartANR");
    }
}

これはUnity UIのボタンをクリックしたときに呼び出されることを想定して作成しています。ですので、適当にUnity UIでボタンを作成して、OnClick()イベントで呼び出されるように設定してください。

Java側のコードですが、StartANR()が呼び出された後にトーストを表示します。これによりネイティブコードが呼び出されていることが視覚的に分かると思います。

次のUnityPlayer.currentActivity.runOnUiThread(…)は、AndroidアプリケーションにおけるUIスレッドで実行して欲しいメソッドを登録しています。このように書くことで、UIスレッド(=メインスレッド)以外で時間のかかる処理を行った後に、画面の更新などの処理をUIスレッド(=メインスレッド)で行うことができます。恐らくAndroidアプリケーション開発者ではおなじみのコードだと思います。

今回はUIスレッドで無限ループを実行させているため、そこで処理が止まってしまいます。ただ、Unity側のスレッドはメインスレッドに処理を依頼しただけなのでUnity側は止まらずそのまま処理は進みます。しかし、もう一度呼び出されるとメインスレッドが無限ループで止まっているため、そこで初めてメインスレッドが止まっていることをAndroid OSが検出します。そして一定時間(10秒)経過するとANRダイアログを表示します。そのため2回呼び出す必要があります。

確認方法

これで準備が整いました。前述の通り、ANRを発生させるコードはメインスレッドで無限ループさせて、再度メインスレッドに処理要求をしたところから10秒経過すると発生します。

簡単に言うと、ボタンを2回押せばANRとなります。

それでは試していましょう。このプログラムはJavaのソースコードを含んでいるためAndroidの実機で動作せる必要があります。そのため開発者モードが有効化されたAndroid実機をPC繋げて、アプリをビルドして転送してください。この時、Package Nameを適当な物を設定しておいてください。図は参考です。

なおUnity側は動作し続けていることが分かるようにCubeを作成して、適当な速度で回させておくと良いと思います。そうするとUnity側は特に問題が無いのに急にANRダイアログが表示されることが分かると思います。

この動画では、「ANR」と言うボタンを2回押すことでANRが発生している様子を示している物です。ANR発生後、アプリを強制終了してください。

それではログの確認をしてみましょう。

ブラウザでご自分のBacktraceサブドメインを開き、作成したプロジェクト(左上で選択可能)を開いてください。図は何度か試したときの様子になっています。

詳細を見てみましょう。Fingerprintの部分をクリックします。

するとANRが発生したコールスタックが表示されています。

さらにアクションの「デバッガーで表示」をクリックしてみましょう。

これでさらに詳細な情報が出力されます。これでANR発生個所がほぼ特定できると思います。

おわりに

Backtraceは様々なエラーに関する情報を記録することができるツールですが、ANRのように原因の特定が難しい症状に対してもデバッグに必要な情報を取得することができる強力なツールです。特にネイティブライブラリを使用している場合は原因の特定が非常に困難なことも多いと思います。今回の機能を有効にすることで、仮にANRが発生しても発生原因の個所を特定できることが期待されます。

Backtraceにはまだまだ多数の機能がありますので、機会があればほかの機能についてもご紹介できればと思います。

この記事はいかがでしたか?