blob: 47b3f2a02fec62c4b64023834c002c170ca61249 [file] [log] [blame] [view]
Daniele Di Proietto483fb592022-11-09 16:43:50 +00001# Heap profiler
Primiano Tuccia6624852020-05-21 19:12:50 +01002
3NOTE: **heapprofd requires Android 10 or higher**
4
Daniele Di Proietto483fb592022-11-09 16:43:50 +00005Heapprofd is a tool that tracks heap allocations & deallocations of an Android
6process within a given time period. The resulting profile can be used to
Primiano Tuccia6624852020-05-21 19:12:50 +01007attribute memory usage to particular call-stacks, supporting a mix of both
8native and java code. The tool can be used by Android platform and app
9developers to investigate memory issues.
10
Daniele Di Proietto483fb592022-11-09 16:43:50 +000011By default, the tool records native allocations and deallocations done with
12malloc/free (or new/delete). It can be configured to record java heap memory
13allocations instead: see [Java heap sampling](#java-heap-sampling) below.
14
Primiano Tuccia6624852020-05-21 19:12:50 +010015On debug Android builds, you can profile all apps and most system services.
16On "user" builds, you can only use it on apps with the debuggable or
17profileable manifest flag.
18
19## Quickstart
20
21See the [Memory Guide](/docs/case-studies/memory.md#heapprofd) for getting
22started with heapprofd.
23
24## UI
25
26Dumps from heapprofd are shown as flamegraphs in the UI after clicking on the
27diamond. Each diamond corresponds to a snapshot of the allocations and
28callstacks collected at that point in time.
29
30![heapprofd snapshots in the UI tracks](/docs/images/profile-diamond.png)
31
Daniele Di Proietto483fb592022-11-09 16:43:50 +000032![heapprofd flamegraph](/docs/images/native-heap-prof.png)
Primiano Tuccia6624852020-05-21 19:12:50 +010033
34## SQL
35
36Information about callstacks is written to the following tables:
37
38* [`stack_profile_mapping`](/docs/analysis/sql-tables.autogen#stack_profile_mapping)
39* [`stack_profile_frame`](/docs/analysis/sql-tables.autogen#stack_profile_frame)
40* [`stack_profile_callsite`](/docs/analysis/sql-tables.autogen#stack_profile_callsite)
41
42The allocations themselves are written to
43[`heap_profile_allocation`](/docs/analysis/sql-tables.autogen#heap_profile_allocation).
44
45Offline symbolization data is stored in
46[`stack_profile_symbol`](/docs/analysis/sql-tables.autogen#stack_profile_symbol).
47
48See [Example Queries](#heapprofd-example-queries) for example SQL queries.
49
50## Recording
51
52Heapprofd can be configured and started in three ways.
53
54#### Manual configuration
55
56This requires manually setting the
57[HeapprofdConfig](/docs/reference/trace-config-proto.autogen#HeapprofdConfig)
58section of the trace config. The only benefit of doing so is that in this way
59heap profiling can be enabled alongside any other tracing data sources.
60
61#### Using the tools/heap_profile script (recommended)
62
Florian Mayer6d9066a2020-09-09 17:09:00 +010063You can use the `tools/heap_profile` script. If you are having trouble
Primiano Tuccia6624852020-05-21 19:12:50 +010064make sure you are using the
65[latest version](
Lalit Maganti9e0146e2023-07-06 23:15:24 +010066https://raw.githubusercontent.com/google/perfetto/main/tools/heap_profile).
Primiano Tuccia6624852020-05-21 19:12:50 +010067
68You can target processes either by name (`-n com.example.myapp`) or by PID
69(`-p 1234`). In the first case, the heap profile will be initiated on both on
70already-running processes that match the package name and new processes launched
71after the profiling session is started.
72For the full arguments list see the
73[heap_profile cmdline reference page](/docs/reference/heap_profile-cli).
74
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +000075You can use the [Perfetto UI](https://ui.perfetto.dev) to visualize heap dumps.
76Upload the `raw-trace` file in your output directory. You will see all heap
77dumps as diamonds on the timeline, click any of them to get a flamegraph.
78
79Alternatively [Speedscope](https://speedscope.app) can be used to visualize
80the gzipped protos, but will only show the "Unreleased malloc size" view.
81
Primiano Tuccia6624852020-05-21 19:12:50 +010082#### Using the Recording page of Perfetto UI
83
Tuchila Octavian4e04b302021-06-22 16:26:43 +010084You can also use the [Perfetto UI](https://ui.perfetto.dev/#!/record/memory)
Primiano Tuccia6624852020-05-21 19:12:50 +010085to record heapprofd profiles. Tick "Heap profiling" in the trace configuration,
86enter the processes you want to target, click "Add Device" to pair your phone,
87and record profiles straight from your browser. This is also possible on
88Windows.
89
90## Viewing the data
91
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +000092![Profile Diamond](/docs/images/profile-diamond.png)
93
94The resulting profile proto contains four views on the data, for each diamond.
Primiano Tuccia6624852020-05-21 19:12:50 +010095
Daniele Di Proietto483fb592022-11-09 16:43:50 +000096* **Unreleased malloc size**: how many bytes were allocated but not freed at
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +000097 this callstack, from the moment the recording was started until the timestamp
98 of the diamond.
Daniele Di Proietto483fb592022-11-09 16:43:50 +000099* **Total malloc size**: how many bytes were allocated (including ones freed at
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +0000100 the moment of the dump) at this callstack, from the moment the recording was
101 started until the timestamp of the diamond.
Daniele Di Proietto483fb592022-11-09 16:43:50 +0000102* **Unreleased malloc count**: how many allocations without matching frees were
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +0000103 done at this callstack, from the moment the recording was started until the
104 timestamp of the diamond.
Daniele Di Proietto483fb592022-11-09 16:43:50 +0000105* **Total malloc count**: how many allocations (including ones with matching
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +0000106 frees) were done at this callstack, from the moment the recording was started
107 started until the timestamp of the diamond.
Primiano Tuccia6624852020-05-21 19:12:50 +0100108
109_(Googlers: You can also open the gzipped protos using http://pprof/)_
110
111TIP: you might want to put `libart.so` as a "Hide regex" when profiling apps.
112
Primiano Tuccia6624852020-05-21 19:12:50 +0100113TIP: Click Left Heavy on the top left for a good visualization.
114
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +0000115## Continuous dumps
116
117By default, the heap profiler captures all the allocations from the beginning of
118the recording and stores a single snapshot, shown as a single diamond in the UI,
119which summarizes all allocations/frees.
120
121It is possible to configure the heap profiler to periodically (not just at the
122end of the trace) store snapshots (continuous dumps), for example every 5000ms
123
124* By setting "Continuous dumps interval" in the UI to 5000.
125* By adding
126 ```
127 continuous_dump_config {
128 dump_interval_ms: 5000
129 }
130 ```
131 in the
132 [HeapprofdConfig](/docs/reference/trace-config-proto.autogen#HeapprofdConfig).
133* By adding `-c 5000` to the invocation of
134 [`tools/heap_profile`](/docs/reference/heap_profile-cli).
135
136![Continuous dump flamegraph](/docs/images/heap_prof_continuous.png)
137
138The resulting visualization shows multiple diamonds. Clicking on each diamond
139shows a summary of the allocations/frees from the beginning of the trace until
140that point (i.e. the summary is cumulative).
141
Primiano Tuccia6624852020-05-21 19:12:50 +0100142## Sampling interval
143
144Heapprofd samples heap allocations by hooking calls to malloc/free and C++'s
145operator new/delete. Given a sampling interval of n bytes, one allocation is
146sampled, on average, every n bytes allocated. This allows to reduce the
147performance impact on the target process. The default sampling rate
148is 4096 bytes.
149
150The easiest way to reason about this is to imagine the memory allocations as a
151stream of one byte allocations. From this stream, every byte has a 1/n
152probability of being selected as a sample, and the corresponding callstack
153gets attributed the complete n bytes. For more accuracy, allocations larger than
154the sampling interval bypass the sampling logic and are recorded with their true
155size.
Florian Mayer4158a052021-06-08 13:28:44 +0100156See the [heapprofd Sampling](/docs/design-docs/heapprofd-sampling) document for
157details.
Primiano Tuccia6624852020-05-21 19:12:50 +0100158
159## Startup profiling
160
161When specifying a target process name (as opposite to the PID), new processes
162matching that name are profiled from their startup. The resulting profile will
163contain all allocations done between the start of the process and the end
164of the profiling session.
165
166On Android, Java apps are usually not exec()-ed from scratch, but fork()-ed from
167the [zygote], which then specializes into the desired app. If the app's name
168matches a name specified in the profiling session, profiling will be enabled as
169part of the zygote specialization. The resulting profile contains all
170allocations done between that point in zygote specialization and the end of the
171profiling session. Some allocations done early in the specialization process are
172not accounted for.
173
174At the trace proto level, the resulting [ProfilePacket] will have the
175`from_startup` field set to true in the corresponding `ProcessHeapSamples`
176message. This is not surfaced in the converted pprof compatible proto.
177
178[ProfilePacket]: /docs/reference/trace-packet-proto.autogen#ProfilePacket
179[zygote]: https://developer.android.com/topic/performance/memory-overview#SharingRAM
180
181## Runtime profiling
182
183When a profiling session is started, all matching processes (by name or PID)
Florian Mayer31b44b32020-10-08 15:08:41 +0100184are enumerated and are signalled to request profiling. Profiling isn't actually
185enabled until a few hundred milliseconds after the next allocation that is
186done by the application. If the application is idle when profiling is
187requested, and then does a burst of allocations, these may be missed.
188
189The resulting profile will contain all allocations done between when profiling
190is enabled, and the end of the profiling session.
Primiano Tuccia6624852020-05-21 19:12:50 +0100191
192The resulting [ProfilePacket] will have `from_startup` set to false in the
193corresponding `ProcessHeapSamples` message. This does not get surfaced in the
194converted pprof compatible proto.
195
196## Concurrent profiling sessions
197
198If multiple sessions name the same target process (either by name or PID),
199only the first relevant session will profile the process. The other sessions
200will report that the process had already been profiled when converting to
201the pprof compatible proto.
202
203If you see this message but do not expect any other sessions, run
204
205```shell
206adb shell killall perfetto
207```
208
209to stop any concurrent sessions that may be running.
210
211The resulting [ProfilePacket] will have `rejected_concurrent` set to true in
212otherwise empty corresponding `ProcessHeapSamples` message. This does not get
213surfaced in the converted pprof compatible proto.
214
215## {#heapprofd-targets} Target processes
216
217Depending on the build of Android that heapprofd is run on, some processes
218are not be eligible to be profiled.
219
220On _user_ (i.e. production, non-rootable) builds, only Java applications with
221either the profileable or the debuggable manifest flag set can be profiled.
222Profiling requests for non-profileable/debuggable processes will result in an
223empty profile.
224
Primiano Tuccia3645202020-08-03 16:28:18 +0200225On userdebug builds, all processes except for a small set of critical
226services can be profiled (to find the set of disallowed targets, look for
Primiano Tuccia6624852020-05-21 19:12:50 +0100227`never_profile_heap` in [heapprofd.te](
Lalit Maganti9e0146e2023-07-06 23:15:24 +0100228https://cs.android.com/android/platform/superproject/+/main:system/sepolicy/private/heapprofd.te?q=never_profile_heap).
Primiano Tuccia6624852020-05-21 19:12:50 +0100229This restriction can be lifted by disabling SELinux by running
230`adb shell su root setenforce 0` or by passing `--disable-selinux` to the
231`heap_profile` script.
232
233<center>
234
235| | userdebug setenforce 0 | userdebug | user |
236|-------------------------|:----------------------:|:---------:|:----:|
237| critical native service | Y | N | N |
238| native service | Y | Y | N |
239| app | Y | Y | N |
240| profileable app | Y | Y | Y |
241| debuggable app | Y | Y | Y |
242
243</center>
244
245To mark an app as profileable, put `<profileable android:shell="true"/>` into
246the `<application>` section of the app manifest.
247
248```xml
249<manifest ...>
250 <application>
251 <profileable android:shell="true"/>
252 ...
253 </application>
254</manifest>
255```
256
Daniele Di Proietto483fb592022-11-09 16:43:50 +0000257## {#java-heap-sampling} Java heap sampling
258
259NOTE: **Java heap sampling is available on Android 12 or higher**
260
261NOTE: **Java heap sampling is not to be confused with [Java heap
262dumps](/docs/data-sources/java-heap-profiler.md)**
263
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +0000264Heapprofd can be configured to track Java allocations instead of native ones.
Daniele Di Proietto483fb592022-11-09 16:43:50 +0000265* By setting adding `heaps: "com.android.art"` in
266 [HeapprofdConfig](/docs/reference/trace-config-proto.autogen#HeapprofdConfig).
267* By adding `--heaps com.android.art` to the invocation of
268 [`tools/heap_profile`](/docs/reference/heap_profile-cli).
269
270Unlike java heap dumps (which show the retention graph of a snapshot of the live
271objects) but like native heap profiles, java heap samples show callstacks of
272allocations over time of the entire profile.
273
274Java heap samples only show callstacks of when objects are created, not when
275they're deleted or garbage collected.
276
277![javaheapsamples](/docs/images/java-heap-samples.png)
278
279The resulting profile proto contains two views on the data:
280
281* **Total allocation size**: how many bytes were allocated at this callstack
282 over time of the profile until this point. The bytes might have been freed or
283 not, the tool does not keep track of that.
284* **Total allocation count**: how many object were allocated at this callstack
285 over time of the profile until this point. The objects might have been freed
286 or not, the tool does not keep track of that.
287
288Java heap samples are useful to understand memory churn showing the call stack
289of which parts of the code large allocations are attributed to as well as the
290allocation type from the ART runtime.
291
Primiano Tuccia6624852020-05-21 19:12:50 +0100292## DEDUPED frames
293
294If the name of a Java method includes `[DEDUPED]`, this means that multiple
295methods share the same code. ART only stores the name of a single one in its
296metadata, which is displayed here. This is not necessarily the one that was
297called.
298
299## Triggering heap snapshots on demand
300
301Heap snapshot are recorded into the trace either at regular time intervals, if
302using the `continuous_dump_config` field, or at the end of the session.
303
304You can also trigger a snapshot of all currently profiled processes by running
305`adb shell killall -USR1 heapprofd`. This can be useful in lab tests for
306recording the current memory usage of the target in a specific state.
307
308This dump will show up in addition to the dump at the end of the profile that is
309always produced. You can create multiple of these dumps, and they will be
310enumerated in the output directory.
311
312## Symbolization
313
Primiano Tuccia6624852020-05-21 19:12:50 +0100314### Set up llvm-symbolizer
315
316You only need to do this once.
317
318To use symbolization, your system must have llvm-symbolizer installed and
319accessible from `$PATH` as `llvm-symbolizer`. On Debian, you can install it
Daniele Di Proietto03942592022-07-08 13:53:19 +0100320using `sudo apt install llvm`.
Primiano Tuccia6624852020-05-21 19:12:50 +0100321
322### Symbolize your profile
323
324If the profiled binary or libraries do not have symbol names, you can
325symbolize profiles offline. Even if they do, you might want to symbolize in
326order to get inlined function and line number information. All tools
327(traceconv, trace_processor_shell, the heap_profile script) support specifying
328the `PERFETTO_BINARY_PATH` as an environment variable.
329
330```
331PERFETTO_BINARY_PATH=somedir tools/heap_profile --name ${NAME}
332```
333
334You can persist symbols for a trace by running
335`PERFETTO_BINARY_PATH=somedir tools/traceconv symbolize raw-trace > symbols`.
336You can then concatenate the symbols to the trace (
337`cat raw-trace symbols > symbolized-trace`) and the symbols will part of
338`symbolized-trace`. The `tools/heap_profile` script will also generate this
339file in your output directory, if `PERFETTO_BINARY_PATH` is used.
340
341The symbol file is the first with matching Build ID in the following order:
342
3431. absolute path of library file relative to binary path.
3442. absolute path of library file relative to binary path, but with base.apk!
345 removed from filename.
3463. basename of library file relative to binary path.
3474. basename of library file relative to binary path, but with base.apk!
348 removed from filename.
3495. in the subdirectory .build-id: the first two hex digits of the build-id
Florian Mayer82ca62d2020-06-09 19:38:06 +0200350 as subdirectory, then the rest of the hex digits, with ".debug" appended.
Primiano Tuccia6624852020-05-21 19:12:50 +0100351 See
352 https://fedoraproject.org/wiki/RolandMcGrath/BuildID#Find_files_by_build_ID
353
354For example, "/system/lib/base.apk!foo.so" with build id abcd1234,
355is looked for at:
356
3571. $PERFETTO_BINARY_PATH/system/lib/base.apk!foo.so
3582. $PERFETTO_BINARY_PATH/system/lib/foo.so
3593. $PERFETTO_BINARY_PATH/base.apk!foo.so
3604. $PERFETTO_BINARY_PATH/foo.so
3615. $PERFETTO_BINARY_PATH/.build-id/ab/cd1234.debug
362
Florian Mayer6d1f0ae2020-07-21 08:59:29 +0100363Alternatively, you can set the `PERFETTO_SYMBOLIZER_MODE` environment variable
364to `index`, and the symbolizer will recursively search the given directory for
365an ELF file with the given build id. This way, you will not have to worry
366about correct filenames.
367
Florian Mayer04433322020-12-10 12:41:04 +0000368## Deobfuscation
369
370If your profile contains obfuscated Java methods (like `fsd.a`), you can
Florian Mayeredc48102020-12-15 19:38:44 +0000371provide a deobfuscation map to turn them back into human readable.
Florian Mayer04433322020-12-10 12:41:04 +0000372To do so, use the `PERFETTO_PROGUARD_MAP` environment variable, using the
Daniele Di Proietto59fa7f12022-10-19 11:21:48 +0100373format `packagename=map_filename[:packagename=map_filename...]`, e.g.
Florian Mayer04433322020-12-10 12:41:04 +0000374`PERFETTO_PROGUARD_MAP=com.example.pkg1=foo.txt:com.example.pkg2=bar.txt`.
Florian Mayeredc48102020-12-15 19:38:44 +0000375All tools
376(traceconv, trace_processor_shell, the heap_profile script) support specifying
377the `PERFETTO_PROGUARD_MAP` as an environment variable.
378
379You can get a deobfuscation map for your trace using
380`tools/traceconv deobfuscate`. Then concatenate the resulting file to your
Daniele Di Proietto59fa7f12022-10-19 11:21:48 +0100381trace to get a deobfuscated version of it (the input trace should be in the
382perfetto format, otherwise concatenation will not produce a reasonable output).
Florian Mayer04433322020-12-10 12:41:04 +0000383
384```
Daniele Di Proietto59fa7f12022-10-19 11:21:48 +0100385PERFETTO_PROGUARD_MAP=com.example.pkg=proguard_map.txt tools/traceconv deobfuscate ${TRACE} > deobfuscation_map
Florian Mayer04433322020-12-10 12:41:04 +0000386cat ${TRACE} deobfuscation_map > deobfuscated_trace
387```
388
Daniele Di Proietto59fa7f12022-10-19 11:21:48 +0100389`deobfuscated_trace` can be viewed in the
390[Perfetto UI](https://ui.perfetto.dev).
391
Primiano Tuccia6624852020-05-21 19:12:50 +0100392## Troubleshooting
393
394### Buffer overrun
395
396If the rate of allocations is too high for heapprofd to keep up, the profiling
397session will end early due to a buffer overrun. If the buffer overrun is
398caused by a transient spike in allocations, increasing the shared memory buffer
399size (passing `--shmem-size` to `tools/heap_profile`) can resolve the issue.
400Otherwise the sampling interval can be increased (at the expense of lower
401accuracy in the resulting profile) by passing `--interval=16000` or higher.
402
403### Profile is empty
404
405Check whether your target process is eligible to be profiled by consulting
James Zerne385eb92020-11-06 10:55:21 -0800406[Target processes](#heapprofd-targets) above.
Primiano Tuccia6624852020-05-21 19:12:50 +0100407
408Also check the [Known Issues](#known-issues).
409
410### Implausible callstacks
411
412If you see a callstack that seems to impossible from looking at the code, make
413sure no [DEDUPED frames](#deduped-frames) are involved.
414
415Also, if your code is linked using _Identical Code Folding_
416(ICF), i.e. passing `-Wl,--icf=...` to the linker, most trivial functions, often
417constructors and destructors, can be aliased to binary-equivalent operators
418of completely unrelated classes.
419
420### Symbolization: Could not find library
421
422When symbolizing a profile, you might come across messages like this:
423
424```bash
425Could not find /data/app/invalid.app-wFgo3GRaod02wSvPZQ==/lib/arm64/somelib.so
426(Build ID: 44b7138abd5957b8d0a56ce86216d478).
427```
428
429Check whether your library (in this example somelib.so) exists in
430`PERFETTO_BINARY_PATH`. Then compare the Build ID to the one in your
431symbol file, which you can get by running
432`readelf -n /path/in/binary/path/somelib.so`. If it does not match, the
433symbolized file has a different version than the one on device, and cannot
434be used for symbolization.
435If it does, try moving somelib.so to the root of `PERFETTO_BINARY_PATH` and
436try again.
437
438### Only one frame shown
439If you only see a single frame for functions in a specific library, make sure
440that the library has unwind information. We need one of
441
442* `.gnu_debugdata`
443* `.eh_frame` (+ preferably `.eh_frame_hdr`)
444* `.debug_frame`.
445
446Frame-pointer unwinding is *not supported*.
447
448To check if an ELF file has any of those, run
449
450```console
451$ readelf -S file.so | grep "gnu_debugdata\|eh_frame\|debug_frame"
452 [12] .eh_frame_hdr PROGBITS 000000000000c2b0 0000c2b0
453 [13] .eh_frame PROGBITS 0000000000011000 00011000
454 [24] .gnu_debugdata PROGBITS 0000000000000000 000f7292
455```
456
457If this does not show one or more of the sections, change your build system
458to not strip them.
459
Florian Mayer86d5a4e2020-10-09 14:16:15 +0100460## (non-Android) Linux support
461
Florian Mayeraea9ed42021-05-12 18:19:55 +0100462NOTE: Do not use this for production purposes.
Florian Mayer86d5a4e2020-10-09 14:16:15 +0100463
464You can use a standalone library to profile memory allocations on Linux.
Florian Mayer5922a2d2021-05-12 17:35:41 +0100465First [build Perfetto](/docs/contributing/build-instructions.md). You only need
466to do this once.
Florian Mayer86d5a4e2020-10-09 14:16:15 +0100467
468```
Anna Mayzner5089d222022-11-07 16:25:55 +0000469tools/setup_all_configs.py
Florian Mayer86d5a4e2020-10-09 14:16:15 +0100470ninja -C out/linux_clang_release
471```
472
473Then, run traced
474
475```
476out/linux_clang_release/traced
477```
478
479Start the profile (e.g. targeting trace_processor_shell)
480
481```
Florian Mayer5922a2d2021-05-12 17:35:41 +0100482tools/heap_profile -n trace_processor_shell --print-config | \
Florian Mayer86d5a4e2020-10-09 14:16:15 +0100483out/linux_clang_release/perfetto \
484 -c - --txt \
Florian Mayer5922a2d2021-05-12 17:35:41 +0100485 -o ~/heapprofd-trace
Florian Mayer86d5a4e2020-10-09 14:16:15 +0100486```
487
488Finally, run your target (e.g. trace_processor_shell) with LD_PRELOAD
489
490```
Florian Mayer7c52b262021-01-13 12:44:53 +0000491LD_PRELOAD=out/linux_clang_release/libheapprofd_glibc_preload.so out/linux_clang_release/trace_processor_shell <trace>
Florian Mayer86d5a4e2020-10-09 14:16:15 +0100492```
493
494Then, Ctrl-C the Perfetto invocation and upload ~/heapprofd-trace to the
495[Perfetto UI](https://ui.perfetto.dev).
496
Lalit Maganti94a969c2022-03-04 16:18:14 +0000497NOTE: by default, heapprofd lazily initalizes to avoid blocking your program's
498main thread. However, if your program makes memory allocations on startup,
499these can be missed. To avoid this from happening, set the enironment variable
500`PERFETTO_HEAPPROFD_BLOCKING_INIT=1`; on the first malloc, your program will
501be blocked until heapprofd initializes fully but means every allocation will
502be correctly tracked.
503
Primiano Tuccia6624852020-05-21 19:12:50 +0100504## Known Issues
505
Daniele Di Proietto5982e3b2022-11-16 10:18:05 +0000506### {#known-issues-android13} Android 13
507
508* Unwinding java frames might not work properly, depending on the ART module
509 version in use. The UI reports a single "unknown" frame at the top of the
510 stack in this case. The problem is fixed in Android 13 QPR1.
511
512### {#known-issues-android12} Android 12
513
514* Unwinding java frames might not work properly, depending on the ART module
515 version in use. The UI reports a single "unknown" frame at the top of the
516 stack in this case.
517
Florian Mayerc4de3912020-11-23 14:11:43 +0000518### {#known-issues-android11} Android 11
Florian Mayercc61e5a2020-08-27 16:10:22 +0100519
520* 32-bit programs cannot be targeted on 64-bit devices.
Florian Mayer12494ee2020-09-23 16:25:58 +0100521* Setting `sampling_interval_bytes` to 0 crashes the target process.
522 This is an invalid config that should be rejected instead.
Florian Mayerfb364652020-11-05 10:38:52 +0000523* For startup profiles, some frame names might be missing. This will be
524 resolved in Android 12.
Florian Mayerc21ce022021-02-01 17:28:56 +0000525* `Failed to send control socket byte.` is displayed in logcat at the end of
526 every profile. This is benign.
Florian Mayerc0357532021-02-16 16:22:58 +0000527* The object count may be incorrect in `dump_at_max` profiles.
Florian Mayer0ce14ff2021-05-17 13:34:11 +0100528* Choosing a low shared memory buffer size and `block_client` mode might
529 lock up the target process.
Florian Mayercc61e5a2020-08-27 16:10:22 +0100530
Florian Mayerc4de3912020-11-23 14:11:43 +0000531### {#known-issues-android10} Android 10
532* Function names in libraries with load bias might be incorrect. Use
533 [offline symbolization](#symbolization) to resolve this issue.
534* For startup profiles, some frame names might be missing. This will be
535 resolved in Android 12.
536* 32-bit programs cannot be targeted on 64-bit devices.
Florian Mayer371d8952021-03-17 13:53:23 +0000537* x86 / x86_64 platforms are not supported. This includes the Android
538_Cuttlefish_.
Primiano Tuccia6624852020-05-21 19:12:50 +0100539 emulator.
Florian Mayerc4de3912020-11-23 14:11:43 +0000540* On ARM32, the bottom-most frame is always `ERROR 2`. This is harmless and
541 the callstacks are still complete.
Primiano Tuccia6624852020-05-21 19:12:50 +0100542* If heapprofd is run standalone (by running `heapprofd` in a root shell, rather
543 than through init), `/dev/socket/heapprofd` get assigned an incorrect SELinux
544 domain. You will not be able to profile any processes unless you disable
545 SELinux enforcement.
546 Run `restorecon /dev/socket/heapprofd` in a root shell to resolve.
Florian Mayer9c7a8fb2020-06-18 15:36:00 +0200547* Using `vfork(2)` or `clone(2)` with `CLONE_VM` and allocating / freeing
548 memory in the child process will prematurely end the profile.
549 `java.lang.Runtime.exec` does this, calling it will prematurely end
550 the profile. Note that this is in violation of the POSIX standard.
Florian Mayer12494ee2020-09-23 16:25:58 +0100551* Setting `sampling_interval_bytes` to 0 crashes the target process.
552 This is an invalid config that should be rejected instead.
Florian Mayerc21ce022021-02-01 17:28:56 +0000553* `Failed to send control socket byte.` is displayed in logcat at the end of
554 every profile. This is benign.
Florian Mayerc0357532021-02-16 16:22:58 +0000555* The object count may be incorrect in `dump_at_max` profiles.
Florian Mayer0ce14ff2021-05-17 13:34:11 +0100556* Choosing a low shared memory buffer size and `block_client` mode might
557 lock up the target process.
Primiano Tuccia6624852020-05-21 19:12:50 +0100558
559## Heapprofd vs malloc_info() vs RSS
560
561When using heapprofd and interpreting results, it is important to know the
562precise meaning of the different memory metrics that can be obtained from the
563operating system.
564
565**heapprofd** gives you the number of bytes the target program
566requested from the default C/C++ allocator. If you are profiling a Java app from
567startup, allocations that happen early in the application's initialization will
568not be visible to heapprofd. Native services that do not fork from the Zygote
569are not affected by this.
570
571**malloc\_info** is a libc function that gives you information about the
572allocator. This can be triggered on userdebug builds by using
573`am dumpheap -m <PID> /data/local/tmp/heap.txt`. This will in general be more
574than the memory seen by heapprofd, depending on the allocator not all memory
575is immediately freed. In particular, jemalloc retains some freed memory in
576thread caches.
577
578**Heap RSS** is the amount of memory requested from the operating system by the
579allocator. This is larger than the previous two numbers because memory can only
580be obtained in page size chunks, and fragmentation causes some of that memory to
581be wasted. This can be obtained by running `adb shell dumpsys meminfo <PID>` and
582looking at the "Private Dirty" column.
583RSS can also end up being smaller than the other two if the device kernel uses
584memory compression (ZRAM, enabled by default on recent versions of android) and
585the memory of the process get swapped out onto ZRAM.
586
587| | heapprofd | malloc\_info | RSS |
588|---------------------|:-----------------:|:------------:|:---:|
589| from native startup | x | x | x |
590| after zygote init | x | x | x |
591| before zygote init | | x | x |
592| thread caches | | x | x |
593| fragmentation | | | x |
594
595If you observe high RSS or malloc\_info metrics but heapprofd does not match,
Florian Mayer27a43fb2021-05-19 11:06:10 +0100596you might be hitting some pathological fragmentation problem in the allocator.
Primiano Tuccia6624852020-05-21 19:12:50 +0100597
598## Convert to pprof
599
600You can use [traceconv](/docs/quickstart/traceconv.md) to convert the heap dumps
601in a trace into the [pprof](https://github.com/google/pprof) format. These can
602then be viewed using the pprof CLI or a UI (e.g. Speedscope, or Google-internal
603pprof/).
604
605```bash
606tools/traceconv profile /tmp/profile
607```
608
609This will create a directory in `/tmp/` containing the heap dumps. Run:
610
611```bash
612gzip /tmp/heap_profile-XXXXXX/*.pb
613```
614
615to get gzipped protos, which tools handling pprof profile protos expect.
616
617## {#heapprofd-example-queries} Example SQL Queries
618
619We can get the callstacks that allocated using an SQL Query in the
620Trace Processor. For each frame, we get one row for the number of allocated
621bytes, where `count` and `size` is positive, and, if any of them were already
622freed, another line with negative `count` and `size`. The sum of those gets us
Daniele Di Proiettod92a37a2022-11-11 14:22:04 +0000623the `Unreleased malloc size` view.
Primiano Tuccia6624852020-05-21 19:12:50 +0100624
625```sql
626select a.callsite_id, a.ts, a.upid, f.name, f.rel_pc, m.build_id, m.name as mapping_name,
627 sum(a.size) as space_size, sum(a.count) as space_count
628 from heap_profile_allocation a join
629 stack_profile_callsite c ON (a.callsite_id = c.id) join
630 stack_profile_frame f ON (c.frame_id = f.id) join
631 stack_profile_mapping m ON (f.mapping = m.id)
632 group by 1, 2, 3, 4, 5, 6, 7 order by space_size desc;
633```
634
635| callsite_id | ts | upid | name | rel_pc | build_id | mapping_name | space_size | space_count |
636|-------------|----|------|-------|-----------|------|--------|----------|------|
637|6660|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |106496|4|
638|192 |5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26624 |1|
639|1421|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26624 |1|
640|1537|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26624 |1|
641|8843|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26424 |1|
642|8618|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |24576 |4|
643|3750|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |12288 |1|
644|2820|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |8192 |2|
645|3788|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |8192 |2|
646
647We can see all the functions are "malloc" and "realloc", which is not terribly
648informative. Usually we are interested in the _cumulative_ bytes allocated in
649a function (otherwise, we will always only see malloc / realloc). Chasing the
650parent_id of a callsite (not shown in this table) recursively is very hard in
651SQL.
652
653There is an **experimental** table that surfaces this information. The **API is
654subject to change**.
655
656```sql
Lalit Magantic07333b2024-01-24 01:12:49 +0000657select
658 name,
659 map_name,
660 cumulative_size
661from experimental_flamegraph(
662 -- The type of the profile from which the flamegraph is being generated.
663 -- Always 'native' for native heap profiles.
664 'native',
665 -- The timestamp of the heap profile.
666 8300973884377,
667 -- Timestamp constraints: not relevant and always null for native heap
668 -- profiles.
669 NULL,
670 -- The upid of the heap profile.
671 1,
672 -- The upid group: not relevant and always null for native heap profiles.
673 NULL,
674 -- A regex for focusing on a particular node in the heapgraph: for advanced
675 -- use only.
676 NULL
677)
678order by abs(cumulative_size) desc;
Tuchila Octaviand94d6242021-11-15 10:39:56 +0000679```
Primiano Tuccia6624852020-05-21 19:12:50 +0100680
681| name | map_name | cumulative_size |
682|------|----------|----------------|
683|__start_thread|/apex/com.android.runtime/lib64/bionic/libc.so|392608|
684|_ZL15__pthread_startPv|/apex/com.android.runtime/lib64/bionic/libc.so|392608|
685|_ZN13thread_data_t10trampolineEPKS|/system/lib64/libutils.so|199496|
686|_ZN7android14AndroidRuntime15javaThreadShellEPv|/system/lib64/libandroid_runtime.so|199496|
687|_ZN7android6Thread11_threadLoopEPv|/system/lib64/libutils.so|199496|
688|_ZN3art6Thread14CreateCallbackEPv|/apex/com.android.art/lib64/libart.so|193112|
689|_ZN3art35InvokeVirtualOrInterface...|/apex/com.android.art/lib64/libart.so|193112|
690|_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc|/apex/com.android.art/lib64/libart.so|193112|
691|art_quick_invoke_stub|/apex/com.android.art/lib64/libart.so|193112|