1 package shark.internal 2 3 import shark.HeapGraph 4 import shark.HeapObject.HeapInstance 5 6 /** 7 * A [ReferenceReader] that first delegates expanding to [virtualRefReaders] in order until one 8 * matches (or none), and then always proceeds with [fieldRefReader]. This means any 9 * synthetic ref will be on the shortest path, but we still explore the entire data structure so 10 * that we correctly track which objects have been visited and correctly compute dominators and 11 * retained size. 12 */ 13 internal class ChainingInstanceReferenceReader( 14 private val virtualRefReaders: List<VirtualInstanceReferenceReader>, 15 private val fieldRefReader: FieldInstanceReferenceReader 16 ) : ReferenceReader<HeapInstance> { 17 readnull18 override fun read(source: HeapInstance): Sequence<Reference> { 19 val virtualRefs = expandVirtualRefs(source) 20 // Note: always forwarding to fieldRefReader means we may navigate the structure twice 21 // which increases IO reads. However this is a trade-of that allows virtualRef impls to 22 // focus on a subset of references and more importantly it means we still get a proper 23 // calculation of retained size as we don't skip any instance. 24 val fieldRefs = fieldRefReader.read(source) 25 return virtualRefs + fieldRefs 26 } 27 expandVirtualRefsnull28 private fun expandVirtualRefs(instance: HeapInstance): Sequence<Reference> { 29 for (expander in virtualRefReaders) { 30 if (expander.matches(instance)) { 31 return expander.read(instance) 32 } 33 } 34 return emptySequence() 35 } 36 37 /** 38 * Same as [ReferenceReader] but [read] is only invoked when [matches] returns 39 * true. [matches] should return false if this [VirtualInstanceReferenceReader] implementation isn't 40 * able to expand the provided instance, in which case [ChainingInstanceReferenceReader] will delegate 41 * to the next [VirtualInstanceReferenceReader] implementation. 42 */ 43 interface VirtualInstanceReferenceReader : ReferenceReader<HeapInstance> { matchesnull44 fun matches(instance: HeapInstance): Boolean 45 46 /** 47 * May create a new InstanceExpander, depending on what's in the heap graph. 48 * [OptionalFactory] implementations might return a different [ReferenceReader] 49 * depending on which version of a class is present in the heap dump, or they might return null if 50 * that class is missing. 51 */ 52 fun interface OptionalFactory { 53 fun create(graph: HeapGraph): VirtualInstanceReferenceReader? 54 } 55 } 56 } 57