33
44namespace Multiplayer . Client . Desyncs ;
55
6- public static class DeferredStackTracingImpl
6+ public struct AddrTable ( )
77{
8- struct AddrInfo
8+ private const int StartingN = 10 ; // 1024
9+ private const int StartingShift = 64 - StartingN ;
10+ private const int StartingSize = 1 << StartingN ;
11+ private const float LoadFactor = 0.5f ;
12+
13+ private AddrInfo [ ] hashtable = new AddrInfo [ StartingSize ] ;
14+
15+ public int Size => hashtable . Length ;
16+ public int Entries { get ; private set ; } = 0 ;
17+ public int Collisions { get ; private set ; } = 0 ;
18+
19+ private int shift = StartingShift ;
20+
21+ public ref AddrInfo GetOrCreateAddrInfo ( long ret )
922 {
10- public long addr ;
11- public long stackUsage ;
12- public long nameHash ;
13- public long unused ;
23+ int indexmask = Size - 1 ;
24+ int index = ( int ) ( HashAddr ( ( ulong ) ret ) >> shift ) ;
25+ ref var info = ref hashtable [ index ] ;
26+ int colls = 0 ;
27+
28+ // Open addressing
29+ while ( info . addr != 0 && info . addr != ret )
30+ {
31+ index = ( index + 1 ) & indexmask ;
32+ info = ref hashtable [ index ] ;
33+ colls ++ ;
34+ }
35+ if ( colls > Collisions ) Collisions = colls ;
36+
37+ // When returning an unpopulated AddrInfo, assume it's going to get populated shortly and consider it used
38+ // immediately.
39+ if ( info . addr == 0 && Entries ++ > Size * LoadFactor ) ResizeHashtable ( ) ;
40+ return ref info ;
1441 }
1542
16- const int StartingN = 7 ;
17- const int StartingShift = 64 - StartingN ;
18- const int StartingSize = 1 << StartingN ;
19- const float LoadFactor = 0.5f ;
43+ private static ulong HashAddr ( ulong addr ) => ( ( addr >> 4 ) | addr << 60 ) * 11400714819323198485 ;
2044
21- static AddrInfo [ ] hashtable = new AddrInfo [ StartingSize ] ;
22- public static int hashtableSize = StartingSize ;
23- public static int hashtableEntries ;
24- public static int hashtableShift = StartingShift ;
25- public static int collisions ;
45+ private void ResizeHashtable ( )
46+ {
47+ var oldTable = hashtable ;
2648
49+ hashtable = new AddrInfo [ Size * 2 ] ;
50+ shift -- ;
51+ Collisions = 0 ;
52+
53+ int indexmask = Size - 1 ;
54+
55+ for ( int i = 0 ; i < oldTable . Length ; i ++ )
56+ {
57+ ref var oldInfo = ref oldTable [ i ] ;
58+ if ( oldInfo . addr != 0 )
59+ {
60+ int index = ( int ) ( HashAddr ( ( ulong ) oldInfo . addr ) >> shift ) ;
61+
62+ while ( hashtable [ index ] . addr != 0 )
63+ index = ( index + 1 ) & indexmask ;
64+
65+ ref var newInfo = ref hashtable [ index ] ;
66+ newInfo . addr = oldInfo . addr ;
67+ newInfo . stackUsage = oldInfo . stackUsage ;
68+ newInfo . nameHash = oldInfo . nameHash ;
69+ }
70+ }
71+ }
72+ }
73+
74+ public struct AddrInfo
75+ {
76+ public long addr ;
77+ public long stackUsage ;
78+ public long nameHash ;
79+ }
80+
81+ public static class DeferredStackTracingImpl
82+ {
2783 const long NotJit = long . MaxValue ;
2884 const long RbpBased = long . MaxValue - 1 ;
2985
3086 const long UsesRbpAsGpr = 1L << 50 ;
3187 const long UsesRbx = 1L << 51 ;
3288 const long RbpInfoClearMask = ~ ( UsesRbpAsGpr | UsesRbx ) ;
3389
34- public const int MaxDepth = 32 ;
3590 public const int HashInfluence = 6 ;
3691
37- public static unsafe int TraceImpl ( long [ ] traceIn , ref int hash )
92+ public static AddrTable hashTable = new ( ) ;
93+
94+ public static unsafe int TraceImpl ( long [ ] traceIn , ref int hash , int skipFrames = 0 )
3895 {
3996 if ( Native . LmfPtr == 0 )
4097 return 0 ;
4198
42- long [ ] trace = traceIn ;
4399 long rbp = GetRbp ( ) ;
44100
45101 long stck = rbp ;
46102 rbp = * ( long * ) rbp ;
47103
48- int indexmask = hashtableSize - 1 ;
49- int shift = hashtableShift ;
50-
51- long ret ;
52104 long lmfPtr = * ( long * ) Native . LmfPtr ;
53105
54- int depth = 0 ;
55-
106+ int depth = 0 ; // frames seen
107+ int index = 0 ; // frames returned through long[] traceIn
56108 while ( true )
57109 {
58- ret = * ( long * ) ( stck + 8 ) ;
59-
60- int index = ( int ) ( HashAddr ( ( ulong ) ret) >> shift ) ;
61- ref var info = ref hashtable [ index ] ;
62- int colls = 0 ;
63-
64- // Open addressing
65- while ( info . addr != 0 && info . addr != ret )
66- {
67- index = ( index + 1 ) & indexmask ;
68- info = ref hashtable [ index ] ;
69- colls++ ;
70- }
71-
72- if ( colls > collisions )
73- collisions = colls;
74-
75- long stackUsage = 0 ;
76-
77- if ( info . addr != 0 )
78- stackUsage = info . stackUsage ;
79- else
80- stackUsage = UpdateNewElement ( ref info , ret ) ;
110+ var ret = * ( long * ) ( stck + 8 ) ;
111+ ref var info = ref hashTable . GetOrCreateAddrInfo ( ret ) ;
112+ if ( info . addr == 0 ) UpdateNewElement( ref info , ret ) ;
81113
114+ long stackUsage = info. stackUsage ;
82115 if ( stackUsage == NotJit )
83116 {
84117 // LMF (Last Managed Frame) layout on x64:
@@ -98,13 +131,18 @@ public static unsafe int TraceImpl(long[] traceIn, ref int hash)
98131 continue ;
99132 }
100133
101- trace [ depth ] = ret ;
134+ if ( depth >= skipFrames )
135+ {
136+ traceIn[ index ] = ret ;
137+
138+ // info.nameHash == 0 marks methods to skip
139+ if ( index < HashInfluence && info . nameHash != 0 ) hash = HashCombineInt( hash , ( int ) info . nameHash ) ;
102140
103- // info.nameHash == 0 marks methods to skip
104- if ( depth < HashInfluence && info . nameHash != 0 )
105- hash = HashCombineInt( hash , ( int ) info . nameHash ) ;
141+ index++ ;
142+ }
106143
107- if ( info . nameHash != 0 && ++ depth == MaxDepth )
144+ // traceIn length limits above all how many frames are visited, not how many are populated.
145+ if ( info . nameHash != 0 && ++ depth == traceIn . Length )
108146 break;
109147
110148 if ( stackUsage == RbpBased )
@@ -129,63 +167,21 @@ public static unsafe int TraceImpl(long[] traceIn, ref int hash)
129167 stck += stackUsage ;
130168 }
131169
132- return depth ;
170+ return index ;
133171 }
134172
135- static long UpdateNewElement ( ref AddrInfo info , long ret )
136- {
137- long stackUsage = GetStackUsage( ret ) ;
138173
174+ private static void UpdateNewElement ( ref AddrInfo info , long ret )
175+ {
139176 info . addr = ret ;
140- info. stackUsage = stackUsage ;
177+ info. stackUsage = GetStackUsage ( ret ) ;
141178
142179 var normalizedMethodNameBetweenOS = Native. MethodNameNormalizedFromAddr( ret , true ) ;
143180
144181 info . nameHash =
145182 normalizedMethodNameBetweenOS == null ? 1 :
146183 Native . GetMethodAggressiveInlining ( ret ) ? 0 :
147184 StableStringHash ( normalizedMethodNameBetweenOS ) ;
148-
149- hashtableEntries++ ;
150- if ( hashtableEntries > hashtableSize * LoadFactor )
151- ResizeHashtable( ) ;
152-
153- return stackUsage;
154- }
155-
156- static ulong HashAddr( ulong addr ) => ( ( addr >> 4 ) | addr << 60 ) * 11400714819323198485 ;
157-
158- static int ResizeHashtable ( )
159- {
160- var oldTable = hashtable;
161-
162- hashtableSize *= 2 ;
163- hashtableShift-- ;
164-
165- hashtable = new AddrInfo [ hashtableSize ] ;
166- collisions = 0 ;
167-
168- int indexmask = hashtableSize - 1 ;
169- int shift = hashtableShift;
170-
171- for ( int i = 0 ; i < oldTable . Length ; i++ )
172- {
173- ref var oldInfo = ref oldTable [ i ] ;
174- if ( oldInfo . addr != 0 )
175- {
176- int index = ( int ) ( HashAddr ( ( ulong ) oldInfo . addr ) >> shift ) ;
177-
178- while ( hashtable [ index ] . addr != 0 )
179- index = ( index + 1 ) & indexmask ;
180-
181- ref var newInfo = ref hashtable [ index ] ;
182- newInfo. addr = oldInfo . addr ;
183- newInfo. stackUsage = oldInfo . stackUsage ;
184- newInfo. nameHash = oldInfo . nameHash ;
185- }
186- }
187-
188- return indexmask;
189185 }
190186
191187 static unsafe long GetStackUsage( long addr )
@@ -243,19 +239,17 @@ private static unsafe void CheckRbpUsage(uint* at, ref long stackUsage)
243239 }
244240
245241 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
246- static unsafe long GetRbp( )
242+ private static unsafe long GetRbp( )
247243 {
248244 long rbp = 0 ;
249245
250246 return * ( & rbp + 1 ) ;
251247 }
252248
253- public static int HashCombineInt ( int seed , int value )
254- {
255- return ( int ) ( seed ^ ( value + 2654435769u + ( seed << 6 ) + ( seed >> 2 ) ) ) ;
256- }
249+ private static int HashCombineInt ( int seed , int value ) =>
250+ ( int ) ( seed ^ ( value + 2654435769u + ( seed << 6 ) + ( seed >> 2 ) ) ) ;
257251
258- public static int StableStringHash( string ? str )
252+ private static int StableStringHash ( string ? str )
259253 {
260254 if ( str == null )
261255 {
0 commit comments