1# Heap profiler 2 3NOTE: **heapprofd requires Android 10 or higher** 4 5Heapprofd is a tool that tracks heap allocations & deallocations of an Android 6process within a given time period. The resulting profile can be used to 7attribute 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 11By 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 15On 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 31 32 33 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 63You can use the `tools/heap_profile` script. If you are having trouble 64make sure you are using the 65[latest version]( 66https://raw.githubusercontent.com/google/perfetto/master/tools/heap_profile). 67 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 75You 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 82#### Using the Recording page of Perfetto UI 83 84You can also use the [Perfetto UI](https://ui.perfetto.dev/#!/record/memory) 85to 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 92 93 94The resulting profile proto contains four views on the data, for each diamond. 95 96* **Unreleased malloc size**: how many bytes were allocated but not freed at 97 this callstack, from the moment the recording was started until the timestamp 98 of the diamond. 99* **Total malloc size**: how many bytes were allocated (including ones freed at 100 the moment of the dump) at this callstack, from the moment the recording was 101 started until the timestamp of the diamond. 102* **Unreleased malloc count**: how many allocations without matching frees were 103 done at this callstack, from the moment the recording was started until the 104 timestamp of the diamond. 105* **Total malloc count**: how many allocations (including ones with matching 106 frees) were done at this callstack, from the moment the recording was started 107 started until the timestamp of the diamond. 108 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 113TIP: Click Left Heavy on the top left for a good visualization. 114 115## 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 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 142## 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. 156See the [heapprofd Sampling](/docs/design-docs/heapprofd-sampling) document for 157details. 158 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) 184are 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. 191 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 225On userdebug builds, all processes except for a small set of critical 226services can be profiled (to find the set of disallowed targets, look for 227`never_profile_heap` in [heapprofd.te]( 228https://cs.android.com/android/platform/superproject/+/master:system/sepolicy/private/heapprofd.te?q=never_profile_heap). 229This 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 257## {#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 264Heapprofd can be configured to track Java allocations instead of native ones. 265* 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 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 292## 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 314### 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 320using `sudo apt install llvm`. 321 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 350 as subdirectory, then the rest of the hex digits, with ".debug" appended. 351 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 363Alternatively, 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 368## Deobfuscation 369 370If your profile contains obfuscated Java methods (like `fsd.a`), you can 371provide a deobfuscation map to turn them back into human readable. 372To do so, use the `PERFETTO_PROGUARD_MAP` environment variable, using the 373format `packagename=map_filename[:packagename=map_filename...]`, e.g. 374`PERFETTO_PROGUARD_MAP=com.example.pkg1=foo.txt:com.example.pkg2=bar.txt`. 375All 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 381trace to get a deobfuscated version of it (the input trace should be in the 382perfetto format, otherwise concatenation will not produce a reasonable output). 383 384``` 385PERFETTO_PROGUARD_MAP=com.example.pkg=proguard_map.txt tools/traceconv deobfuscate ${TRACE} > deobfuscation_map 386cat ${TRACE} deobfuscation_map > deobfuscated_trace 387``` 388 389`deobfuscated_trace` can be viewed in the 390[Perfetto UI](https://ui.perfetto.dev). 391 392## 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 406[Target processes](#heapprofd-targets) above. 407 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 460## (non-Android) Linux support 461 462NOTE: Do not use this for production purposes. 463 464You can use a standalone library to profile memory allocations on Linux. 465First [build Perfetto](/docs/contributing/build-instructions.md). You only need 466to do this once. 467 468``` 469tools/setup_all_configs.py 470ninja -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``` 482tools/heap_profile -n trace_processor_shell --print-config | \ 483out/linux_clang_release/perfetto \ 484 -c - --txt \ 485 -o ~/heapprofd-trace 486``` 487 488Finally, run your target (e.g. trace_processor_shell) with LD_PRELOAD 489 490``` 491LD_PRELOAD=out/linux_clang_release/libheapprofd_glibc_preload.so out/linux_clang_release/trace_processor_shell <trace> 492``` 493 494Then, Ctrl-C the Perfetto invocation and upload ~/heapprofd-trace to the 495[Perfetto UI](https://ui.perfetto.dev). 496 497NOTE: 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 504## Known Issues 505 506### {#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 518### {#known-issues-android11} Android 11 519 520* 32-bit programs cannot be targeted on 64-bit devices. 521* Setting `sampling_interval_bytes` to 0 crashes the target process. 522 This is an invalid config that should be rejected instead. 523* For startup profiles, some frame names might be missing. This will be 524 resolved in Android 12. 525* `Failed to send control socket byte.` is displayed in logcat at the end of 526 every profile. This is benign. 527* The object count may be incorrect in `dump_at_max` profiles. 528* Choosing a low shared memory buffer size and `block_client` mode might 529 lock up the target process. 530 531### {#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. 537* x86 / x86_64 platforms are not supported. This includes the Android 538_Cuttlefish_. 539 emulator. 540* On ARM32, the bottom-most frame is always `ERROR 2`. This is harmless and 541 the callstacks are still complete. 542* 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. 547* 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. 551* Setting `sampling_interval_bytes` to 0 crashes the target process. 552 This is an invalid config that should be rejected instead. 553* `Failed to send control socket byte.` is displayed in logcat at the end of 554 every profile. This is benign. 555* The object count may be incorrect in `dump_at_max` profiles. 556* Choosing a low shared memory buffer size and `block_client` mode might 557 lock up the target process. 558 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, 596you might be hitting some pathological fragmentation problem in the allocator. 597 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 623the `Unreleased malloc size` view. 624 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 657select name, map_name, cumulative_size 658 from experimental_flamegraph 659 where ts = 8300973884377 660 and upid = 1 661 and profile_type = 'native' 662 order by abs(cumulative_size) desc; 663``` 664 665| name | map_name | cumulative_size | 666|------|----------|----------------| 667|__start_thread|/apex/com.android.runtime/lib64/bionic/libc.so|392608| 668|_ZL15__pthread_startPv|/apex/com.android.runtime/lib64/bionic/libc.so|392608| 669|_ZN13thread_data_t10trampolineEPKS|/system/lib64/libutils.so|199496| 670|_ZN7android14AndroidRuntime15javaThreadShellEPv|/system/lib64/libandroid_runtime.so|199496| 671|_ZN7android6Thread11_threadLoopEPv|/system/lib64/libutils.so|199496| 672|_ZN3art6Thread14CreateCallbackEPv|/apex/com.android.art/lib64/libart.so|193112| 673|_ZN3art35InvokeVirtualOrInterface...|/apex/com.android.art/lib64/libart.so|193112| 674|_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc|/apex/com.android.art/lib64/libart.so|193112| 675|art_quick_invoke_stub|/apex/com.android.art/lib64/libart.so|193112| 676