1
- using System . Runtime ;
1
+ using System . Text ;
2
2
using UMS . Analysis ;
3
- using UMS . Analysis . Structures ;
4
3
using UMS . Analysis . Structures . Objects ;
5
- using UMS . LowLevel ;
6
- using UMS . LowLevel . Structures ;
7
4
8
5
namespace UnityMemorySnapshotThing ;
9
6
@@ -71,17 +68,23 @@ private static void FindLeakedUnityObjects(SnapshotFile file)
71
68
//Find all the managed objects, filter to those which have a m_CachedObjectPtr field
72
69
//Then filter to those for which that field is 0 (i.e. not pointing to a native object)
73
70
//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 ) ;
75
75
76
76
var filterStart = DateTime . Now ;
77
77
78
78
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 ) ;
81
83
82
84
var detectStart = DateTime . Now ;
83
85
84
86
int numLeaked = 0 ;
87
+ var leakedTypes = new Dictionary < string , int > ( ) ;
85
88
foreach ( var managedClassInstance in unityEngineObjects )
86
89
{
87
90
var fields = file . GetInstanceFieldInfoForTypeIndex ( managedClassInstance . TypeInfo . TypeIndex ) ;
@@ -100,14 +103,32 @@ private static void FindLeakedUnityObjects(SnapshotFile file)
100
103
if ( integerFieldValue . Value == 0 )
101
104
{
102
105
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
+
105
117
numLeaked ++ ;
106
118
}
107
119
}
108
120
}
109
121
}
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 ) ;
110
131
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 ( ) ) ;
112
133
}
113
134
}
0 commit comments