RetryingHeapAnalyzer.kt
TLDR
The file RetryingHeapAnalyzer.kt
is a Kotlin file that contains the implementation of the RetryingHeapAnalyzer
class. This class is responsible for wrapping the InstrumentationHeapAnalyzer
and retrying the analysis once if it fails.
Methods
analyze
The analyze
method takes a heapDumpFile
as a parameter and performs the analysis using the heapAnalyzer
. It first creates a copy of the heapDumpFile
in case of failure followed by success. It then sleeps for 2 seconds to allow the hprof to flush to the file system. After that, it calls the analyze
method of heapAnalyzer
to perform the analysis. If the analysis fails, it logs the failure and retries after sleeping for 10 seconds. If the second analysis succeeds, it returns the analysis result with additional metadata. If the second analysis also fails, it returns the failure result. If the analysis succeeds, it deletes the heap dump copy and returns the analysis result.
Class
RetryingHeapAnalyzer
The RetryingHeapAnalyzer
class wraps the InstrumentationHeapAnalyzer
and provides a mechanism to retry the analysis once if it fails. It has one constructor that takes an instance of InstrumentationHeapAnalyzer
as a parameter. It contains the analyze
method that performs the analysis and handles the retry logic.
package leakcanary.internal
import android.os.SystemClock
import android.util.Log
import java.io.File
import shark.HeapAnalysis
import shark.HeapAnalysisFailure
import shark.HeapAnalysisSuccess
import shark.SharkLog
/**
* Wraps [InstrumentationHeapAnalyzer] and retries the analysis once if it fails.
*/
internal class RetryingHeapAnalyzer(
private val heapAnalyzer: InstrumentationHeapAnalyzer
) {
fun analyze(heapDumpFile: File): HeapAnalysis {
// A copy that will be used in case of failure followed by success, to see if the file has changed.
val heapDumpCopyFile = File(heapDumpFile.parent, "copy-${heapDumpFile.name}")
heapDumpFile.copyTo(heapDumpCopyFile)
// Giving an extra 2 seconds to flush the hprof to the file system. We've seen several cases
// of corrupted hprof files and assume this could be a timing issue.
SystemClock.sleep(2000)
val heapAnalysis = heapAnalyzer.analyze(heapDumpFile)
return if (heapAnalysis is HeapAnalysisFailure) {
// Experience has shown that trying again often just works. Not sure why.
SharkLog.d(heapAnalysis.exception) {
"Heap Analysis failed, retrying in 10s in case the heap dump was not fully baked yet. " +
"Copy of original heap dump available at ${heapDumpCopyFile.absolutePath}"
}
SystemClock.sleep(10000)
heapAnalyzer.analyze(heapDumpFile).let {
when (it) {
is HeapAnalysisSuccess -> it.copy(
metadata = it.metadata + mapOf(
"previousFailureHeapDumpCopy" to heapDumpCopyFile.absolutePath,
"previousFailureStacktrace" to Log.getStackTraceString(heapAnalysis.exception)
)
)
is HeapAnalysisFailure -> it
}
}
} else {
// We don't need the copy after all.
heapDumpCopyFile.delete()
heapAnalysis
}
}
}