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