@@ -20,16 +20,10 @@ public static class ModuleTrackerTemplate
20
20
public static string HitsFilePath ;
21
21
public static int [ ] HitsArray ;
22
22
23
- // Special case when instrumenting CoreLib, the static below prevents infinite loop in CoreLib
24
- // while allowing the tracker template to call any of its types and functions.
25
- private static bool s_isTracking ;
26
-
27
23
static ModuleTrackerTemplate ( )
28
24
{
29
- s_isTracking = true ;
30
25
AppDomain . CurrentDomain . ProcessExit += new EventHandler ( UnloadModule ) ;
31
26
AppDomain . CurrentDomain . DomainUnload += new EventHandler ( UnloadModule ) ;
32
- s_isTracking = false ;
33
27
34
28
// At the end of the instrumentation of a module, the instrumenter needs to add code here
35
29
// to initialize the static fields according to the values derived from the instrumentation of
@@ -38,15 +32,18 @@ static ModuleTrackerTemplate()
38
32
39
33
public static void RecordHit ( int hitLocationIndex )
40
34
{
41
- if ( s_isTracking )
35
+ // Make sure to avoid recording if this is a call to RecordHit within the AppDomain setup code in an
36
+ // instrumented build of System.Private.CoreLib.
37
+ if ( HitsArray is null )
42
38
return ;
43
39
44
40
Interlocked . Increment ( ref HitsArray [ hitLocationIndex ] ) ;
45
41
}
46
42
47
43
public static void UnloadModule ( object sender , EventArgs e )
48
44
{
49
- s_isTracking = true ;
45
+ // Claim the current hits array and reset it to prevent double-counting scenarios.
46
+ var hitsArray = Interlocked . Exchange ( ref HitsArray , new int [ HitsArray . Length ] ) ;
50
47
51
48
// The same module can be unloaded multiple times in the same process via different app domains.
52
49
// Use a global mutex to ensure no concurrent access.
@@ -61,8 +58,8 @@ public static void UnloadModule(object sender, EventArgs e)
61
58
using ( var fs = new FileStream ( HitsFilePath , FileMode . CreateNew ) )
62
59
using ( var bw = new BinaryWriter ( fs ) )
63
60
{
64
- bw . Write ( HitsArray . Length ) ;
65
- foreach ( int hitCount in HitsArray )
61
+ bw . Write ( hitsArray . Length ) ;
62
+ foreach ( int hitCount in hitsArray )
66
63
{
67
64
bw . Write ( hitCount ) ;
68
65
}
@@ -82,25 +79,21 @@ public static void UnloadModule(object sender, EventArgs e)
82
79
using ( var bw = new BinaryWriter ( fs ) )
83
80
{
84
81
int hitsLength = br . ReadInt32 ( ) ;
85
- if ( hitsLength != HitsArray . Length )
82
+ if ( hitsLength != hitsArray . Length )
86
83
{
87
84
throw new InvalidOperationException (
88
- $ "{ HitsFilePath } has { hitsLength } entries but on memory { nameof ( HitsArray ) } has { HitsArray . Length } ") ;
85
+ $ "{ HitsFilePath } has { hitsLength } entries but on memory { nameof ( HitsArray ) } has { hitsArray . Length } ") ;
89
86
}
90
87
91
88
for ( int i = 0 ; i < hitsLength ; ++ i )
92
89
{
93
90
int oldHitCount = br . ReadInt32 ( ) ;
94
91
bw . Seek ( - sizeof ( int ) , SeekOrigin . Current ) ;
95
- bw . Write ( HitsArray [ i ] + oldHitCount ) ;
92
+ bw . Write ( hitsArray [ i ] + oldHitCount ) ;
96
93
}
97
94
}
98
95
}
99
96
100
- // Prevent any double counting scenario, i.e.: UnloadModule called twice (not sure if this can happen in practice ...)
101
- // Only an issue if DomainUnload and ProcessExit can both happens, perhaps can be removed...
102
- Array . Clear ( HitsArray , 0 , HitsArray . Length ) ;
103
-
104
97
// On purpose this is not under a try-finally: it is better to have an exception if there was any error writing the hits file
105
98
// this case is relevant when instrumenting corelib since multiple processes can be running against the same instrumented dll.
106
99
mutex . ReleaseMutex ( ) ;
0 commit comments