@@ -29,6 +29,7 @@ internal class Instrumenter
29
29
private FieldDefinition _customTrackerHitsFilePath ;
30
30
private ILProcessor _customTrackerClassConstructorIl ;
31
31
private TypeDefinition _customTrackerTypeDef ;
32
+ private MethodReference _customTrackerRegisterUnloadEventsMethod ;
32
33
private MethodReference _customTrackerRecordHitMethod ;
33
34
34
35
public Instrumenter ( string module , string identifier , string [ ] excludeFilters , string [ ] includeFilters , string [ ] excludedFiles , string [ ] excludedAttributes )
@@ -76,6 +77,7 @@ private void InstrumentModule()
76
77
77
78
using ( var module = ModuleDefinition . ReadModule ( stream , parameters ) )
78
79
{
80
+ var containsAppContext = module . GetType ( nameof ( System ) , nameof ( AppContext ) ) != null ;
79
81
var types = module . GetTypes ( ) ;
80
82
AddCustomModuleTrackerToModule ( module ) ;
81
83
@@ -95,13 +97,49 @@ private void InstrumentModule()
95
97
}
96
98
97
99
// Fixup the custom tracker class constructor, according to all instrumented types
100
+ if ( _customTrackerRegisterUnloadEventsMethod == null )
101
+ {
102
+ _customTrackerRegisterUnloadEventsMethod = new MethodReference (
103
+ nameof ( ModuleTrackerTemplate . RegisterUnloadEvents ) , module . TypeSystem . Void , _customTrackerTypeDef ) ;
104
+ }
105
+
98
106
Instruction lastInstr = _customTrackerClassConstructorIl . Body . Instructions . Last ( ) ;
107
+
108
+ if ( ! containsAppContext )
109
+ {
110
+ // For "normal" cases, where the instrumented assembly is not the core library, we add a call to
111
+ // RegisterUnloadEvents to the static constructor of the generated custom tracker. Due to static
112
+ // initialization constraints, the core library is handled separately below.
113
+ _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Call , _customTrackerRegisterUnloadEventsMethod ) ) ;
114
+ }
115
+
99
116
_customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldc_I4 , _result . HitCandidates . Count ) ) ;
100
117
_customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Newarr , module . TypeSystem . Int32 ) ) ;
101
118
_customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Stsfld , _customTrackerHitsArray ) ) ;
102
119
_customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldstr , _result . HitsFilePath ) ) ;
103
120
_customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Stsfld , _customTrackerHitsFilePath ) ) ;
104
121
122
+ if ( containsAppContext )
123
+ {
124
+ // Handle the core library by instrumenting System.AppContext.OnProcessExit to directly call
125
+ // the UnloadModule method of the custom tracker type. This avoids loops between the static
126
+ // initialization of the custom tracker and the static initialization of the hosting AppDomain
127
+ // (which for the core library case will be instrumented code).
128
+ var eventArgsType = new TypeReference ( nameof ( System ) , nameof ( EventArgs ) , module , module . TypeSystem . CoreLibrary ) ;
129
+ var customTrackerUnloadModule = new MethodReference ( nameof ( ModuleTrackerTemplate . UnloadModule ) , module . TypeSystem . Void , _customTrackerTypeDef ) ;
130
+ customTrackerUnloadModule . Parameters . Add ( new ParameterDefinition ( module . TypeSystem . Object ) ) ;
131
+ customTrackerUnloadModule . Parameters . Add ( new ParameterDefinition ( eventArgsType ) ) ;
132
+
133
+ var appContextType = new TypeReference ( nameof ( System ) , nameof ( AppContext ) , module , module . TypeSystem . CoreLibrary ) ;
134
+ var onProcessExitMethod = new MethodReference ( "OnProcessExit" , module . TypeSystem . Void , appContextType ) . Resolve ( ) ;
135
+ var onProcessExitIl = onProcessExitMethod . Body . GetILProcessor ( ) ;
136
+
137
+ lastInstr = onProcessExitIl . Body . Instructions . Last ( ) ;
138
+ onProcessExitIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldnull ) ) ;
139
+ onProcessExitIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldnull ) ) ;
140
+ onProcessExitIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Call , customTrackerUnloadModule ) ) ;
141
+ }
142
+
105
143
module . Write ( stream ) ;
106
144
}
107
145
}
@@ -135,12 +173,9 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
135
173
{
136
174
MethodDefinition methodOnCustomType = new MethodDefinition ( methodDef . Name , methodDef . Attributes , methodDef . ReturnType ) ;
137
175
138
- if ( methodDef . Name == "RecordHit" )
176
+ foreach ( var parameter in methodDef . Parameters )
139
177
{
140
- foreach ( var parameter in methodDef . Parameters )
141
- {
142
- methodOnCustomType . Parameters . Add ( new ParameterDefinition ( module . ImportReference ( parameter . ParameterType ) ) ) ;
143
- }
178
+ methodOnCustomType . Parameters . Add ( new ParameterDefinition ( module . ImportReference ( parameter . ParameterType ) ) ) ;
144
179
}
145
180
146
181
foreach ( var variable in methodDef . Body . Variables )
@@ -166,8 +201,11 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
166
201
else
167
202
{
168
203
// Move to the custom type
169
- instr . Operand = new MethodReference (
170
- methodReference . Name , methodReference . ReturnType , _customTrackerTypeDef ) ;
204
+ var updatedMethodReference = new MethodReference ( methodReference . Name , methodReference . ReturnType , _customTrackerTypeDef ) ;
205
+ foreach ( var parameter in methodReference . Parameters )
206
+ updatedMethodReference . Parameters . Add ( new ParameterDefinition ( parameter . Name , parameter . Attributes , module . ImportReference ( parameter . ParameterType ) ) ) ;
207
+
208
+ instr . Operand = updatedMethodReference ;
171
209
}
172
210
}
173
211
else if ( instr . Operand is FieldReference fieldReference )
0 commit comments