1# Android application profiling 2 3This section shows how to profile an Android application. 4Some examples are [Here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/demo/README.md). 5 6Profiling an Android application involves three steps: 71. Prepare an Android application. 82. Record profiling data. 93. Report profiling data. 10 11 12## Table of Contents 13 14- [Android application profiling](#android-application-profiling) 15 - [Table of Contents](#table-of-contents) 16 - [Prepare an Android application](#prepare-an-android-application) 17 - [Record and report profiling data](#record-and-report-profiling-data) 18 - [Record and report call graph](#record-and-report-call-graph) 19 - [Report in html interface](#report-in-html-interface) 20 - [Show flamegraph](#show-flamegraph) 21 - [Report in Android Studio](#report-in-android-studio) 22 - [Record both on CPU time and off CPU time](#record-both-on-cpu-time-and-off-cpu-time) 23 - [Profile from launch](#profile-from-launch) 24 - [Control recording in application code](#control-recording-in-application-code) 25 - [Parse profiling data manually](#parse-profiling-data-manually) 26 27 28## Prepare an Android application 29 30Based on the profiling situation, we may need to customize the build script to generate an apk file 31specifically for profiling. Below are some suggestions. 32 331. If you want to profile a debug build of an application: 34 35For the debug build type, Android studio sets android::debuggable="true" in AndroidManifest.xml, 36enables JNI checks and may not optimize C/C++ code. It can be profiled by simpleperf without any 37change. 38 392. If you want to profile a release build of an application: 40 41For the release build type, Android studio sets android::debuggable="false" in AndroidManifest.xml, 42disables JNI checks and optimizes C/C++ code. However, security restrictions mean that only apps 43with android::debuggable set to true can be profiled. So simpleperf can only profile a release 44build under these three circumstances: 45If you are on a rooted device, you can profile any app. 46 47If you are on Android >= Q, you can add profileableFromShell flag in AndroidManifest.xml, this makes 48a released app profileable by preinstalled profiling tools. In this case, simpleperf downloaded by 49adb will invoke simpleperf preinstalled in system image to profile the app. 50 51``` 52<manifest ...> 53 <application ...> 54 <profileable android:shell="true" /> 55 </application> 56</manifest> 57``` 58 59If you are on Android >= O, we can use [wrap.sh](https://developer.android.com/ndk/guides/wrap-script.html) 60to profile a release build: 61Step 1: Add android::debuggable="true" in AndroidManifest.xml to enable profiling. 62``` 63<manifest ...> 64 <application android::debuggable="true" ...> 65``` 66 67Step 2: Add wrap.sh in lib/`arch` directories. wrap.sh runs the app without passing any debug flags 68to ART, so the app runs as a release app. wrap.sh can be done by adding the script below in 69app/build.gradle. 70``` 71android { 72 buildTypes { 73 release { 74 sourceSets { 75 release { 76 resources { 77 srcDir { 78 "wrap_sh_lib_dir" 79 } 80 } 81 } 82 } 83 } 84 } 85} 86 87task createWrapShLibDir 88 for (String abi : ["armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64"]) { 89 def dir = new File("app/wrap_sh_lib_dir/lib/" + abi) 90 dir.mkdirs() 91 def wrapFile = new File(dir, "wrap.sh") 92 wrapFile.withWriter { writer -> 93 writer.write('#!/system/bin/sh\n\$@\n') 94 } 95 } 96} 97``` 98 993. If you want to profile C/C++ code: 100 101Android studio strips symbol table and debug info of native libraries in the apk. So the profiling 102results may contain unknown symbols or broken callgraphs. To fix this, we can pass app_profiler.py 103a directory containing unstripped native libraries via the -lib option. Usually the directory can 104be the path of your Android Studio project. 105 106 1074. If you want to profile Java code: 108 109On Android >= P, simpleperf supports profiling Java code, no matter whether it is executed by 110the interpreter, or JITed, or compiled into native instructions. So you don't need to do anything. 111 112On Android O, simpleperf supports profiling Java code which is compiled into native instructions, 113and it also needs wrap.sh to use the compiled Java code. To compile Java code, we can pass 114app_profiler.py the --compile_java_code option. 115 116On Android N, simpleperf supports profiling Java code that is compiled into native instructions. 117To compile java code, we can pass app_profiler.py the --compile_java_code option. 118 119On Android <= M, simpleperf doesn't support profiling Java code. 120 121 122Below I use application [SimpleperfExampleWithNative](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/demo/SimpleperfExampleWithNative). 123It builds an app-profiling.apk for profiling. 124 125```sh 126$ git clone https://android.googlesource.com/platform/system/extras 127$ cd extras/simpleperf/demo 128# Open SimpleperfExamplesWithNative project with Android studio, and build this project 129# successfully, otherwise the `./gradlew` command below will fail. 130$ cd SimpleperfExampleWithNative 131 132# On windows, use "gradlew" instead. 133$ ./gradlew clean assemble 134$ adb install -r app/build/outputs/apk/profiling/app-profiling.apk 135``` 136 137## Record and report profiling data 138 139We can use [app-profiler.py](scripts_reference.md#app_profilerpy) to profile Android applications. 140 141```sh 142# Cd to the directory of simpleperf scripts. Record perf.data. 143# -p option selects the profiled app using its package name. 144# --compile_java_code option compiles Java code into native instructions, which isn't needed on 145# Android >= P. 146# -a option selects the Activity to profile. 147# -lib option gives the directory to find debug native libraries. 148$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative --compile_java_code \ 149 -a .MixActivity -lib path_of_SimpleperfExampleWithNative 150``` 151 152This will collect profiling data in perf.data in the current directory, and related native 153binaries in binary_cache/. 154 155Normally we need to use the app when profiling, otherwise we may record no samples. But in this 156case, the MixActivity starts a busy thread. So we don't need to use the app while profiling. 157 158```sh 159# Report perf.data in stdio interface. 160$ python report.py 161Cmdline: /data/data/com.example.simpleperf.simpleperfexamplewithnative/simpleperf record ... 162Arch: arm64 163Event: task-clock:u (type 1, config 1) 164Samples: 10023 165Event count: 10023000000 166 167Overhead Command Pid Tid Shared Object Symbol 16827.04% BusyThread 5703 5729 /system/lib64/libart.so art::JniMethodStart(art::Thread*) 16925.87% BusyThread 5703 5729 /system/lib64/libc.so long StrToI<long, ... 170... 171``` 172 173[report.py](scripts_reference.md#reportpy) reports profiling data in stdio interface. If there 174are a lot of unknown symbols in the report, check [here](README.md#how-to-solve-missing-symbols-in-report). 175 176```sh 177# Report perf.data in html interface. 178$ python report_html.py 179 180# Add source code and disassembly. Change the path of source_dirs if it not correct. 181$ python report_html.py --add_source_code --source_dirs path_of_SimpleperfExampleWithNative \ 182 --add_disassembly 183``` 184 185[report_html.py](scripts_reference.md#report_htmlpy) generates report in report.html, and pops up 186a browser tab to show it. 187 188## Record and report call graph 189 190We can record and report [call graphs](executable_commands_reference.md#record-call-graphs) as below. 191 192```sh 193# Record dwarf based call graphs: add "-g" in the -r option. 194$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative \ 195 -r "-e task-clock:u -f 1000 --duration 10 -g" -lib path_of_SimpleperfExampleWithNative 196 197# Record stack frame based call graphs: add "--call-graph fp" in the -r option. 198$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative \ 199 -r "-e task-clock:u -f 1000 --duration 10 --call-graph fp" \ 200 -lib path_of_SimpleperfExampleWithNative 201 202# Report call graphs in stdio interface. 203$ python report.py -g 204 205# Report call graphs in python Tk interface. 206$ python report.py -g --gui 207 208# Report call graphs in html interface. 209$ python report_html.py 210 211# Report call graphs in flamegraphs. 212# On Windows, use inferno.bat instead of ./inferno.sh. 213$ ./inferno.sh -sc 214``` 215 216## Report in html interface 217 218We can use [report_html.py](scripts_reference.md#report_htmlpy) to show profiling results in a web browser. 219report_html.py integrates chart statistics, sample table, flamegraphs, source code annotation 220and disassembly annotation. It is the recommended way to show reports. 221 222```sh 223$ python report_html.py 224``` 225 226## Show flamegraph 227 228To show flamegraphs, we need to first record call graphs. Flamegraphs are shown by 229report_html.py in the "Flamegraph" tab. 230We can also use [inferno](scripts_reference.md#inferno) to show flamegraphs directly. 231 232```sh 233# On Windows, use inferno.bat instead of ./inferno.sh. 234$ ./inferno.sh -sc 235``` 236 237We can also build flamegraphs using https://github.com/brendangregg/FlameGraph. 238Please make sure you have perl installed. 239 240```sh 241$ git clone https://github.com/brendangregg/FlameGraph.git 242$ python report_sample.py --symfs binary_cache >out.perf 243$ FlameGraph/stackcollapse-perf.pl out.perf >out.folded 244$ FlameGraph/flamegraph.pl out.folded >a.svg 245``` 246 247## Report in Android Studio 248 249simpleperf report-sample command can convert perf.data into protobuf format accepted by 250Android Studio cpu profiler. The conversion can be done either on device or on host. If you have 251more symbol info on host, then prefer do it on host with --symdir option. 252 253```sh 254$ simpleperf report-sample --protobuf --show-callchain -i perf.data -o perf.trace 255# Then open perf.trace in Android Studio to show it. 256``` 257 258## Record both on CPU time and off CPU time 259 260We can [record both on CPU time and off CPU time](executable_commands_reference.md#record-both-on-cpu-time-and-off-cpu-time). 261 262First check if trace-offcpu feature is supported on the device. 263 264```sh 265$ python run_simpleperf_on_device.py list --show-features 266dwarf-based-call-graph 267trace-offcpu 268``` 269 270If trace-offcpu is supported, it will be shown in the feature list. Then we can try it. 271 272```sh 273$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative -a .SleepActivity \ 274 -r "-g -e task-clock:u -f 1000 --duration 10 --trace-offcpu" \ 275 -lib path_of_SimpleperfExampleWithNative 276$ python report_html.py --add_disassembly --add_source_code \ 277 --source_dirs path_of_SimpleperfExampleWithNative 278``` 279 280## Profile from launch 281 282We can [profile from launch of an application](scripts_reference.md#profile-from-launch-of-an-application). 283 284```sh 285# Start simpleperf recording, then start the Activity to profile. 286$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative -a .MainActivity 287 288# We can also start the Activity on the device manually. 289# 1. Make sure the application isn't running or one of the recent apps. 290# 2. Start simpleperf recording. 291$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative 292# 3. Start the app manually on the device. 293``` 294 295## Control recording in application code 296 297Simpleperf supports controlling recording from application code. Below is the workflow: 298 2991. Run `api_profiler.py prepare` to enable simpleperf recording on a device. The script needs to run 300 every time the device reboots. 301 3022. Link simpleperf app_api code in the application. The app needs to be debuggable or 303 profileableFromShell as described [here](#prepare-an-android-application). Then the app can 304 use the api to start/pause/resume/stop recording. To start recording, the app_api forks a child 305 process running simpleperf, and uses pipe files to send commands to the child process. After 306 recording, a profiling data file is generated. 307 3083. Run `api_profiler.py collect -p <package_name>` to collect profiling data files to host. 309 310Examples are CppApi and JavaApi in [demo](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/demo). 311 312 313## Parse profiling data manually 314 315We can also write python scripts to parse profiling data manually, by using 316[simpleperf_report_lib.py](scripts_reference.md#simpleperf_report_libpy). Examples are report_sample.py, 317report_html.py. 318