xref: /aosp_15_r20/external/leakcanary2/docs/snippets/bugsnag-uploader.md (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)
1*d9e8da70SAndroid Build Coastguard Worker```kotlin
2*d9e8da70SAndroid Build Coastguard Workerimport android.app.Application
3*d9e8da70SAndroid Build Coastguard Workerimport com.bugsnag.android.Bugsnag
4*d9e8da70SAndroid Build Coastguard Workerimport com.bugsnag.android.Configuration
5*d9e8da70SAndroid Build Coastguard Workerimport com.bugsnag.android.ErrorTypes
6*d9e8da70SAndroid Build Coastguard Workerimport com.bugsnag.android.Event
7*d9e8da70SAndroid Build Coastguard Workerimport com.bugsnag.android.ThreadSendPolicy
8*d9e8da70SAndroid Build Coastguard Workerimport shark.HeapAnalysis
9*d9e8da70SAndroid Build Coastguard Workerimport shark.HeapAnalysisFailure
10*d9e8da70SAndroid Build Coastguard Workerimport shark.HeapAnalysisSuccess
11*d9e8da70SAndroid Build Coastguard Workerimport shark.Leak
12*d9e8da70SAndroid Build Coastguard Workerimport shark.LeakTrace
13*d9e8da70SAndroid Build Coastguard Workerimport shark.LeakTraceReference
14*d9e8da70SAndroid Build Coastguard Workerimport shark.LibraryLeak
15*d9e8da70SAndroid Build Coastguard Worker
16*d9e8da70SAndroid Build Coastguard Workerclass BugsnagLeakUploader(applicationContext: Application) {
17*d9e8da70SAndroid Build Coastguard Worker
18*d9e8da70SAndroid Build Coastguard Worker  private val bugsnagClient = Bugsnag.start(
19*d9e8da70SAndroid Build Coastguard Worker    applicationContext,
20*d9e8da70SAndroid Build Coastguard Worker    Configuration("YOUR_BUGSNAG_API_KEY").apply {
21*d9e8da70SAndroid Build Coastguard Worker      enabledErrorTypes = ErrorTypes(
22*d9e8da70SAndroid Build Coastguard Worker        anrs = false,
23*d9e8da70SAndroid Build Coastguard Worker        ndkCrashes = false,
24*d9e8da70SAndroid Build Coastguard Worker        unhandledExceptions = false,
25*d9e8da70SAndroid Build Coastguard Worker        unhandledRejections = false
26*d9e8da70SAndroid Build Coastguard Worker      )
27*d9e8da70SAndroid Build Coastguard Worker      sendThreads = ThreadSendPolicy.NEVER
28*d9e8da70SAndroid Build Coastguard Worker    }
29*d9e8da70SAndroid Build Coastguard Worker  )
30*d9e8da70SAndroid Build Coastguard Worker
31*d9e8da70SAndroid Build Coastguard Worker  fun upload(heapAnalysis: HeapAnalysis) {
32*d9e8da70SAndroid Build Coastguard Worker    when (heapAnalysis) {
33*d9e8da70SAndroid Build Coastguard Worker      is HeapAnalysisSuccess -> {
34*d9e8da70SAndroid Build Coastguard Worker        val allLeakTraces = heapAnalysis
35*d9e8da70SAndroid Build Coastguard Worker          .allLeaks
36*d9e8da70SAndroid Build Coastguard Worker          .toList()
37*d9e8da70SAndroid Build Coastguard Worker          .flatMap { leak ->
38*d9e8da70SAndroid Build Coastguard Worker            leak.leakTraces.map { leakTrace -> leak to leakTrace }
39*d9e8da70SAndroid Build Coastguard Worker          }
40*d9e8da70SAndroid Build Coastguard Worker        if (allLeakTraces.isEmpty()) {
41*d9e8da70SAndroid Build Coastguard Worker          // Track how often we perform a heap analysis that yields no result.
42*d9e8da70SAndroid Build Coastguard Worker          bugsnagClient.notify(NoLeakException()) { event ->
43*d9e8da70SAndroid Build Coastguard Worker            event.addHeapAnalysis(heapAnalysis)
44*d9e8da70SAndroid Build Coastguard Worker            true
45*d9e8da70SAndroid Build Coastguard Worker          }
46*d9e8da70SAndroid Build Coastguard Worker        } else {
47*d9e8da70SAndroid Build Coastguard Worker          allLeakTraces.forEach { (leak, leakTrace) ->
48*d9e8da70SAndroid Build Coastguard Worker            val message = "Memory leak: ${leak.shortDescription}. See LEAK tab."
49*d9e8da70SAndroid Build Coastguard Worker            val exception = leakTrace.asFakeException(message)
50*d9e8da70SAndroid Build Coastguard Worker            bugsnagClient.notify(exception) { event ->
51*d9e8da70SAndroid Build Coastguard Worker              event.addHeapAnalysis(heapAnalysis)
52*d9e8da70SAndroid Build Coastguard Worker              event.addLeak(leak)
53*d9e8da70SAndroid Build Coastguard Worker              event.addLeakTrace(leakTrace)
54*d9e8da70SAndroid Build Coastguard Worker              event.groupingHash = leak.signature
55*d9e8da70SAndroid Build Coastguard Worker              true
56*d9e8da70SAndroid Build Coastguard Worker            }
57*d9e8da70SAndroid Build Coastguard Worker          }
58*d9e8da70SAndroid Build Coastguard Worker        }
59*d9e8da70SAndroid Build Coastguard Worker      }
60*d9e8da70SAndroid Build Coastguard Worker      is HeapAnalysisFailure -> {
61*d9e8da70SAndroid Build Coastguard Worker        // Please file any reported failure to
62*d9e8da70SAndroid Build Coastguard Worker        // https://github.com/square/leakcanary/issues
63*d9e8da70SAndroid Build Coastguard Worker        bugsnagClient.notify(heapAnalysis.exception)
64*d9e8da70SAndroid Build Coastguard Worker      }
65*d9e8da70SAndroid Build Coastguard Worker    }
66*d9e8da70SAndroid Build Coastguard Worker  }
67*d9e8da70SAndroid Build Coastguard Worker
68*d9e8da70SAndroid Build Coastguard Worker  class NoLeakException : RuntimeException()
69*d9e8da70SAndroid Build Coastguard Worker
70*d9e8da70SAndroid Build Coastguard Worker  private fun Event.addHeapAnalysis(heapAnalysis: HeapAnalysisSuccess) {
71*d9e8da70SAndroid Build Coastguard Worker    addMetadata("Leak", "heapDumpPath", heapAnalysis.heapDumpFile.absolutePath)
72*d9e8da70SAndroid Build Coastguard Worker    heapAnalysis.metadata.forEach { (key, value) ->
73*d9e8da70SAndroid Build Coastguard Worker      addMetadata("Leak", key, value)
74*d9e8da70SAndroid Build Coastguard Worker    }
75*d9e8da70SAndroid Build Coastguard Worker    addMetadata("Leak", "analysisDurationMs", heapAnalysis.analysisDurationMillis)
76*d9e8da70SAndroid Build Coastguard Worker  }
77*d9e8da70SAndroid Build Coastguard Worker
78*d9e8da70SAndroid Build Coastguard Worker  private fun Event.addLeak(leak: Leak) {
79*d9e8da70SAndroid Build Coastguard Worker    addMetadata("Leak", "libraryLeak", leak is LibraryLeak)
80*d9e8da70SAndroid Build Coastguard Worker    if (leak is LibraryLeak) {
81*d9e8da70SAndroid Build Coastguard Worker      addMetadata("Leak", "libraryLeakPattern", leak.pattern.toString())
82*d9e8da70SAndroid Build Coastguard Worker      addMetadata("Leak", "libraryLeakDescription", leak.description)
83*d9e8da70SAndroid Build Coastguard Worker    }
84*d9e8da70SAndroid Build Coastguard Worker  }
85*d9e8da70SAndroid Build Coastguard Worker
86*d9e8da70SAndroid Build Coastguard Worker  private fun Event.addLeakTrace(leakTrace: LeakTrace) {
87*d9e8da70SAndroid Build Coastguard Worker    addMetadata("Leak", "retainedHeapByteSize", leakTrace.retainedHeapByteSize)
88*d9e8da70SAndroid Build Coastguard Worker    addMetadata("Leak", "signature", leakTrace.signature)
89*d9e8da70SAndroid Build Coastguard Worker    addMetadata("Leak", "leakTrace", leakTrace.toString())
90*d9e8da70SAndroid Build Coastguard Worker  }
91*d9e8da70SAndroid Build Coastguard Worker
92*d9e8da70SAndroid Build Coastguard Worker  private fun LeakTrace.asFakeException(message: String): RuntimeException {
93*d9e8da70SAndroid Build Coastguard Worker    val exception = RuntimeException(message)
94*d9e8da70SAndroid Build Coastguard Worker    val stackTrace = mutableListOf<StackTraceElement>()
95*d9e8da70SAndroid Build Coastguard Worker    stackTrace.add(StackTraceElement("GcRoot", gcRootType.name, "GcRoot.kt", 42))
96*d9e8da70SAndroid Build Coastguard Worker    for (cause in referencePath) {
97*d9e8da70SAndroid Build Coastguard Worker      stackTrace.add(buildStackTraceElement(cause))
98*d9e8da70SAndroid Build Coastguard Worker    }
99*d9e8da70SAndroid Build Coastguard Worker    exception.stackTrace = stackTrace.toTypedArray()
100*d9e8da70SAndroid Build Coastguard Worker    return exception
101*d9e8da70SAndroid Build Coastguard Worker  }
102*d9e8da70SAndroid Build Coastguard Worker
103*d9e8da70SAndroid Build Coastguard Worker  private fun buildStackTraceElement(reference: LeakTraceReference): StackTraceElement {
104*d9e8da70SAndroid Build Coastguard Worker    val file = reference.owningClassName.substringAfterLast(".") + ".kt"
105*d9e8da70SAndroid Build Coastguard Worker    return StackTraceElement(reference.owningClassName, reference.referenceDisplayName, file, 42)
106*d9e8da70SAndroid Build Coastguard Worker  }
107*d9e8da70SAndroid Build Coastguard Worker}
108*d9e8da70SAndroid Build Coastguard Worker```
109