@@ -29,6 +29,7 @@ internal class Instrumenter
2929 private FieldDefinition _customTrackerHitsFilePath ;
3030 private ILProcessor _customTrackerClassConstructorIl ;
3131 private TypeDefinition _customTrackerTypeDef ;
32+ private MethodReference _customTrackerRegisterUnloadEventsMethod ;
3233 private MethodReference _customTrackerRecordHitMethod ;
3334
3435 public Instrumenter ( string module , string identifier , string [ ] excludeFilters , string [ ] includeFilters , string [ ] excludedFiles , string [ ] excludedAttributes )
@@ -76,6 +77,7 @@ private void InstrumentModule()
7677
7778 using ( var module = ModuleDefinition . ReadModule ( stream , parameters ) )
7879 {
80+ var containsAppContext = module . GetType ( nameof ( System ) , nameof ( AppContext ) ) != null ;
7981 var types = module . GetTypes ( ) ;
8082 AddCustomModuleTrackerToModule ( module ) ;
8183
@@ -95,13 +97,49 @@ private void InstrumentModule()
9597 }
9698
9799 // 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+
98106 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+
99116 _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldc_I4 , _result . HitCandidates . Count ) ) ;
100117 _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Newarr , module . TypeSystem . Int32 ) ) ;
101118 _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Stsfld , _customTrackerHitsArray ) ) ;
102119 _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldstr , _result . HitsFilePath ) ) ;
103120 _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Stsfld , _customTrackerHitsFilePath ) ) ;
104121
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+
105143 module . Write ( stream ) ;
106144 }
107145 }
@@ -135,12 +173,9 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
135173 {
136174 MethodDefinition methodOnCustomType = new MethodDefinition ( methodDef . Name , methodDef . Attributes , methodDef . ReturnType ) ;
137175
138- if ( methodDef . Name == "RecordHit" )
176+ foreach ( var parameter in methodDef . Parameters )
139177 {
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 ) ) ) ;
144179 }
145180
146181 foreach ( var variable in methodDef . Body . Variables )
@@ -166,8 +201,11 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
166201 else
167202 {
168203 // 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 ;
171209 }
172210 }
173211 else if ( instr . Operand is FieldReference fieldReference )
0 commit comments