1# Native heap profiler 2 3NOTE: **heapprofd requires Android 10 or higher** 4 5Heapprofd is a tool that tracks native heap allocations & deallocations of an 6Android process 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 11On debug Android builds, you can profile all apps and most system services. 12On "user" builds, you can only use it on apps with the debuggable or 13profileable manifest flag. 14 15## Quickstart 16 17See the [Memory Guide](/docs/case-studies/memory.md#heapprofd) for getting 18started with heapprofd. 19 20## UI 21 22Dumps from heapprofd are shown as flamegraphs in the UI after clicking on the 23diamond. Each diamond corresponds to a snapshot of the allocations and 24callstacks collected at that point in time. 25 26 27 28 29 30## SQL 31 32Information about callstacks is written to the following tables: 33 34* [`stack_profile_mapping`](/docs/analysis/sql-tables.autogen#stack_profile_mapping) 35* [`stack_profile_frame`](/docs/analysis/sql-tables.autogen#stack_profile_frame) 36* [`stack_profile_callsite`](/docs/analysis/sql-tables.autogen#stack_profile_callsite) 37 38The allocations themselves are written to 39[`heap_profile_allocation`](/docs/analysis/sql-tables.autogen#heap_profile_allocation). 40 41Offline symbolization data is stored in 42[`stack_profile_symbol`](/docs/analysis/sql-tables.autogen#stack_profile_symbol). 43 44See [Example Queries](#heapprofd-example-queries) for example SQL queries. 45 46## Recording 47 48Heapprofd can be configured and started in three ways. 49 50#### Manual configuration 51 52This requires manually setting the 53[HeapprofdConfig](/docs/reference/trace-config-proto.autogen#HeapprofdConfig) 54section of the trace config. The only benefit of doing so is that in this way 55heap profiling can be enabled alongside any other tracing data sources. 56 57#### Using the tools/heap_profile script (recommended) 58 59You can use the `tools/heap_profile` script. If you are having trouble 60make sure you are using the 61[latest version]( 62https://raw.githubusercontent.com/google/perfetto/master/tools/heap_profile). 63 64You can target processes either by name (`-n com.example.myapp`) or by PID 65(`-p 1234`). In the first case, the heap profile will be initiated on both on 66already-running processes that match the package name and new processes launched 67after the profiling session is started. 68For the full arguments list see the 69[heap_profile cmdline reference page](/docs/reference/heap_profile-cli). 70 71#### Using the Recording page of Perfetto UI 72 73You can also use the [Perfetto UI](https://ui.perfetto.dev/#!/record/memory) 74to record heapprofd profiles. Tick "Heap profiling" in the trace configuration, 75enter the processes you want to target, click "Add Device" to pair your phone, 76and record profiles straight from your browser. This is also possible on 77Windows. 78 79## Viewing the data 80 81The resulting profile proto contains four views on the data 82 83* **space**: how many bytes were allocated but not freed at this callstack the 84 moment the dump was created. 85* **alloc\_space**: how many bytes were allocated (including ones freed at the 86 moment of the dump) at this callstack 87* **objects**: how many allocations without matching frees were done at this 88 callstack. 89* **alloc\_objects**: how many allocations (including ones with matching frees) 90 were done at this callstack. 91 92_(Googlers: You can also open the gzipped protos using http://pprof/)_ 93 94TIP: you might want to put `libart.so` as a "Hide regex" when profiling apps. 95 96You can use the [Perfetto UI](https://ui.perfetto.dev) to visualize heap dumps. 97Upload the `raw-trace` file in your output directory. You will see all heap 98dumps as diamonds on the timeline, click any of them to get a flamegraph. 99 100Alternatively [Speedscope](https://speedscope.app) can be used to visualize 101the gzipped protos, but will only show the space view. 102 103TIP: Click Left Heavy on the top left for a good visualization. 104 105## Sampling interval 106 107Heapprofd samples heap allocations by hooking calls to malloc/free and C++'s 108operator new/delete. Given a sampling interval of n bytes, one allocation is 109sampled, on average, every n bytes allocated. This allows to reduce the 110performance impact on the target process. The default sampling rate 111is 4096 bytes. 112 113The easiest way to reason about this is to imagine the memory allocations as a 114stream of one byte allocations. From this stream, every byte has a 1/n 115probability of being selected as a sample, and the corresponding callstack 116gets attributed the complete n bytes. For more accuracy, allocations larger than 117the sampling interval bypass the sampling logic and are recorded with their true 118size. 119See the [heapprofd Sampling](/docs/design-docs/heapprofd-sampling) document for 120details. 121 122## Startup profiling 123 124When specifying a target process name (as opposite to the PID), new processes 125matching that name are profiled from their startup. The resulting profile will 126contain all allocations done between the start of the process and the end 127of the profiling session. 128 129On Android, Java apps are usually not exec()-ed from scratch, but fork()-ed from 130the [zygote], which then specializes into the desired app. If the app's name 131matches a name specified in the profiling session, profiling will be enabled as 132part of the zygote specialization. The resulting profile contains all 133allocations done between that point in zygote specialization and the end of the 134profiling session. Some allocations done early in the specialization process are 135not accounted for. 136 137At the trace proto level, the resulting [ProfilePacket] will have the 138`from_startup` field set to true in the corresponding `ProcessHeapSamples` 139message. This is not surfaced in the converted pprof compatible proto. 140 141[ProfilePacket]: /docs/reference/trace-packet-proto.autogen#ProfilePacket 142[zygote]: https://developer.android.com/topic/performance/memory-overview#SharingRAM 143 144## Runtime profiling 145 146When a profiling session is started, all matching processes (by name or PID) 147are enumerated and are signalled to request profiling. Profiling isn't actually 148enabled until a few hundred milliseconds after the next allocation that is 149done by the application. If the application is idle when profiling is 150requested, and then does a burst of allocations, these may be missed. 151 152The resulting profile will contain all allocations done between when profiling 153is enabled, and the end of the profiling session. 154 155The resulting [ProfilePacket] will have `from_startup` set to false in the 156corresponding `ProcessHeapSamples` message. This does not get surfaced in the 157converted pprof compatible proto. 158 159## Concurrent profiling sessions 160 161If multiple sessions name the same target process (either by name or PID), 162only the first relevant session will profile the process. The other sessions 163will report that the process had already been profiled when converting to 164the pprof compatible proto. 165 166If you see this message but do not expect any other sessions, run 167 168```shell 169adb shell killall perfetto 170``` 171 172to stop any concurrent sessions that may be running. 173 174The resulting [ProfilePacket] will have `rejected_concurrent` set to true in 175otherwise empty corresponding `ProcessHeapSamples` message. This does not get 176surfaced in the converted pprof compatible proto. 177 178## {#heapprofd-targets} Target processes 179 180Depending on the build of Android that heapprofd is run on, some processes 181are not be eligible to be profiled. 182 183On _user_ (i.e. production, non-rootable) builds, only Java applications with 184either the profileable or the debuggable manifest flag set can be profiled. 185Profiling requests for non-profileable/debuggable processes will result in an 186empty profile. 187 188On userdebug builds, all processes except for a small set of critical 189services can be profiled (to find the set of disallowed targets, look for 190`never_profile_heap` in [heapprofd.te]( 191https://cs.android.com/android/platform/superproject/+/master:system/sepolicy/private/heapprofd.te?q=never_profile_heap). 192This restriction can be lifted by disabling SELinux by running 193`adb shell su root setenforce 0` or by passing `--disable-selinux` to the 194`heap_profile` script. 195 196<center> 197 198| | userdebug setenforce 0 | userdebug | user | 199|-------------------------|:----------------------:|:---------:|:----:| 200| critical native service | Y | N | N | 201| native service | Y | Y | N | 202| app | Y | Y | N | 203| profileable app | Y | Y | Y | 204| debuggable app | Y | Y | Y | 205 206</center> 207 208To mark an app as profileable, put `<profileable android:shell="true"/>` into 209the `<application>` section of the app manifest. 210 211```xml 212<manifest ...> 213 <application> 214 <profileable android:shell="true"/> 215 ... 216 </application> 217</manifest> 218``` 219 220## DEDUPED frames 221 222If the name of a Java method includes `[DEDUPED]`, this means that multiple 223methods share the same code. ART only stores the name of a single one in its 224metadata, which is displayed here. This is not necessarily the one that was 225called. 226 227## Triggering heap snapshots on demand 228 229Heap snapshot are recorded into the trace either at regular time intervals, if 230using the `continuous_dump_config` field, or at the end of the session. 231 232You can also trigger a snapshot of all currently profiled processes by running 233`adb shell killall -USR1 heapprofd`. This can be useful in lab tests for 234recording the current memory usage of the target in a specific state. 235 236This dump will show up in addition to the dump at the end of the profile that is 237always produced. You can create multiple of these dumps, and they will be 238enumerated in the output directory. 239 240## Symbolization 241 242### Set up llvm-symbolizer 243 244You only need to do this once. 245 246To use symbolization, your system must have llvm-symbolizer installed and 247accessible from `$PATH` as `llvm-symbolizer`. On Debian, you can install it 248using `sudo apt install llvm-9`. 249This will create `/usr/bin/llvm-symbolizer-9`. Symlink that to somewhere in 250your `$PATH` as `llvm-symbolizer`. 251 252For instance, `ln -s /usr/bin/llvm-symbolizer-9 ~/bin/llvm-symbolizer`, and 253add `~/bin` to your path (or run the commands below with `PATH=~/bin:$PATH` 254prefixed). 255 256### Symbolize your profile 257 258If the profiled binary or libraries do not have symbol names, you can 259symbolize profiles offline. Even if they do, you might want to symbolize in 260order to get inlined function and line number information. All tools 261(traceconv, trace_processor_shell, the heap_profile script) support specifying 262the `PERFETTO_BINARY_PATH` as an environment variable. 263 264``` 265PERFETTO_BINARY_PATH=somedir tools/heap_profile --name ${NAME} 266``` 267 268You can persist symbols for a trace by running 269`PERFETTO_BINARY_PATH=somedir tools/traceconv symbolize raw-trace > symbols`. 270You can then concatenate the symbols to the trace ( 271`cat raw-trace symbols > symbolized-trace`) and the symbols will part of 272`symbolized-trace`. The `tools/heap_profile` script will also generate this 273file in your output directory, if `PERFETTO_BINARY_PATH` is used. 274 275The symbol file is the first with matching Build ID in the following order: 276 2771. absolute path of library file relative to binary path. 2782. absolute path of library file relative to binary path, but with base.apk! 279 removed from filename. 2803. basename of library file relative to binary path. 2814. basename of library file relative to binary path, but with base.apk! 282 removed from filename. 2835. in the subdirectory .build-id: the first two hex digits of the build-id 284 as subdirectory, then the rest of the hex digits, with ".debug" appended. 285 See 286 https://fedoraproject.org/wiki/RolandMcGrath/BuildID#Find_files_by_build_ID 287 288For example, "/system/lib/base.apk!foo.so" with build id abcd1234, 289is looked for at: 290 2911. $PERFETTO_BINARY_PATH/system/lib/base.apk!foo.so 2922. $PERFETTO_BINARY_PATH/system/lib/foo.so 2933. $PERFETTO_BINARY_PATH/base.apk!foo.so 2944. $PERFETTO_BINARY_PATH/foo.so 2955. $PERFETTO_BINARY_PATH/.build-id/ab/cd1234.debug 296 297Alternatively, you can set the `PERFETTO_SYMBOLIZER_MODE` environment variable 298to `index`, and the symbolizer will recursively search the given directory for 299an ELF file with the given build id. This way, you will not have to worry 300about correct filenames. 301 302## Deobfuscation 303 304If your profile contains obfuscated Java methods (like `fsd.a`), you can 305provide a deobfuscation map to turn them back into human readable. 306To do so, use the `PERFETTO_PROGUARD_MAP` environment variable, using the 307format `packagename=filename[:packagename=filename...]`, e.g. 308`PERFETTO_PROGUARD_MAP=com.example.pkg1=foo.txt:com.example.pkg2=bar.txt`. 309All tools 310(traceconv, trace_processor_shell, the heap_profile script) support specifying 311the `PERFETTO_PROGUARD_MAP` as an environment variable. 312 313You can get a deobfuscation map for your trace using 314`tools/traceconv deobfuscate`. Then concatenate the resulting file to your 315trace to get a deobfuscated version of it. 316 317``` 318PERFETTO_PROGUARD_MAP=com.example.pkg tools/traceconv deobfuscate ${TRACE} > deobfuscation_map 319cat ${TRACE} deobfuscation_map > deobfuscated_trace 320``` 321 322## Troubleshooting 323 324### Buffer overrun 325 326If the rate of allocations is too high for heapprofd to keep up, the profiling 327session will end early due to a buffer overrun. If the buffer overrun is 328caused by a transient spike in allocations, increasing the shared memory buffer 329size (passing `--shmem-size` to `tools/heap_profile`) can resolve the issue. 330Otherwise the sampling interval can be increased (at the expense of lower 331accuracy in the resulting profile) by passing `--interval=16000` or higher. 332 333### Profile is empty 334 335Check whether your target process is eligible to be profiled by consulting 336[Target processes](#heapprofd-targets) above. 337 338Also check the [Known Issues](#known-issues). 339 340### Implausible callstacks 341 342If you see a callstack that seems to impossible from looking at the code, make 343sure no [DEDUPED frames](#deduped-frames) are involved. 344 345Also, if your code is linked using _Identical Code Folding_ 346(ICF), i.e. passing `-Wl,--icf=...` to the linker, most trivial functions, often 347constructors and destructors, can be aliased to binary-equivalent operators 348of completely unrelated classes. 349 350### Symbolization: Could not find library 351 352When symbolizing a profile, you might come across messages like this: 353 354```bash 355Could not find /data/app/invalid.app-wFgo3GRaod02wSvPZQ==/lib/arm64/somelib.so 356(Build ID: 44b7138abd5957b8d0a56ce86216d478). 357``` 358 359Check whether your library (in this example somelib.so) exists in 360`PERFETTO_BINARY_PATH`. Then compare the Build ID to the one in your 361symbol file, which you can get by running 362`readelf -n /path/in/binary/path/somelib.so`. If it does not match, the 363symbolized file has a different version than the one on device, and cannot 364be used for symbolization. 365If it does, try moving somelib.so to the root of `PERFETTO_BINARY_PATH` and 366try again. 367 368### Only one frame shown 369If you only see a single frame for functions in a specific library, make sure 370that the library has unwind information. We need one of 371 372* `.gnu_debugdata` 373* `.eh_frame` (+ preferably `.eh_frame_hdr`) 374* `.debug_frame`. 375 376Frame-pointer unwinding is *not supported*. 377 378To check if an ELF file has any of those, run 379 380```console 381$ readelf -S file.so | grep "gnu_debugdata\|eh_frame\|debug_frame" 382 [12] .eh_frame_hdr PROGBITS 000000000000c2b0 0000c2b0 383 [13] .eh_frame PROGBITS 0000000000011000 00011000 384 [24] .gnu_debugdata PROGBITS 0000000000000000 000f7292 385``` 386 387If this does not show one or more of the sections, change your build system 388to not strip them. 389 390## (non-Android) Linux support 391 392NOTE: Do not use this for production purposes. 393 394You can use a standalone library to profile memory allocations on Linux. 395First [build Perfetto](/docs/contributing/build-instructions.md). You only need 396to do this once. 397 398``` 399tools/build_all_configs.py 400ninja -C out/linux_clang_release 401``` 402 403Then, run traced 404 405``` 406out/linux_clang_release/traced 407``` 408 409Start the profile (e.g. targeting trace_processor_shell) 410 411``` 412tools/heap_profile -n trace_processor_shell --print-config | \ 413out/linux_clang_release/perfetto \ 414 -c - --txt \ 415 -o ~/heapprofd-trace 416``` 417 418Finally, run your target (e.g. trace_processor_shell) with LD_PRELOAD 419 420``` 421LD_PRELOAD=out/linux_clang_release/libheapprofd_glibc_preload.so out/linux_clang_release/trace_processor_shell <trace> 422``` 423 424Then, Ctrl-C the Perfetto invocation and upload ~/heapprofd-trace to the 425[Perfetto UI](https://ui.perfetto.dev). 426 427NOTE: by default, heapprofd lazily initalizes to avoid blocking your program's 428main thread. However, if your program makes memory allocations on startup, 429these can be missed. To avoid this from happening, set the enironment variable 430`PERFETTO_HEAPPROFD_BLOCKING_INIT=1`; on the first malloc, your program will 431be blocked until heapprofd initializes fully but means every allocation will 432be correctly tracked. 433 434## Known Issues 435 436### {#known-issues-android11} Android 11 437 438* 32-bit programs cannot be targeted on 64-bit devices. 439* Setting `sampling_interval_bytes` to 0 crashes the target process. 440 This is an invalid config that should be rejected instead. 441* For startup profiles, some frame names might be missing. This will be 442 resolved in Android 12. 443* `Failed to send control socket byte.` is displayed in logcat at the end of 444 every profile. This is benign. 445* The object count may be incorrect in `dump_at_max` profiles. 446* Choosing a low shared memory buffer size and `block_client` mode might 447 lock up the target process. 448 449### {#known-issues-android10} Android 10 450* Function names in libraries with load bias might be incorrect. Use 451 [offline symbolization](#symbolization) to resolve this issue. 452* For startup profiles, some frame names might be missing. This will be 453 resolved in Android 12. 454* 32-bit programs cannot be targeted on 64-bit devices. 455* x86 / x86_64 platforms are not supported. This includes the Android 456_Cuttlefish_. 457 emulator. 458* On ARM32, the bottom-most frame is always `ERROR 2`. This is harmless and 459 the callstacks are still complete. 460* If heapprofd is run standalone (by running `heapprofd` in a root shell, rather 461 than through init), `/dev/socket/heapprofd` get assigned an incorrect SELinux 462 domain. You will not be able to profile any processes unless you disable 463 SELinux enforcement. 464 Run `restorecon /dev/socket/heapprofd` in a root shell to resolve. 465* Using `vfork(2)` or `clone(2)` with `CLONE_VM` and allocating / freeing 466 memory in the child process will prematurely end the profile. 467 `java.lang.Runtime.exec` does this, calling it will prematurely end 468 the profile. Note that this is in violation of the POSIX standard. 469* Setting `sampling_interval_bytes` to 0 crashes the target process. 470 This is an invalid config that should be rejected instead. 471* `Failed to send control socket byte.` is displayed in logcat at the end of 472 every profile. This is benign. 473* The object count may be incorrect in `dump_at_max` profiles. 474* Choosing a low shared memory buffer size and `block_client` mode might 475 lock up the target process. 476 477## Heapprofd vs malloc_info() vs RSS 478 479When using heapprofd and interpreting results, it is important to know the 480precise meaning of the different memory metrics that can be obtained from the 481operating system. 482 483**heapprofd** gives you the number of bytes the target program 484requested from the default C/C++ allocator. If you are profiling a Java app from 485startup, allocations that happen early in the application's initialization will 486not be visible to heapprofd. Native services that do not fork from the Zygote 487are not affected by this. 488 489**malloc\_info** is a libc function that gives you information about the 490allocator. This can be triggered on userdebug builds by using 491`am dumpheap -m <PID> /data/local/tmp/heap.txt`. This will in general be more 492than the memory seen by heapprofd, depending on the allocator not all memory 493is immediately freed. In particular, jemalloc retains some freed memory in 494thread caches. 495 496**Heap RSS** is the amount of memory requested from the operating system by the 497allocator. This is larger than the previous two numbers because memory can only 498be obtained in page size chunks, and fragmentation causes some of that memory to 499be wasted. This can be obtained by running `adb shell dumpsys meminfo <PID>` and 500looking at the "Private Dirty" column. 501RSS can also end up being smaller than the other two if the device kernel uses 502memory compression (ZRAM, enabled by default on recent versions of android) and 503the memory of the process get swapped out onto ZRAM. 504 505| | heapprofd | malloc\_info | RSS | 506|---------------------|:-----------------:|:------------:|:---:| 507| from native startup | x | x | x | 508| after zygote init | x | x | x | 509| before zygote init | | x | x | 510| thread caches | | x | x | 511| fragmentation | | | x | 512 513If you observe high RSS or malloc\_info metrics but heapprofd does not match, 514you might be hitting some pathological fragmentation problem in the allocator. 515 516## Convert to pprof 517 518You can use [traceconv](/docs/quickstart/traceconv.md) to convert the heap dumps 519in a trace into the [pprof](https://github.com/google/pprof) format. These can 520then be viewed using the pprof CLI or a UI (e.g. Speedscope, or Google-internal 521pprof/). 522 523```bash 524tools/traceconv profile /tmp/profile 525``` 526 527This will create a directory in `/tmp/` containing the heap dumps. Run: 528 529```bash 530gzip /tmp/heap_profile-XXXXXX/*.pb 531``` 532 533to get gzipped protos, which tools handling pprof profile protos expect. 534 535## {#heapprofd-example-queries} Example SQL Queries 536 537We can get the callstacks that allocated using an SQL Query in the 538Trace Processor. For each frame, we get one row for the number of allocated 539bytes, where `count` and `size` is positive, and, if any of them were already 540freed, another line with negative `count` and `size`. The sum of those gets us 541the `space` view. 542 543```sql 544select a.callsite_id, a.ts, a.upid, f.name, f.rel_pc, m.build_id, m.name as mapping_name, 545 sum(a.size) as space_size, sum(a.count) as space_count 546 from heap_profile_allocation a join 547 stack_profile_callsite c ON (a.callsite_id = c.id) join 548 stack_profile_frame f ON (c.frame_id = f.id) join 549 stack_profile_mapping m ON (f.mapping = m.id) 550 group by 1, 2, 3, 4, 5, 6, 7 order by space_size desc; 551``` 552 553| callsite_id | ts | upid | name | rel_pc | build_id | mapping_name | space_size | space_count | 554|-------------|----|------|-------|-----------|------|--------|----------|------| 555|6660|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |106496|4| 556|192 |5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26624 |1| 557|1421|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26624 |1| 558|1537|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26624 |1| 559|8843|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |26424 |1| 560|8618|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |24576 |4| 561|3750|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |12288 |1| 562|2820|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |8192 |2| 563|3788|5|1| malloc |244716| 8126fd.. | /apex/com.android.runtime/lib64/bionic/libc.so |8192 |2| 564 565We can see all the functions are "malloc" and "realloc", which is not terribly 566informative. Usually we are interested in the _cumulative_ bytes allocated in 567a function (otherwise, we will always only see malloc / realloc). Chasing the 568parent_id of a callsite (not shown in this table) recursively is very hard in 569SQL. 570 571There is an **experimental** table that surfaces this information. The **API is 572subject to change**. 573 574```sql 575select name, map_name, cumulative_size 576 from experimental_flamegraph 577 where ts = 8300973884377 578 and upid = 1 579 and profile_type = 'native' 580 order by abs(cumulative_size) desc; 581``` 582 583| name | map_name | cumulative_size | 584|------|----------|----------------| 585|__start_thread|/apex/com.android.runtime/lib64/bionic/libc.so|392608| 586|_ZL15__pthread_startPv|/apex/com.android.runtime/lib64/bionic/libc.so|392608| 587|_ZN13thread_data_t10trampolineEPKS|/system/lib64/libutils.so|199496| 588|_ZN7android14AndroidRuntime15javaThreadShellEPv|/system/lib64/libandroid_runtime.so|199496| 589|_ZN7android6Thread11_threadLoopEPv|/system/lib64/libutils.so|199496| 590|_ZN3art6Thread14CreateCallbackEPv|/apex/com.android.art/lib64/libart.so|193112| 591|_ZN3art35InvokeVirtualOrInterface...|/apex/com.android.art/lib64/libart.so|193112| 592|_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc|/apex/com.android.art/lib64/libart.so|193112| 593|art_quick_invoke_stub|/apex/com.android.art/lib64/libart.so|193112| 594