@@ -58,6 +58,8 @@ public static void Main(string[] args)
58
58
// Console.WriteLine($"Querying large dynamic arrays took {(DateTime.Now - start).TotalMilliseconds} ms\n");
59
59
60
60
CrawlManagedObjects ( file ) ;
61
+
62
+ FindLeakedUnityObjects ( file ) ;
61
63
}
62
64
63
65
private static void CrawlManagedObjects ( SnapshotFile file )
@@ -78,36 +80,63 @@ private static void CrawlManagedObjects(SnapshotFile file)
78
80
// GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
79
81
foreach ( var gcHandle in gcHandles )
80
82
{
81
- var info = file . ParseManagedObjectInfo ( gcHandle ) ;
82
-
83
- if ( ! info . IsKnownType )
84
- continue ;
85
-
86
- // var type = file.ReadSingleStringFromChapter(EntryType.TypeDescriptions_Name, info.TypeDescriptionIndex);
87
-
88
- // Console.WriteLine($"Size {info.Size}, type {type} ({info.TypeDescriptionIndex})");
89
-
90
- rootObjects . Add ( new ( file , info ) ) ;
83
+ rootObjects . Add ( file . GetManagedClassInstance ( gcHandle ) ! . Value ) ;
91
84
92
85
validCount ++ ;
93
86
94
- if ( validCount % 1000 == 0 )
95
- Console . WriteLine ( $ "Processed { validCount } GC roots in { ( DateTime . Now - start ) . TotalMilliseconds } ms") ;
87
+ // if(validCount % 1000 == 0)
88
+ // Console.WriteLine($"Processed {validCount} GC roots in {(DateTime.Now - start).TotalMilliseconds} ms");
96
89
}
97
90
98
91
GCSettings . LatencyMode = GCLatencyMode . Interactive ;
99
92
100
93
Console . WriteLine ( $ "Found { validCount } valid GC roots out of { gcHandles . Length } total in { ( DateTime . Now - start ) . TotalMilliseconds } ms") ;
101
94
}
102
95
103
- private static void FindLeakedUnityObjects ( LowLevelSnapshotFile file )
96
+ private static void FindLeakedUnityObjects ( SnapshotFile file )
104
97
{
105
98
var start = DateTime . Now ;
106
99
Console . WriteLine ( "Finding leaked Unity objects..." ) ;
107
100
108
101
//Find all the managed objects, filter to those which have a m_CachedObjectPtr field
109
102
//Then filter to those for which that field is 0 (i.e. not pointing to a native object)
110
103
//That gives the leaked managed shells.
104
+ Console . WriteLine ( $ "Snapshot contains { file . AllManagedClassInstances . Count ( ) } managed objects") ;
105
+
106
+ var filterStart = DateTime . Now ;
107
+
108
+ var unityEngineObjects = file . AllManagedClassInstances . Where ( i => i . InheritsFromUnityEngineObject ( file ) ) . ToArray ( ) ;
109
+
110
+ Console . WriteLine ( $ "Of those, { unityEngineObjects . Length } inherit from UnityEngine.Object (filtered in { ( DateTime . Now - filterStart ) . TotalMilliseconds } ms)") ;
111
+
112
+ var detectStart = DateTime . Now ;
113
+
114
+ int numLeaked = 0 ;
115
+ foreach ( var managedClassInstance in unityEngineObjects )
116
+ {
117
+ var fields = file . GetFieldInfoForTypeIndex ( managedClassInstance . TypeInfo . TypeIndex ) ;
118
+ for ( var fieldNumber = 0 ; fieldNumber < fields . Length ; fieldNumber ++ )
119
+ {
120
+ var basicFieldInfoCache = fields [ fieldNumber ] ;
121
+ var name = file . ReadSingleStringFromChapter ( EntryType . FieldDescriptions_Name , basicFieldInfoCache . FieldIndex ) ;
122
+
123
+ if ( name == "m_CachedPtr" )
124
+ {
125
+ var value = managedClassInstance . Fields [ fieldNumber ] ;
126
+
127
+ if ( value is not IntegerFieldValue integerFieldValue )
128
+ throw new Exception ( "Expected integer field value" ) ;
129
+
130
+ if ( integerFieldValue . Value == 0 )
131
+ {
132
+ var typeName = file . ReadSingleStringFromChapter ( EntryType . TypeDescriptions_Name , managedClassInstance . TypeInfo . TypeIndex ) ;
133
+ Console . WriteLine ( $ "Found leaked managed object of type: { typeName } ") ;
134
+ numLeaked ++ ;
135
+ }
136
+ }
137
+ }
138
+ }
111
139
140
+ Console . WriteLine ( $ "Finished detection in { ( DateTime . Now - detectStart ) . TotalMilliseconds } ms. { numLeaked } of those are leaked managed shells") ;
112
141
}
113
142
}
0 commit comments