xref: /aosp_15_r20/external/leakcanary2/docs/shark.md (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)
1*d9e8da70SAndroid Build Coastguard Worker# Shark ��
2*d9e8da70SAndroid Build Coastguard Worker
3*d9e8da70SAndroid Build Coastguard Worker<!-- Made with http://patorjk.com/text-color-fader/ -->
4*d9e8da70SAndroid Build Coastguard Worker**Shark**: **<span style="color:#c757bc;">S</span><span style="color:#c858b7;">m</span><span style="color:#ca5ab2;">a</span><span style="color:#cb5bad;">r</span><span style="color:#cc5ca9;">t</span><span style="color:#ce5ea4;"> </span><span style="color:#cf5f9f;">H</span><span style="color:#d0609a;">e</span><span style="color:#d26295;">a</span><span style="color:#d36390;">p</span><span style="color:#d4658c;"> </span><span style="color:#d66687;">A</span><span style="color:#d76782;">n</span><span style="color:#d8697d;">a</span><span style="color:#da6a78;">l</span><span style="color:#db6b73;">y</span><span style="color:#dc6d6f;">s</span><span style="color:#de6e6a;">i</span><span style="color:#df6f65;">s</span><span style="color:#e07160;"> </span><span style="color:#e1725b;">R</span><span style="color:#e37356;">e</span><span style="color:#e47552;">p</span><span style="color:#e5764d;">o</span><span style="color:#e77748;">r</span><span style="color:#e87943;">t</span><span style="color:#e97a3e;">s</span><span style="color:#eb7b39;"> </span><span style="color:#ec7d35;">f</span><span style="color:#ed7e30;">o</span><span style="color:#ef802b;">r</span><span style="color:#f08126;"> </span><span style="color:#f18221;">K</span><span style="color:#f3841c;">o</span><span style="color:#f48518;">t</span><span style="color:#f58613;">l</span><span style="color:#f7880e;">i</span><span style="color:#f88909;">n</span>**
5*d9e8da70SAndroid Build Coastguard Worker
6*d9e8da70SAndroid Build Coastguard Worker<p align="center">
7*d9e8da70SAndroid Build Coastguard Worker<img src="../images/shark.png" />
8*d9e8da70SAndroid Build Coastguard Worker</p>
9*d9e8da70SAndroid Build Coastguard Worker
10*d9e8da70SAndroid Build Coastguard WorkerShark is the heap analyzer that powers LeakCanary 2. It's a Kotlin standalone heap analysis library that runs at **high speed** with a **low memory footprint**.
11*d9e8da70SAndroid Build Coastguard Worker
12*d9e8da70SAndroid Build Coastguard WorkerShark is released in layers:
13*d9e8da70SAndroid Build Coastguard Worker
14*d9e8da70SAndroid Build Coastguard Worker1. **Shark Hprof**: Read and write records in hprof files.
15*d9e8da70SAndroid Build Coastguard Worker2. **Shark Graph**: Navigate the heap object graph.
16*d9e8da70SAndroid Build Coastguard Worker3. **Shark**: Generate heap analysis reports.
17*d9e8da70SAndroid Build Coastguard Worker4. **Shark Android**: Android heuristics to generate tailored heap analysis reports.
18*d9e8da70SAndroid Build Coastguard Worker5. **Shark CLI**: Analyze the heap of debuggable apps installed on an Android device connected to your desktop. The output is similar to the output of LeakCanary, except you don't have to add the LeakCanary dependency to your app.
19*d9e8da70SAndroid Build Coastguard Worker6. **LeakCanary**: Builds on top. It automatically watches destroyed activities and fragments, triggers a heap dump, runs Shark Android and then displays the result.
20*d9e8da70SAndroid Build Coastguard Worker
21*d9e8da70SAndroid Build Coastguard WorkerA few more things:
22*d9e8da70SAndroid Build Coastguard Worker
23*d9e8da70SAndroid Build Coastguard Worker* Shark is built on top of Okio. Okio makes it easy to parse heap dumps efficiently.
24*d9e8da70SAndroid Build Coastguard Worker* Shark is a 100% Kotlin library, and Kotlin is essential to its design, because Shark relies heavily on sealed classes and sequences to save memory.
25*d9e8da70SAndroid Build Coastguard Worker* Shark has the unique ability to help narrow down the cause of memory leaks through platform specific [heuristics](fundamentals-fixing-a-memory-leak.md#2-narrow-down-the-suspect-references).
26*d9e8da70SAndroid Build Coastguard Worker* Shark is heavily tested (80% test coverage).
27*d9e8da70SAndroid Build Coastguard Worker* Shark can run in both Java and Android VMs, with no other dependency than Okio and Kotlin.
28*d9e8da70SAndroid Build Coastguard Worker* Shark can analyze both Java and Android VM hprof files.
29*d9e8da70SAndroid Build Coastguard Worker* Shark can deobfuscate hprof records if it has access to obfuscation mapping file.
30*d9e8da70SAndroid Build Coastguard Worker
31*d9e8da70SAndroid Build Coastguard Worker## Shark CLI
32*d9e8da70SAndroid Build Coastguard Worker
33*d9e8da70SAndroid Build Coastguard WorkerThe Shark Command Line Interface (CLI) enables you to analyze heaps directly from your computer. It can dump the heap of an app installed on a connected Android device, analyze it, and even strip a heap dump of any sensitive data (e.g. PII, passwords or encryption keys) which is useful when sharing a heap dump.
34*d9e8da70SAndroid Build Coastguard Worker
35*d9e8da70SAndroid Build Coastguard WorkerInstall it via [Homebrew](https://brew.sh/):
36*d9e8da70SAndroid Build Coastguard Worker
37*d9e8da70SAndroid Build Coastguard Worker```bash
38*d9e8da70SAndroid Build Coastguard Workerbrew install leakcanary-shark
39*d9e8da70SAndroid Build Coastguard Worker```
40*d9e8da70SAndroid Build Coastguard Worker
41*d9e8da70SAndroid Build Coastguard WorkerYou can also download it [here](https://github.com/square/leakcanary/releases/download/v{{ leak_canary.release }}/shark-cli-{{ leak_canary.release }}.zip).
42*d9e8da70SAndroid Build Coastguard Worker
43*d9e8da70SAndroid Build Coastguard WorkerYou can then look for leaks in apps on any connected device, for example:
44*d9e8da70SAndroid Build Coastguard Worker
45*d9e8da70SAndroid Build Coastguard Worker```
46*d9e8da70SAndroid Build Coastguard Worker$ shark-cli --device emulator-5554 --process com.example.app.debug analyze
47*d9e8da70SAndroid Build Coastguard Worker```
48*d9e8da70SAndroid Build Coastguard Worker
49*d9e8da70SAndroid Build Coastguard Worker!!! info
50*d9e8da70SAndroid Build Coastguard Worker    `shark-cli` works with all debuggable apps, even if they don't include the `leakcanary-android` dependency.
51*d9e8da70SAndroid Build Coastguard Worker
52*d9e8da70SAndroid Build Coastguard WorkerRun `shark-cli` to see usage instructions:
53*d9e8da70SAndroid Build Coastguard Worker
54*d9e8da70SAndroid Build Coastguard Worker```
55*d9e8da70SAndroid Build Coastguard Worker$ shark-cli
56*d9e8da70SAndroid Build Coastguard Worker
57*d9e8da70SAndroid Build Coastguard WorkerUsage: shark-cli [OPTIONS] COMMAND [ARGS]...
58*d9e8da70SAndroid Build Coastguard Worker
59*d9e8da70SAndroid Build Coastguard Worker                   ^`.                 .=""=.
60*d9e8da70SAndroid Build Coastguard Worker   ^_              \  \               / _  _ \
61*d9e8da70SAndroid Build Coastguard Worker   \ \             {   \             |  d  b  |
62*d9e8da70SAndroid Build Coastguard Worker   {  \           /     `~~~--__     \   /\   /
63*d9e8da70SAndroid Build Coastguard Worker   {   \___----~~'              `~~-_/'-=\/=-'\,
64*d9e8da70SAndroid Build Coastguard Worker    \                         /// a  `~.      \ \
65*d9e8da70SAndroid Build Coastguard Worker    / /~~~~-, ,__.    ,      ///  __,,,,)      \ |
66*d9e8da70SAndroid Build Coastguard Worker    \/      \/    `~~~;   ,---~~-_`/ \        / \/
67*d9e8da70SAndroid Build Coastguard Worker                     /   /            '.    .'
68*d9e8da70SAndroid Build Coastguard Worker                    '._.'             _|`~~`|_
69*d9e8da70SAndroid Build Coastguard Worker                                      /|\  /|\
70*d9e8da70SAndroid Build Coastguard Worker
71*d9e8da70SAndroid Build Coastguard WorkerOptions:
72*d9e8da70SAndroid Build Coastguard Worker  -p, --process TEXT              Full or partial name of a process, e.g.
73*d9e8da70SAndroid Build Coastguard Worker                                  "example" would match "com.example.app"
74*d9e8da70SAndroid Build Coastguard Worker  -d, --device ID                 device/emulator id
75*d9e8da70SAndroid Build Coastguard Worker  -m, --obfuscation-mapping PATH  path to obfuscation mapping file
76*d9e8da70SAndroid Build Coastguard Worker  --verbose / --no-verbose        provide additional details as to what
77*d9e8da70SAndroid Build Coastguard Worker                                  shark-cli is doing
78*d9e8da70SAndroid Build Coastguard Worker  -h, --hprof FILE                path to a .hprof file
79*d9e8da70SAndroid Build Coastguard Worker  --help                          Show this message and exit
80*d9e8da70SAndroid Build Coastguard Worker
81*d9e8da70SAndroid Build Coastguard WorkerCommands:
82*d9e8da70SAndroid Build Coastguard Worker  interactive   Explore a heap dump.
83*d9e8da70SAndroid Build Coastguard Worker  analyze       Analyze a heap dump.
84*d9e8da70SAndroid Build Coastguard Worker  dump-process  Dump the heap and pull the hprof file.
85*d9e8da70SAndroid Build Coastguard Worker  strip-hprof   Replace all primitive arrays from the provided heap dump with
86*d9e8da70SAndroid Build Coastguard Worker                arrays of zeroes and generate a new "-stripped.hprof" file.
87*d9e8da70SAndroid Build Coastguard Worker```
88*d9e8da70SAndroid Build Coastguard Worker
89*d9e8da70SAndroid Build Coastguard Worker
90*d9e8da70SAndroid Build Coastguard Worker## Shark code examples
91*d9e8da70SAndroid Build Coastguard Worker
92*d9e8da70SAndroid Build Coastguard Worker### Reading records in a hprof file
93*d9e8da70SAndroid Build Coastguard Worker
94*d9e8da70SAndroid Build Coastguard Worker```groovy
95*d9e8da70SAndroid Build Coastguard Workerdependencies {
96*d9e8da70SAndroid Build Coastguard Worker  implementation 'com.squareup.leakcanary:shark-hprof:$sharkVersion'
97*d9e8da70SAndroid Build Coastguard Worker}
98*d9e8da70SAndroid Build Coastguard Worker```
99*d9e8da70SAndroid Build Coastguard Worker
100*d9e8da70SAndroid Build Coastguard Worker```kotlin
101*d9e8da70SAndroid Build Coastguard Worker// Prints all class and field names
102*d9e8da70SAndroid Build Coastguard WorkerHprof.open(heapDumpFile)
103*d9e8da70SAndroid Build Coastguard Worker    .use { hprof ->
104*d9e8da70SAndroid Build Coastguard Worker      hprof.reader.readHprofRecords(
105*d9e8da70SAndroid Build Coastguard Worker          recordTypes = setOf(StringRecord::class),
106*d9e8da70SAndroid Build Coastguard Worker          listener = OnHprofRecordListener { position, record ->
107*d9e8da70SAndroid Build Coastguard Worker            println((record as StringRecord).string)
108*d9e8da70SAndroid Build Coastguard Worker          })
109*d9e8da70SAndroid Build Coastguard Worker    }
110*d9e8da70SAndroid Build Coastguard Worker```
111*d9e8da70SAndroid Build Coastguard Worker
112*d9e8da70SAndroid Build Coastguard Worker### Navigating the heap object graph
113*d9e8da70SAndroid Build Coastguard Worker
114*d9e8da70SAndroid Build Coastguard Worker```groovy
115*d9e8da70SAndroid Build Coastguard Workerdependencies {
116*d9e8da70SAndroid Build Coastguard Worker  implementation 'com.squareup.leakcanary:shark-graph:$sharkVersion'
117*d9e8da70SAndroid Build Coastguard Worker}
118*d9e8da70SAndroid Build Coastguard Worker```
119*d9e8da70SAndroid Build Coastguard Worker
120*d9e8da70SAndroid Build Coastguard Worker```kotlin
121*d9e8da70SAndroid Build Coastguard Worker// Prints all thread names
122*d9e8da70SAndroid Build Coastguard WorkerHprof.open(heapDumpFile)
123*d9e8da70SAndroid Build Coastguard Worker    .use { hprof ->
124*d9e8da70SAndroid Build Coastguard Worker      val heapGraph = HprofHeapGraph.indexHprof(hprof)
125*d9e8da70SAndroid Build Coastguard Worker      val threadClass = heapGraph.findClassByName("java.lang.Thread")!!
126*d9e8da70SAndroid Build Coastguard Worker      val threadNames: Sequence<String> = threadClass.instances.map { instance ->
127*d9e8da70SAndroid Build Coastguard Worker        val nameField = instance["java.lang.Thread", "name"]!!
128*d9e8da70SAndroid Build Coastguard Worker        nameField.value.readAsJavaString()!!
129*d9e8da70SAndroid Build Coastguard Worker      }
130*d9e8da70SAndroid Build Coastguard Worker      threadNames.forEach { println(it) }
131*d9e8da70SAndroid Build Coastguard Worker    }
132*d9e8da70SAndroid Build Coastguard Worker```
133*d9e8da70SAndroid Build Coastguard Worker
134*d9e8da70SAndroid Build Coastguard Worker### Generating a heap analysis report
135*d9e8da70SAndroid Build Coastguard Worker
136*d9e8da70SAndroid Build Coastguard Worker```groovy
137*d9e8da70SAndroid Build Coastguard Workerdependencies {
138*d9e8da70SAndroid Build Coastguard Worker  implementation 'com.squareup.leakcanary:shark:$sharkVersion'
139*d9e8da70SAndroid Build Coastguard Worker}
140*d9e8da70SAndroid Build Coastguard Worker```
141*d9e8da70SAndroid Build Coastguard Worker
142*d9e8da70SAndroid Build Coastguard Worker```kotlin
143*d9e8da70SAndroid Build Coastguard Worker// Marks any instance of com.example.ThingWithLifecycle with
144*d9e8da70SAndroid Build Coastguard Worker// ThingWithLifecycle.destroyed=true as leaking
145*d9e8da70SAndroid Build Coastguard Workerval leakingObjectFilter = object : LeakingObjectFilter {
146*d9e8da70SAndroid Build Coastguard Worker  override fun isLeakingObject(heapObject: HeapObject): Boolean {
147*d9e8da70SAndroid Build Coastguard Worker    return if (heapObject instanceOf "com.example.ThingWithLifecycle") {
148*d9e8da70SAndroid Build Coastguard Worker      val instance = heapObject as HeapInstance
149*d9e8da70SAndroid Build Coastguard Worker      val destroyedField = instance["com.example.ThingWithLifecycle", "destroyed"]!!
150*d9e8da70SAndroid Build Coastguard Worker      destroyedField.value.asBoolean!!
151*d9e8da70SAndroid Build Coastguard Worker    } else false
152*d9e8da70SAndroid Build Coastguard Worker  }
153*d9e8da70SAndroid Build Coastguard Worker}
154*d9e8da70SAndroid Build Coastguard Worker
155*d9e8da70SAndroid Build Coastguard Workerval leakingObjectFinder = FilteringLeakingObjectFinder(listOf(leakingObjectFilter))
156*d9e8da70SAndroid Build Coastguard Worker
157*d9e8da70SAndroid Build Coastguard Workerval heapAnalysis = Hprof.open(heapDumpFile)
158*d9e8da70SAndroid Build Coastguard Worker    .use { hprof ->
159*d9e8da70SAndroid Build Coastguard Worker      val heapGraph = HprofHeapGraph.indexHprof(hprof)
160*d9e8da70SAndroid Build Coastguard Worker      val heapAnalyzer = HeapAnalyzer(AnalyzerProgressListener.NONE)
161*d9e8da70SAndroid Build Coastguard Worker      heapAnalyzer.analyze(
162*d9e8da70SAndroid Build Coastguard Worker          heapDumpFile = heapDumpFile,
163*d9e8da70SAndroid Build Coastguard Worker          graph = heapGraph,
164*d9e8da70SAndroid Build Coastguard Worker          leakingObjectFinder = leakingObjectFinder,
165*d9e8da70SAndroid Build Coastguard Worker      )
166*d9e8da70SAndroid Build Coastguard Worker    }
167*d9e8da70SAndroid Build Coastguard Workerprintln(analysis)
168*d9e8da70SAndroid Build Coastguard Worker```
169*d9e8da70SAndroid Build Coastguard Worker
170*d9e8da70SAndroid Build Coastguard Worker### Generating an Android heap analysis report
171*d9e8da70SAndroid Build Coastguard Worker
172*d9e8da70SAndroid Build Coastguard Worker```groovy
173*d9e8da70SAndroid Build Coastguard Workerdependencies {
174*d9e8da70SAndroid Build Coastguard Worker  implementation 'com.squareup.leakcanary:shark-android:$sharkVersion'
175*d9e8da70SAndroid Build Coastguard Worker}
176*d9e8da70SAndroid Build Coastguard Worker```
177*d9e8da70SAndroid Build Coastguard Worker
178*d9e8da70SAndroid Build Coastguard Worker
179*d9e8da70SAndroid Build Coastguard Worker```kotlin
180*d9e8da70SAndroid Build Coastguard Workerval heapAnalyzer = HeapAnalyzer(AnalyzerProgressListener.NONE)
181*d9e8da70SAndroid Build Coastguard Workerval analysis = heapAnalyzer.checkForLeaks(
182*d9e8da70SAndroid Build Coastguard Worker    heapDumpFile = heapDumpFile,
183*d9e8da70SAndroid Build Coastguard Worker    referenceMatchers = AndroidReferenceMatchers.appDefaults,
184*d9e8da70SAndroid Build Coastguard Worker    objectInspectors = AndroidObjectInspectors.appDefaults
185*d9e8da70SAndroid Build Coastguard Worker)
186*d9e8da70SAndroid Build Coastguard Workerprintln(analysis)
187*d9e8da70SAndroid Build Coastguard Worker```
188