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