4343import static com .squareup .leakcanary .HahaHelper .extendsThread ;
4444import static com .squareup .leakcanary .HahaHelper .fieldToString ;
4545import static com .squareup .leakcanary .HahaHelper .fieldValue ;
46+ import static com .squareup .leakcanary .HahaHelper .hasField ;
4647import static com .squareup .leakcanary .HahaHelper .threadName ;
4748import static com .squareup .leakcanary .LeakTraceElement .Holder .ARRAY ;
4849import static com .squareup .leakcanary .LeakTraceElement .Holder .CLASS ;
@@ -63,6 +64,36 @@ public HeapAnalyzer(ExcludedRefs excludedRefs) {
6364 this .excludedRefs = excludedRefs ;
6465 }
6566
67+ public List <TrackedReference > findTrackedReferences (File heapDumpFile ) {
68+ if (!heapDumpFile .exists ()) {
69+ throw new IllegalArgumentException ("File does not exist: " + heapDumpFile );
70+ }
71+ try {
72+ HprofBuffer buffer = new MemoryMappedFileBuffer (heapDumpFile );
73+ HprofParser parser = new HprofParser (buffer );
74+ Snapshot snapshot = parser .parse ();
75+ deduplicateGcRoots (snapshot );
76+
77+ ClassObj refClass = snapshot .findClass (KeyedWeakReference .class .getName ());
78+ List <TrackedReference > references = new ArrayList <>();
79+ for (Instance weakRef : refClass .getInstancesList ()) {
80+ List <ClassInstance .FieldValue > values = classInstanceValues (weakRef );
81+ String key = asString (fieldValue (values , "key" ));
82+ String name =
83+ hasField (values , "name" ) ? asString (fieldValue (values , "name" )) : "(No name field)" ;
84+ Instance instance = fieldValue (values , "referent" );
85+ if (instance != null ) {
86+ String className = getClassName (instance );
87+ List <String > fields = describeFields (instance );
88+ references .add (new TrackedReference (key , name , className , fields ));
89+ }
90+ }
91+ return references ;
92+ } catch (Throwable e ) {
93+ throw new RuntimeException (e );
94+ }
95+ }
96+
6697 /**
6798 * Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key,
6899 * and then computes the shortest strong reference path from that instance to the GC roots.
@@ -112,8 +143,7 @@ void deduplicateGcRoots(Snapshot snapshot) {
112143 // Repopulate snapshot with unique GC roots.
113144 gcRoots .clear ();
114145 uniqueRootMap .forEach (new TObjectProcedure <String >() {
115- @ Override
116- public boolean execute (String key ) {
146+ @ Override public boolean execute (String key ) {
117147 return gcRoots .add (uniqueRootMap .get (key ));
118148 }
119149 });
@@ -250,37 +280,16 @@ private LeakTraceElement buildLeakElement(LeakNode node) {
250280 LeakTraceElement .Holder holderType ;
251281 String className ;
252282 String extra = null ;
253- List <String > fields = new ArrayList <>();
283+ List <String > fields = describeFields (holder );
284+
285+ className = getClassName (holder );
286+
254287 if (holder instanceof ClassObj ) {
255- ClassObj classObj = (ClassObj ) holder ;
256288 holderType = CLASS ;
257- className = classObj .getClassName ();
258- for (Map .Entry <Field , Object > entry : classObj .getStaticFieldValues ().entrySet ()) {
259- Field field = entry .getKey ();
260- Object value = entry .getValue ();
261- fields .add ("static " + field .getName () + " = " + value );
262- }
263289 } else if (holder instanceof ArrayInstance ) {
264- ArrayInstance arrayInstance = (ArrayInstance ) holder ;
265290 holderType = ARRAY ;
266- className = arrayInstance .getClassObj ().getClassName ();
267- if (arrayInstance .getArrayType () == Type .OBJECT ) {
268- Object [] values = arrayInstance .getValues ();
269- for (int i = 0 ; i < values .length ; i ++) {
270- fields .add ("[" + i + "] = " + values [i ]);
271- }
272- }
273291 } else {
274- ClassInstance classInstance = (ClassInstance ) holder ;
275292 ClassObj classObj = holder .getClassObj ();
276- for (Map .Entry <Field , Object > entry : classObj .getStaticFieldValues ().entrySet ()) {
277- fields .add ("static " + fieldToString (entry ));
278- }
279- for (ClassInstance .FieldValue field : classInstance .getValues ()) {
280- fields .add (fieldToString (field ));
281- }
282- className = classObj .getClassName ();
283-
284293 if (extendsThread (classObj )) {
285294 holderType = THREAD ;
286295 String threadName = threadName (holder );
@@ -316,6 +325,52 @@ private LeakTraceElement buildLeakElement(LeakNode node) {
316325 fields );
317326 }
318327
328+ private List <String > describeFields (Instance instance ) {
329+ List <String > fields = new ArrayList <>();
330+
331+ if (instance instanceof ClassObj ) {
332+ ClassObj classObj = (ClassObj ) instance ;
333+ for (Map .Entry <Field , Object > entry : classObj .getStaticFieldValues ().entrySet ()) {
334+ Field field = entry .getKey ();
335+ Object value = entry .getValue ();
336+ fields .add ("static " + field .getName () + " = " + value );
337+ }
338+ } else if (instance instanceof ArrayInstance ) {
339+ ArrayInstance arrayInstance = (ArrayInstance ) instance ;
340+ if (arrayInstance .getArrayType () == Type .OBJECT ) {
341+ Object [] values = arrayInstance .getValues ();
342+ for (int i = 0 ; i < values .length ; i ++) {
343+ fields .add ("[" + i + "] = " + values [i ]);
344+ }
345+ }
346+ } else {
347+ ClassObj classObj = instance .getClassObj ();
348+ for (Map .Entry <Field , Object > entry : classObj .getStaticFieldValues ().entrySet ()) {
349+ fields .add ("static " + fieldToString (entry ));
350+ }
351+ ClassInstance classInstance = (ClassInstance ) instance ;
352+ for (ClassInstance .FieldValue field : classInstance .getValues ()) {
353+ fields .add (fieldToString (field ));
354+ }
355+ }
356+ return fields ;
357+ }
358+
359+ private String getClassName (Instance instance ) {
360+ String className ;
361+ if (instance instanceof ClassObj ) {
362+ ClassObj classObj = (ClassObj ) instance ;
363+ className = classObj .getClassName ();
364+ } else if (instance instanceof ArrayInstance ) {
365+ ArrayInstance arrayInstance = (ArrayInstance ) instance ;
366+ className = arrayInstance .getClassObj ().getClassName ();
367+ } else {
368+ ClassObj classObj = instance .getClassObj ();
369+ className = classObj .getClassName ();
370+ }
371+ return className ;
372+ }
373+
319374 private long since (long analysisStartNanoTime ) {
320375 return NANOSECONDS .toMillis (System .nanoTime () - analysisStartNanoTime );
321376 }
0 commit comments