インテル® プロセッサー・ベースのプラットフォームにおけるサードパーティー Android* アプリケーションのデバッグ・リファレンス – パート 1
この記事は、インテル® デベロッパー・ゾーンに掲載されている「Third-Party Android* Application Debug Reference on Intel® Processor-based Platforms Part 1」の日本語参考訳です。
目次
その他のデバッグの手法: MethodTracing を使用する
HProf (ヒープ・プロファイル) を使用する
SamplingProfile を使用する
System Signal を使用する
Logcat を使用する
jdwp (Java* Debug Wire Protocol) を使用する
android.os.Debug を使用する
ターゲットデバイス側:
ホスト PC 側:
gdb のマルチスレッド・デバッグ・コマンド
デバッグ例: system_server プロセスのサービスをデバッグ
デバッグ例: ネイティブ・ライブラリーを含む Android* アプリケーションをデバッグ
Android* コア・ダンプ・ファイルの解析
Eclipse* のトラブルシューティング
はじめに
アプリケーションの開発は、インテル® プロセッサー・ベースのモバイル・プラットフォームを成功へと導く上で重要です。インテル® プラットフォームでソースコードのないサードパーティー ISV (Google* など) のアプリケーションをできるだけ多く動作させたいと考えるプラットフォーム・エンジニアやアプリケーション・エンジニアにとって、これらのアプリケーションをデバッグするのは大きな課題です。
この記事では、インテル® プロセッサー・ベースのプラットフォームでソースコードのないサードパーティー・アプリケーションをデバッグする手法やツールの使用方法について説明します。
デバッグのヒント
コールスタック
-
説明:
コールスタックはソースコードでバグが発生している場所を知らせてくれるため、デバッグには重要です。Java* 空間のコールスタックとネイティブ空間のコールスタックでは出力する方法が異なります。 -
Java* 空間のコールスタックを出力:
-
デバッグしているプログラムをブレークしない手法
Import android.util.Log; void printJavaCallStack() { java.util.Map<Thread, StackTraceElement[]> ts = Thread.getAllStackTraces(); StackTraceElement[] ste = ts.get(Thread.currentThread()); for (StackTraceElement s : ste) { Log.d("zwang",s.toString()); } }
-
プログラムをブレークする手法 (非推奨)
new RuntimeException("stack").printStackTrace();
-
-
ネイティブ空間のコールスタックを出力:
-
デバッグしているプログラムをブレークしない手法
include <utils callstack="" h=""> using namespace android; namespace android { void get_backtrace() { CallStack stack; stack.update(); stack.dump(""); } };
-
プログラムをブレークする手法 (必要な場合以外は非推奨)
int* p = NULL; *p = 0x8888;
-
-
ネイティブ空間から Java* 空間のスタックを出力:
-
パッチ 0001-Dalvik-add-support-of-print-Java-Stack-from-Native-s.patch を Dalvik* プロジェクトに適用します。
-
Dalvik* プロジェクトを make して、libdvm.so をデバイスの /system/lib に転送します。
-
再起動後、Dalvik* のインターフェイスを使用してプロセスのネイティブ空間から Java* 空間のスタックを /sdcard/logs/ javastack ファイルにダンプします。2 つの方法があります。
シェルコマンドから:
kill -31 <pid>
API インターフェイスから:
ネイティブ空間から Java* 空間のスタックをダンプするソースコードの場所に “kill(getpid(),31);” を追加します。
次に例を示します。
<JB>/frameworks/native/libs/binder/IServiceManager.cpp virtual sp<IBinder> getService(const String16& name) const { kill(getpid(),31); … }
デバイスの /sdcard/logs/ javastack の Java* スタックを確認します。ネイティブ空間から Java* 空間へのすべてのコールスタックを確認することで、呼び出された Java* 関数とネイティブ・ライブラリーが分かります。
root@android:/sdcard/logs # cat javastack ----- pid 25653 at 1982-01-01 02:15:14 ----- Cmd line: com.android.providers.calendar DALVIK THREADS: (mutexes: tll=0 tsl=0 tscl=0 ghl=0) "main" prio=5 tid=1 NATIVE | group="main" sCount=0 dsCount=0 obj=0x417c2550 self=0x417b2af0 | sysTid=25653 nice=0 sched=0/0 cgrp=apps handle=1074057536 | schedstat=( 13633356 12645753 23 ) utm=0 stm=1 core=1 #00 pc 000b01ad /system/lib/libdvm.so #01 pc 000907ee /system/lib/libdvm.so #02 pc 00091ad4 /system/lib/libdvm.so #03 pc 0008a33d /system/lib/libdvm.so #04 pc 00000400 [vdso] at android.view.Display.init(Native Method) at android.view.Display.<init>(Display.java:57) at android.view.WindowManagerImpl.getDefaultDisplay(WindowManagerImpl.java:630) at android.app.ActivityThread.getDisplayMetricsLocked(ActivityThread.java:1530) at android.app.ActivityThread.applyConfigurationToResourcesLocked(ActivityThread.java:3649) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:3969) at android.app.ActivityThread.access$1300(ActivityThread.java:130) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1255) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method)
-
system/core プロジェクトのパッチ 0001-systemcore-add-Dalvik-Tombstone-call-stack-support.patch はオプションで、/sdcard/logs/ javastack に Java* スタックを出力するコマンドが追加されています。
0001-Dalvik-add-support-of-print-Java-Stack-from-Native-s.patch 0001-systemcore-add-Dalvik-Tombstone-call-stack-support.patch
-
ログツール
logcat – Android* ログ・メッセージ・アプリケーション
-
プロセス・インターフェイス:
cat /proc/kmsg – kernel debug message /proc/cpuinfo /proc/meminfo /proc/iomem /proc/vmallocinfo /proc/interrupts /proc/sys/vm/drop_caches
-
情報ダンプ:
procrank: process memory rank procmem: a specific process’ memory showslab: kernel slab utilization, /proc/slabinfo latencytop: CONFIG_LATENCYTOP showmap: process memory mmap address space; /proc/XXX/maps dumpstate- system information like memory , cpu etc dumpsys – system service information etc to see all of the "subcommands" of dumpsys do: dumpsys | grep DUMP DUMP OF SERVICE SurfaceFlinger: DUMP OF SERVICE accessibility: DUMP OF SERVICE account: DUMP OF SERVICE activity: DUMP OF SERVICE alarm: DUMP OF SERVICE appwidget: DUMP OF SERVICE audio: DUMP OF SERVICE backup: DUMP OF SERVICE battery: DUMP OF SERVICE batteryinfo: DUMP OF SERVICE clipboard: DUMP OF SERVICE connectivity: DUMP OF SERVICE content: DUMP OF SERVICE cpuinfo: DUMP OF SERVICE device_policy: DUMP OF SERVICE devicestoragemonitor: DUMP OF SERVICE diskstats: DUMP OF SERVICE dropbox: DUMP OF SERVICE entropy: DUMP OF SERVICE hardware: DUMP OF SERVICE input_method: DUMP OF SERVICE iphonesubinfo: DUMP OF SERVICE isms: DUMP OF SERVICE location: DUMP OF SERVICE media.audio_flinger: DUMP OF SERVICE media.audio_policy: DUMP OF SERVICE media.player: DUMP OF SERVICE meminfo: DUMP OF SERVICE mount: DUMP OF SERVICE netstat: DUMP OF SERVICE network_management: DUMP OF SERVICE notification: DUMP OF SERVICE package: Permission [android.permission.DUMP] (49f43060): perm=Permission{49fc39e0 android.permission.DUMP} android.permission.DUMP DUMP OF SERVICE permission: DUMP OF SERVICE phone: DUMP OF SERVICE power: DUMP OF SERVICE reboot: DUMP OF SERVICE screenshot: DUMP OF SERVICE search: DUMP OF SERVICE sensor: DUMP OF SERVICE simphonebook: DUMP OF SERVICE statusbar: DUMP OF SERVICE telephony.registry: DUMP OF SERVICE throttle: DUMP OF SERVICE usagestats: DUMP OF SERVICE vibrator: DUMP OF SERVICE wallpaper: DUMP OF SERVICE wifi: DUMP OF SERVICE window: dumptcp – tcp/ip information bugreport
ウェイクロック
-
説明:
ロックされたウェイクロックは、その種類に応じて、システムがサスペンドしたり、ほかの低電力ステートに移行しないようにします。ウェイクロックを作成する際、ウェイクロックの種類を設定できます。WAKE_LOCK_SUSPEND に設定すると、ウェイクロックはシステム全体をサスペンドしません。WAKE_LOCK_IDLE の場合、ウェイクロックが解放されるまで、アイドルステートから (割り込みレイテインシーの長い、または割り込みセットが無効になる) 低電力ステートに移行しません。この記事では、種類が指定されていないウェイクロックは WAKE_LOCK_SUSPEND に設定されていると仮定しています。ウェイクロックをロックするときにサスペンド処理がすでに開始されている場合、suspend_late ステージに達していない限り、サスペンド処理はアボートされます。これは、割り込みハンドラーやフリーズ可能なスレッドからのウェイクロックは常に動作することを意味しますが、suspend_late ハンドラーからウェイクロックをロックする場合は、ハンドラーからエラーを返してサスペンドをアボートする必要があります。
-
デバッグ手法:
ウェイクロックのステータスを確認するには、cat /proc/wakelocks コマンドを使用します。
name – ウェイクロックを保持するコンポーネント
wake_count – 保持するウェイクロックの数
active_since – 最後にウェイクロックを保持した時間からの期間 -
ツール:
CPUSpy.apk – デバイスのディープスリープ時間を取得し、ディープスリープに移行する際に問題が発生していないかを確認するアプリケーション。
get_activewakelock.sh – /proc/wakelocks から name および active_since の情報を取得するスクリプト。
CPUSpy.apk および get_activewakelock.sh は次のようにアタッチされます:get_activewakelock.sh CPUSpy.apk
その他のデバッグの手法: MethodTracing を使用する
hotspot を特定しパフォーマンスを解析します。CPU の使用状況や関数呼び出しの回数などを確認することもできます。
トレースを行うには、次の操作を行います。
import android.os.Debug; …… android.os.Debug.startMethodTracing(“/data/tmp/test”); // create /data/tmp …… // the program to be trace here android.os.Debug.stopMethodTracing();
実行後に、トレースファイルが /data/tmp/test.trace に生成されます。
トレースファイルを PC ホストにコピーします。
$ adb pull /data/tmp/test.trace ./
Android* SDK のトレースツールを使用してトレースを解析します。
$ $ANDROID_SRC/out/host/linux-x86/bin/traceview test.trace $ANDROID_SRC/out/host/linux-x86/bin/dmtracedump -g test.png test.trace
注:
デバッグバージョンの libdvm でビルドしたアプリケーションをトレースすると競合問題が発生します。トレース手法は非デバッグバージョンでビルドした場合にのみ使用してください。
HProf (ヒープ・プロファイル) を使用する
Java* メモリーを解析したり、Dalvik* のメモリー使用量やメモリーリークなどを表示します。
ヒープ・プロファイルを確認するには、次の操作を行います。
import android.os.Debug; import java.io.IOException; …… try { android.os.Debug.dumpHprofData(“/data/tmp/input.hprof”); // create /data/tmp } catch (IOException ioe) { }
hprof ファイルを PC ホストにコピーします。
$ adb pull /data/tmp/input.hprof ./
hprof-conv を使用して hprof を MAT ツール用フォーマットに変換します。
$ $ANDROID_SRC/out/host/linux-x86/bin/hprof-conv input.hprof output.hprof
MAT を使用して hprof ファイルを開き、結果を確認します。
MAT ツール: http://www.eclipse.org/mat/downloads.php
注:
このツールは、ネイティブ空間ではなく Java* 空間のメモリー使用量のみ表示します。
SamplingProfile を使用する
ルーチンをミリ秒間隔でサンプリングして、出力を記録します。
プロファイルをサンプリングするには、次の操作を行います。
import dalvik.system.SamplingProfiler …… SamplingProfile sp = SamplingProfiler.getInstance(); sp.start(n); // n is sample times sp.logSnapshot(sp.snapshot()); …… sp.shutDown(); //there will be a sample thread to output information in logcat
システムシグナルを使用する
システムシグナル SIGQUIT および SIGUSR1 を Dalvik* に送り、これらのシグナルを制御して (dalvik/vm/SignalCatcher.c) コールスタックまたはメモリー使用量を出力します。
システムシグナルを送ってコールスタックを取得するには、次の操作を行います。
-
$ chmod 777 /data/anr -R $ rm /data/anr/traces.txt $ ps # find pid $ kill -3 pid # send SIGQUIT to process to get tracefile $ cat /data/anr/traces.txt
-
$ chmod 777 /data/misc -R $ ps # find pid $ kill -10 pid # send SIGQUIT to process to get hproffile $ ls /data/misc/*.hprof
Logcat を使用する
Android* システムの aplog を取得します。
次の手順で aplog を追加したり、aplog を取得します。
-
android.util.Log は Java* 出力の println と IVD を使用します。
-
Dalvik* はパイプとスレッドを使用します。dup2 を使用して stdout と stderr をパイプにリダイレクトして (vm/StdioConverter.c:dvmstdioConverterStartup)、パイプを読むスレッドを開始し (dalvik/vm/StdioConverter.c:stdioconverterThreadStart())、ログツールを使用してログを (system/core/liblog/logd_write.c: __android_log_print()) /dev/log/* に出力します。
-
logcat ツールの引数は次のとおりです。
# logcat -b main //show main buffer # logcat -b radio //show radio buffer # logcat -b events //show event buffer
jdwp (Java* Debug Wire Protocol) を使用する
Java* Debug Wire Protocol (JDWP) は、デバッガーとデバッグする Java* 仮想マシン間の通信に使用されるプロトコルです。Android* システムでは、JDWP は adb と Android* デバイスの Java* アプリケーション間で使用されます。開発者は、さまざまなデバッグに JDWP を使利用できます。
詳細は、次のリンクを参照してください。
http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html
android.os.Debug を使用する
Android* のデバッグツール、android.os.Debug には多くのデバッグ API が用意されています。
詳細は、次のリンクを参照してください。
http://developer.android.com/reference/android/os/Debug.html
-
ナノレベルの時間の取得
threadCpuTimeNanos()
-
メモリー割り当ての取得
startAllocCounting() stopAllocCounting() getGlobalAllocCount() get…..
-
現在のプロセスにロードされているクラスの出力
getLoadedClassCount() printLoadedClasses() //it needs to open NDEBUG function
デバッグツール
強力なデバッグツールは、根本的な原因を迅速かつ容易に解決できるように開発者を支援します。ここでは、一般的な Android* デバッグツールを使用して根本的な原因を解決する方法を説明します。
GDB
ログの出力は Android* アプリケーションをデバッグする 1 つの方法ですが、十分な情報が得られず、使用も困難です。
gdb は、ソースコードの問題を単一ステップのデバッグで直接調査するときに便利です。ここでは、Android* プラットフォームで gdb ツールを使用する方法を説明します。
ターゲットデバイス側:
gdbserver : <port> –attach <PID>
ホスト PC 側:
- adb forward tcp:<ポート> tcp:<ポート>
- cd <コードのルート・ディレクトリー> コマンドを実行します (gdb が現在の作業パスのソースコードを見つけられます)。
- gdb <デバッグするプログラム> コマンドを実行します (プログラムは -g オプションを指定してコンパイルしておく必要があります)。
- プログラムのデバッグを開始します。
- 次の 2 つのコマンドを実行してライブラリー・パッチをセットアップします。
- #set solib-absolute-prefix <シンボルのパス> (パッチに ~ などの特殊記号が含めないように注意してください)
- #set solib-search-path <シンボルのライブラリーのパス>
ターゲット側の gdbserver に接続するには、<gdb> target remote :<ポート> を実行します。
デバッグシンボルを含むプログラム/ライブラリーに関する注意: Android* ビルドシステムはデフォルトで “-g” オプションを使用してデバッグシンボルを含むネイティブ・ライブラリーをビルドしますが、最後のビルドステージでデバッグシンボルを削除します。このため、デバッグシンボルを含むネイティブ・ライブラリーを使用するには、“out/target/product/symbols” ディレクトリーにあるライブラリーを使用する必要があります。
gdb マルチスレッド・デバッグ・コマンド
gdb ツールには、プロセス内の複数のスレッドをデバッグするコマンドも用意されています。
info threads – デバッグしているプログラムのすべてのスレッド情報を出力します。
thread <tid> – 指定した ID のスレッドへデバッグ対象を切り替えます。
break <ファイル名>:<行> – 指定したファイル名の指定した行にブレークポイントを設定します。このコマンドは、多くのスレッドを含む system_servers で有用です。
例えば、次のコマンドは system_servers プロセスの InputDispatcher スレッドにブレークポイントを設定します。
InputDispatcher.cpp:1280 でブレークして続行します。
ステップ単位でデバッグするには、デバッグする場所を画面でタッチします。gdb は InputDispatcher スレッドを停止します。
set scheduler-locking off|on|step – 複数のスレッドをデバッグする場合、同時に実行しているほかのスレッドも表示されます。現在のスレッドをデバッグするには、“step”/“continue” コマンドを使用します。“set scheduler-locking” を使用すると、(ほかのスレッドをロックして) 現在のデバッグスレッドのみ実行することができます。
off – スレッドをロックせず、すべてのスレッドを実行します (デフォルト)。
on – 現在のデバッグスレッドのみ実行します。
step – ステップ単位でデバッグを行います。“next” コマンドを使用した場合を除いて、現在のデバッグスレッドのみ実行します。
デバッグ例: system_server プロセスのサービスをデバッグ
ここでは、gdb ツールで system_server プロセスのサービススレッドをデバッグする方法を示します。
ターゲットデバイスで、次のコマンドを入力します。
adb shell ps | grep system_server (ex: system_server PID is 312) gdbserver :1234 –attach 312
ホスト PC で、次のように入力します。
adb forward tcp:1234 tcp: 1234 cd ~/ics gdb out/target/product/mfld_pr2/symbols/system/bin/app_process #set solib-absolute-prefix /home/zwang/ics/out/target/product/mfld_pr2/symbols #set solib-search-path /home/zwang/ics/out/target/product/mfld_pr2/symbols/system/lib #target remot :1234
gdb がライブラリーからシンボルをロードします。
# break InputDispatcher.cpp:1280 # continue
ステップ単位でデバッグするには、デバッグする場所を画面でタッチします。gdb は InputDispatcher スレッドを停止します。
デバッグ例: ネイティブ・ライブラリーを含む Android* アプリケーションをデバッグ
この例は、gdb ツールでネイティブ・ライブラリーを含む Android* アプリケーションをデバッグする方法を示します。
ターゲットデバイスで、次のコマンドを入力します。
adb shell ps | grep zwang.test.app ( ex: the app PID is 123) gdbserver :1234 –attach 123
ホスト PC で、次のように入力します。
adb forward tcp:1234 tcp: 1234 cd ~/ics gdb out/target/product/mfld_pr2/symbols/system/bin/app_process #set solib-absolute-prefix /home/zwang/ics/out/target/product/mfld_pr2/symbols #set solib-search-path /home/zwang/ics/out/target/product/mfld_pr2/symbols/system/lib:/home/zwang/app/obj/local/x86
ndk-build を使用してネイティブ・ライブラリーをビルドした場合、デバッグシンボルを含むライブラリーは obj ディレクトリーに置かれます (lib ディレクトリーにあるライブラリーにはデバッグシンボルは含まれていません)。このケースでは、パス /home/zwang/app/obj/local/x86 を gdb solib-search-path のライブラリー検索パスに追加する必要があります。
#target remote :1234 で gdbserver に接続します。
#break zwangjni_test_app.c:12 でブレークポイントを設定し、“No source file named zwangjni_test_app.c, Make breakpoint pending on future shared library load?” のようなメッセージが表示されたら Y を選択します。
#continue
ネイティブ・ライブラリーのブレークポイントで停止した後、ステップ単位でデバッグできます。
Android* コア・ダンプ・ファイルの解析
プログラム例外が発生すると、コア・ダンプ・ファイルが /mnt/sdcard/data/logs/crashlogxx/xxxx_xxx_xxx.core に作成されます。adb pull コマンドを実行して、コア・ダンプ・ファイルをホスト PC にコピーします。
コア・ダンプ・ファイルをロードするには、
gdb <ics>/out/target/product/mfld_pr2/symbols/system/bin/app_process xxxx_xxx_xxx.core コマンドを実行します。
シンボルパスを設定します。
#set solib-absolute-prefix /home/zwang/ics/out/target/product/mfld_pr2/symbols
#set solib-search-path /home/zwang/ics/out/target/product/mfld_pr2/symbols/system/lib
bt、frame、up、down、print などのコマンドを使って、プログラム例外が発生したときにコールスタックを確認します。
Eclipse* のトラブルシューティング
Eclipse* は Android* アプリケーション開発用の優れた統合開発環境ですが、使用中に不明なエラーが発生することがあります。この節では、一般的な問題と解決方法を説明します。
Eclipse のメインメニューから [Preferences (設定)] > [Android] を選択したときに “bad version number in .class file” エラーメッセージが表示された場合、Eclipse* 環境変数に正しくない Java* ランタイムのバージョンが設定されています。
[Help (ヘルプ)] > [About (バージョン情報)] > [Installation Details (インストール詳細)] を選択して Eclipse* 環境変数を確認し、修正してください。kprobe を使用する
kprobe は Linux* のカーネル・デバッグ・ツールで、開発者にカーネルレベルのデバッグログを提供します。
kprobe カーネルデバッグの使用方法
カーネルレベルのログを dmesg バッファープールに出力するには、次の操作を行います。
-
kprobes サンプルコードをカーネルモジュールのビルド用のインテルのドライバー・ディレクトリーにコピーします。
cd ~/aosp/hardware/intel/linux-2.6/drivers/misc cp –r /AOSP/hardware/intel/linux-2.6/samples/kprobes
-
次の行を入力して kprobe サンプル・カーネル・モジュールをビルドするように makefile を変更します。
wang@~/r4_1_stable/hardware/intel/linux-2.6/drivers/misc >git diff diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 166a42e..6ef0f1d 100755 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -3,6 +3,7 @@ # intel_fabric_logging-objs := intel_fw_logging.o intel_fabricerr_status.o +obj-m += kprobes/ obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o diff --git a/samples/kprobes/Makefile b/samples/kprobes/Makefile index 68739bc..8f253fc 100644 --- a/samples/kprobes/Makefile +++ b/samples/kprobes/Makefile @@ -1,5 +1,8 @@ # builds the kprobes example kernel modules; # then to use one (as root): insmod <module_name.ko> +CONFIG_SAMPLE_KPROBES=m +CONFIG_SAMPLE_KRETPROBES=m + obj-$(CONFIG_SAMPLE_KPROBES) += kprobe_example.o jprobe_example.o obj-$(CONFIG_SAMPLE_KRETPROBES) += kretprobe_example.o
-
bootimage を make して kprobe サンプル・カーネル・モジュールをビルドします。
out/target/product/mfld_pr2/kernel_build/drivers/misc/kprobes/kretprobe_example.ko out/target/product/mfld_pr2/kernel_build/drivers/misc/kprobes/kprobe_example.ko out/target/product/mfld_pr2/kernel_build/drivers/misc/kprobes/jprobe_example.ko
-
boot.bin および system.img を含むイメージを再フラッシュして、boot.bin と kprobe モジュールで構成されるマジックナンバーを作成します。その他の場合、kprobe モジュールをカーネルに挿入できません。
-
/proc/kmsg の kprobe カーネルメッセージを確認するには、insmod kprobe_example.ko と入力します。
Part 2 を合わせて参照ください。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。