1- using System . Runtime ;
1+ using System . Text ;
22using UMS . Analysis ;
3- using UMS . Analysis . Structures ;
43using UMS . Analysis . Structures . Objects ;
5- using UMS . LowLevel ;
6- using UMS . LowLevel . Structures ;
74
85namespace UnityMemorySnapshotThing ;
96
@@ -71,17 +68,23 @@ private static void FindLeakedUnityObjects(SnapshotFile file)
7168 //Find all the managed objects, filter to those which have a m_CachedObjectPtr field
7269 //Then filter to those for which that field is 0 (i.e. not pointing to a native object)
7370 //That gives the leaked managed shells.
74- Console . WriteLine ( $ "Snapshot contains { file . AllManagedClassInstances . Count ( ) } managed objects") ;
71+ var ret = new StringBuilder ( ) ;
72+ var str = $ "Snapshot contains { file . AllManagedClassInstances . Count ( ) } managed objects";
73+ Console . WriteLine ( str ) ;
74+ ret . AppendLine ( str ) ;
7575
7676 var filterStart = DateTime . Now ;
7777
7878 var unityEngineObjects = file . AllManagedClassInstances . Where ( i => i . InheritsFromUnityEngineObject ( file ) ) . ToArray ( ) ;
79-
80- Console . WriteLine ( $ "Of those, { unityEngineObjects . Length } inherit from UnityEngine.Object (filtered in { ( DateTime . Now - filterStart ) . TotalMilliseconds } ms)") ;
79+
80+ str = $ "Of those, { unityEngineObjects . Length } inherit from UnityEngine.Object (filtered in { ( DateTime . Now - filterStart ) . TotalMilliseconds } ms)";
81+ Console . WriteLine ( str ) ;
82+ ret . AppendLine ( str ) ;
8183
8284 var detectStart = DateTime . Now ;
8385
8486 int numLeaked = 0 ;
87+ var leakedTypes = new Dictionary < string , int > ( ) ;
8588 foreach ( var managedClassInstance in unityEngineObjects )
8689 {
8790 var fields = file . GetInstanceFieldInfoForTypeIndex ( managedClassInstance . TypeInfo . TypeIndex ) ;
@@ -100,14 +103,32 @@ private static void FindLeakedUnityObjects(SnapshotFile file)
100103 if ( integerFieldValue . Value == 0 )
101104 {
102105 var typeName = file . GetTypeName ( managedClassInstance . TypeInfo . TypeIndex ) ;
103- Console . WriteLine ( $ "Found leaked managed object of type: { typeName } at memory address 0x{ managedClassInstance . ObjectAddress : X} ") ;
104- Console . WriteLine ( $ " Retention Path: { managedClassInstance . GetFirstObservedRetentionPath ( file ) } ") ;
106+
107+ str = $ "Found leaked managed object of type: { typeName } at memory address 0x{ managedClassInstance . ObjectAddress : X} ";
108+ Console . WriteLine ( str ) ;
109+ ret . AppendLine ( str ) ;
110+
111+ str = $ " Retention Path: { managedClassInstance . GetFirstObservedRetentionPath ( file ) } ";
112+ Console . WriteLine ( str ) ;
113+ ret . AppendLine ( str ) ;
114+
115+ leakedTypes [ typeName ] = leakedTypes . GetValueOrDefault ( typeName ) + 1 ;
116+
105117 numLeaked ++ ;
106118 }
107119 }
108120 }
109121 }
122+
123+ str = $ "Finished detection in { ( DateTime . Now - detectStart ) . TotalMilliseconds } ms. { numLeaked } of those are leaked managed shells";
124+ Console . WriteLine ( str ) ;
125+ ret . AppendLine ( str ) ;
126+
127+ var leakedTypesSorted = leakedTypes . OrderByDescending ( kvp => kvp . Value ) . ToArray ( ) ;
128+
129+ str = $ "Leaked types by count: \n { string . Join ( "\n " , leakedTypesSorted . Select ( kvp => $ "{ kvp . Value } x { kvp . Key } ") ) } ";
130+ ret . AppendLine ( str ) ;
110131
111- Console . WriteLine ( $ "Finished detection in { ( DateTime . Now - detectStart ) . TotalMilliseconds } ms. { numLeaked } of those are leaked managed shells" ) ;
132+ File . WriteAllText ( "leaked_objects.txt" , ret . ToString ( ) ) ;
112133 }
113134}
0 commit comments