Repository: square/leakcanary
File: leakcanary-android-instrumentation/src/main/java/leakcanary/InstrumentationLeakDetector.kt (Kotlin)

Code before:

    if (!refWatcher.hasRetainedObjects) {
      return NoAnalysis
    }

    // We're always reusing the same file since we only execute this once at a time.
    val heapDumpFile = File(context.filesDir, "instrumentation_tests_heapdump.hprof")

    val config = LeakCanary.config

    val heapDumpUptimeMillis = SystemClock.uptimeMillis()
    KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis

    val heapDumpDurationMillis: Long

    try {
      heapDumpDurationMillis = measureTimeMillis {
        Debug.dumpHprofData(heapDumpFile.absolutePath)
      }
    } catch (exception: Exception) {
      SharkLog.d(exception) { "Could not dump heap" }
      return AnalysisPerformed(
          HeapAnalysisFailure(
              heapDumpFile = heapDumpFile,
              createdAtTimeMillis = System.currentTimeMillis(),
              dumpDurationMillis = HeapAnalysis.DUMP_DURATION_UNKNOWN,
              analysisDurationMillis = 0,
              exception = HeapAnalysisException(exception)
          )
      )
    }

    refWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)

    val listener = shark.OnAnalysisProgressListener.NO_OP

    // 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 heapAnalyzer = HeapAnalyzer(listener)
    val fullHeapAnalysis = when (val heapAnalysis = heapAnalyzer.analyze(
        heapDumpFile = heapDumpFile,
        leakingObjectFinder = config.leakingObjectFinder,
        referenceMatchers = config.referenceMatchers,
        computeRetainedHeapSize = config.computeRetainedHeapSize,
        objectInspectors = config.objectInspectors
    )) {
      is HeapAnalysisSuccess -> heapAnalysis.copy(dumpDurationMillis = heapDumpDurationMillis)
      is HeapAnalysisFailure -> heapAnalysis.copy(dumpDurationMillis = heapDumpDurationMillis)
    }

Review comment (suggestion):
Technically we could actually provide the duration here (ie start of heap dump to exception)

Code after:
    if (!refWatcher.hasRetainedObjects) {
      return NoAnalysis
    }

    // We're always reusing the same file since we only execute this once at a time.
    val heapDumpFile = File(context.filesDir, "instrumentation_tests_heapdump.hprof")

    val config = LeakCanary.config

    val heapDumpUptimeMillis = SystemClock.uptimeMillis()
    KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis

    val heapDumpDurationMillis: Long

    try {
      Debug.dumpHprofData(heapDumpFile.absolutePath)
      heapDumpDurationMillis = SystemClock.uptimeMillis() - heapDumpUptimeMillis
    } catch (exception: Exception) {
      SharkLog.d(exception) { "Could not dump heap" }
      return AnalysisPerformed(
          HeapAnalysisFailure(
              heapDumpFile = heapDumpFile,
              createdAtTimeMillis = System.currentTimeMillis(),
              dumpDurationMillis = SystemClock.uptimeMillis() - heapDumpUptimeMillis,
              analysisDurationMillis = 0,
              exception = HeapAnalysisException(exception)
          )
      )
    }

    refWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)

    val listener = shark.OnAnalysisProgressListener.NO_OP

    // 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 heapAnalyzer = HeapAnalyzer(listener)
    val fullHeapAnalysis = when (val heapAnalysis = heapAnalyzer.analyze(
        heapDumpFile = heapDumpFile,
        leakingObjectFinder = config.leakingObjectFinder,
        referenceMatchers = config.referenceMatchers,
        computeRetainedHeapSize = config.computeRetainedHeapSize,
        objectInspectors = config.objectInspectors
    )) {
      is HeapAnalysisSuccess -> heapAnalysis.copy(dumpDurationMillis = heapDumpDurationMillis)
      is HeapAnalysisFailure -> heapAnalysis.copy(dumpDurationMillis = heapDumpDurationMillis)
    }
    return AnalysisPerformed(fullHeapAnalysis)
  }