summaryrefslogtreecommitdiff
path: root/doc/android_application_profiling.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/android_application_profiling.md')
-rw-r--r--doc/android_application_profiling.md272
1 files changed, 272 insertions, 0 deletions
diff --git a/doc/android_application_profiling.md b/doc/android_application_profiling.md
new file mode 100644
index 0000000..fae33df
--- /dev/null
+++ b/doc/android_application_profiling.md
@@ -0,0 +1,272 @@
+# Android application profiling
+
+This section shows how to profile an Android application.
+Some examples are [Here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/demo/README.md).
+
+Profiling an Android application involves three steps:
+1. Prepare an Android application.
+2. Record profiling data.
+3. Report profiling data.
+
+
+## Table of Contents
+
+- [Prepare an Android application](#prepare-an-android-application)
+- [Record and report profiling data](#record-and-report-profiling-data)
+- [Record and report call graph](#record-and-report-call-graph)
+- [Report in html interface](#report-in-html-interface)
+- [Show flamegraph](#show-flamegraph)
+- [Record both on CPU time and off CPU time](#record-both-on-cpu-time-and-off-cpu-time)
+- [Profile from launch](#profile-from-launch)
+- [Parse profiling data manually](#parse-profiling-data-manually)
+
+
+## Prepare an Android application
+
+Based on the profiling situation, we may need to customize the build script to generate an apk file
+specifically for profiling. Below are some suggestions.
+
+1. If you want to profile a debug build of an application:
+
+For the debug build type, Android studio sets android::debuggable="true" in AndroidManifest.xml,
+enables JNI checks and may not optimize C/C++ code. It can be profiled by simpleperf without any
+change.
+
+2. If you want to profile a release build of an application:
+
+For the release build type, Android studio sets android::debuggable="false" in AndroidManifest.xml,
+disables JNI checks and optimizes C/C++ code. However, security restrictions mean that only apps
+with android::debuggable set to true can be profiled. So simpleperf can only profile a release
+build under these two circumstances:
+If you are on a rooted device, you can profile any app.
+
+If you are on Android >= O, we can use [wrap.sh](https://developer.android.com/ndk/guides/wrap-script.html)
+to profile a release build:
+Step 1: Add android::debuggable="true" in AndroidManifest.xml to enable profiling.
+```
+<manifest ...>
+ <application android::debuggable="true" ...>
+```
+
+Step 2: Add wrap.sh in lib/`arch` directories. wrap.sh runs the app without passing any debug flags
+to ART, so the app runs as a release app. wrap.sh can be done by adding the script below in
+app/build.gradle.
+```
+android {
+ buildTypes {
+ release {
+ sourceSets {
+ release {
+ resources {
+ srcDir {
+ "wrap_sh_lib_dir"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+task createWrapShLibDir
+ for (String abi : ["armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64"]) {
+ def dir = new File("app/wrap_sh_lib_dir/lib/" + abi)
+ dir.mkdirs()
+ def wrapFile = new File(dir, "wrap.sh")
+ wrapFile.withWriter { writer ->
+ writer.write('#!/system/bin/sh\n\$@\n')
+ }
+ }
+}
+```
+
+3. If you want to profile C/C++ code:
+
+Android studio strips symbol table and debug info of native libraries in the apk. So the profiling
+results may contain unknown symbols or broken callgraphs. To fix this, we can pass app_profiler.py
+a directory containing unstripped native libraries via the -lib option. Usually the directory can
+be the path of your Android Studio project.
+
+
+4. If you want to profile Java code:
+
+On Android >= P, simpleperf supports profiling Java code, no matter whether it is executed by
+the interpreter, or JITed, or compiled into native instructions. So you don't need to do anything.
+
+On Android O, simpleperf supports profiling Java code which is compiled into native instructions,
+and it also needs wrap.sh to use the compiled Java code. To compile Java code, we can pass
+app_profiler.py the --compile_java_code option.
+
+On Android N, simpleperf supports profiling Java code that is compiled into native instructions.
+To compile java code, we can pass app_profiler.py the --compile_java_code option.
+
+On Android <= M, simpleperf doesn't support profiling Java code.
+
+
+Below I use application [SimpleperfExampleWithNative](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/demo/SimpleperfExampleWithNative).
+It builds an app-profiling.apk for profiling.
+
+```sh
+$ git clone https://android.googlesource.com/platform/system/extras
+$ cd extras/simpleperf/demo
+# Open SimpleperfExamplesWithNative project with Android studio, and build this project
+# successfully, otherwise the `./gradlew` command below will fail.
+$ cd SimpleperfExampleWithNative
+
+# On windows, use "gradlew" instead.
+$ ./gradlew clean assemble
+$ adb install -r app/build/outputs/apk/profiling/app-profiling.apk
+```
+
+## Record and report profiling data
+
+We can use [app-profiler.py](scripts_reference.md#app_profilerpy) to profile Android applications.
+
+```sh
+# Cd to the directory of simpleperf scripts. Record perf.data.
+# -p option selects the profiled app using its package name.
+# --compile_java_code option compiles Java code into native instructions, which isn't needed on
+# Android >= P.
+# -a option selects the Activity to profile.
+# -lib option gives the directory to find debug native libraries.
+$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative --compile_java_code \
+ -a .MixActivity -lib path_of_SimpleperfExampleWithNative
+```
+
+This will collect profiling data in perf.data in the current directory, and related native
+binaries in binary_cache/.
+
+Normally we need to use the app when profiling, otherwise we may record no samples. But in this
+case, the MixActivity starts a busy thread. So we don't need to use the app while profiling.
+
+```sh
+# Report perf.data in stdio interface.
+$ python report.py
+Cmdline: /data/data/com.example.simpleperf.simpleperfexamplewithnative/simpleperf record ...
+Arch: arm64
+Event: task-clock:u (type 1, config 1)
+Samples: 10023
+Event count: 10023000000
+
+Overhead Command Pid Tid Shared Object Symbol
+27.04% BusyThread 5703 5729 /system/lib64/libart.so art::JniMethodStart(art::Thread*)
+25.87% BusyThread 5703 5729 /system/lib64/libc.so long StrToI<long, ...
+...
+```
+
+[report.py](scripts_reference.md#reportpy) reports profiling data in stdio interface. If there
+are a lot of unknown symbols in the report, check [here](README.md#how-to-solve-missing-symbols-in-report).
+
+```sh
+# Report perf.data in html interface.
+$ python report_html.py
+
+# Add source code and disassembly. Change the path of source_dirs if it not correct.
+$ python report_html.py --add_source_code --source_dirs path_of_SimpleperfExampleWithNative \
+ --add_disassembly
+```
+
+[report_html.py](scripts_reference.md#report_htmlpy) generates report in report.html, and pops up
+a browser tab to show it.
+
+## Record and report call graph
+
+We can record and report [call graphs](executable_commands_reference.md#record-call-graphs) as below.
+
+```sh
+# Record dwarf based call graphs: add "-g" in the -r option.
+$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative \
+ -r "-e task-clock:u -f 1000 --duration 10 -g" -lib path_of_SimpleperfExampleWithNative
+
+# Record stack frame based call graphs: add "--call-graph fp" in the -r option.
+$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative \
+ -r "-e task-clock:u -f 1000 --duration 10 --call-graph fp" \
+ -lib path_of_SimpleperfExampleWithNative
+
+# Report call graphs in stdio interface.
+$ python report.py -g
+
+# Report call graphs in python Tk interface.
+$ python report.py -g --gui
+
+# Report call graphs in html interface.
+$ python report_html.py
+
+# Report call graphs in flamegraphs.
+# On Windows, use inferno.bat instead of ./inferno.sh.
+$ ./inferno.sh -sc
+```
+
+## Report in html interface
+
+We can use [report_html.py](scripts_reference.md#report_htmlpy) to show profiling results in a web browser.
+report_html.py integrates chart statistics, sample table, flamegraphs, source code annotation
+and disassembly annotation. It is the recommended way to show reports.
+
+```sh
+$ python report_html.py
+```
+
+## Show flamegraph
+
+To show flamegraphs, we need to first record call graphs. Flamegraphs are shown by
+report_html.py in the "Flamegraph" tab.
+We can also use [inferno](scripts_reference.md#inferno) to show flamegraphs directly.
+
+```sh
+# On Windows, use inferno.bat instead of ./inferno.sh.
+$ ./inferno.sh -sc
+```
+
+We can also build flamegraphs using https://github.com/brendangregg/FlameGraph.
+Please make sure you have perl installed.
+
+```sh
+$ git clone https://github.com/brendangregg/FlameGraph.git
+$ python report_sample.py --symfs binary_cache >out.perf
+$ FlameGraph/stackcollapse-perf.pl out.perf >out.folded
+$ FlameGraph/flamegraph.pl out.folded >a.svg
+```
+
+## Record both on CPU time and off CPU time
+
+We can [record both on CPU time and off CPU time](executable_commands_reference.md#record-both-on-cpu-time-and-off-cpu-time).
+
+First check if trace-offcpu feature is supported on the device.
+
+```sh
+$ python run_simpleperf_on_device.py list --show-features
+dwarf-based-call-graph
+trace-offcpu
+```
+
+If trace-offcpu is supported, it will be shown in the feature list. Then we can try it.
+
+```sh
+$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative -a .SleepActivity \
+ -r "-g -e task-clock:u -f 1000 --duration 10 --trace-offcpu" \
+ -lib path_of_SimpleperfExampleWithNative
+$ python report_html.py --add_disassembly --add_source_code \
+ --source_dirs path_of_SimpleperfExampleWithNative
+```
+
+## Profile from launch
+
+We can [profile from launch of an application](scripts_reference.md#profile-from-launch-of-an-application).
+
+```sh
+# Start simpleperf recording, then start the Activity to profile.
+$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative -a .MainActivity
+
+# We can also start the Activity on the device manually.
+# 1. Make sure the application isn't running or one of the recent apps.
+# 2. Start simpleperf recording.
+$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative
+# 3. Start the app manually on the device.
+```
+
+## Parse profiling data manually
+
+We can also write python scripts to parse profiling data manually, by using
+[simpleperf_report_lib.py](scripts_reference.md#simpleperf_report_libpy). Examples are report_sample.py,
+report_html.py.