Skip to content

Commit 367315f

Browse files
committed
Remove the need to register events when instrumenting System.Private.CoreLib
1 parent 92b26a4 commit 367315f

File tree

2 files changed

+41
-8
lines changed

2 files changed

+41
-8
lines changed

src/coverlet.core/Instrumentation/Instrumenter.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
@@ -95,13 +96,42 @@ private void InstrumentModule()
9596
}
9697

9798
// Fixup the custom tracker class constructor, according to all instrumented types
99+
if (_customTrackerRegisterUnloadEventsMethod == null)
100+
{
101+
_customTrackerRegisterUnloadEventsMethod = new MethodReference(
102+
nameof(ModuleTrackerTemplate.RegisterUnloadEvents), module.TypeSystem.Void, _customTrackerTypeDef);
103+
}
104+
98105
Instruction lastInstr = _customTrackerClassConstructorIl.Body.Instructions.Last();
106+
107+
if (!module.Equals(module.TypeSystem.Void.Module))
108+
{
109+
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Call, _customTrackerRegisterUnloadEventsMethod));
110+
}
111+
99112
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4, _result.HitCandidates.Count));
100113
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Newarr, module.TypeSystem.Int32));
101114
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsArray));
102115
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath));
103116
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsFilePath));
104117

118+
if (module.Equals(module.TypeSystem.Void.Module))
119+
{
120+
var eventArgsType = new TypeReference(nameof(System), nameof(EventArgs), module, module.TypeSystem.CoreLibrary).Resolve();
121+
var customTrackerUnloadModule = new MethodReference(nameof(ModuleTrackerTemplate.UnloadModule), module.TypeSystem.Void, _customTrackerTypeDef);
122+
customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(module.TypeSystem.Object));
123+
customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(eventArgsType));
124+
125+
var appContextType = new TypeReference(nameof(System), nameof(AppContext), module, module.TypeSystem.CoreLibrary).Resolve();
126+
var onProcessExitMethod = new MethodReference("OnProcessExit", module.TypeSystem.Void, appContextType).Resolve();
127+
var onProcessExitIl = onProcessExitMethod.Body.GetILProcessor();
128+
129+
lastInstr = onProcessExitIl.Body.Instructions.Last();
130+
onProcessExitIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldnull));
131+
onProcessExitIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldnull));
132+
onProcessExitIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Call, customTrackerUnloadModule));
133+
}
134+
105135
module.Write(stream);
106136
}
107137
}
@@ -135,12 +165,9 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
135165
{
136166
MethodDefinition methodOnCustomType = new MethodDefinition(methodDef.Name, methodDef.Attributes, methodDef.ReturnType);
137167

138-
if (methodDef.Name == "RecordHit")
168+
foreach (var parameter in methodDef.Parameters)
139169
{
140-
foreach (var parameter in methodDef.Parameters)
141-
{
142-
methodOnCustomType.Parameters.Add(new ParameterDefinition(module.ImportReference(parameter.ParameterType)));
143-
}
170+
methodOnCustomType.Parameters.Add(new ParameterDefinition(module.ImportReference(parameter.ParameterType)));
144171
}
145172

146173
foreach (var variable in methodDef.Body.Variables)

src/coverlet.template/ModuleTrackerTemplate.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,20 @@ public static class ModuleTrackerTemplate
2222

2323
static ModuleTrackerTemplate()
2424
{
25-
AppDomain.CurrentDomain.ProcessExit += new EventHandler(UnloadModule);
26-
AppDomain.CurrentDomain.DomainUnload += new EventHandler(UnloadModule);
27-
2825
// At the end of the instrumentation of a module, the instrumenter needs to add code here
2926
// to initialize the static fields according to the values derived from the instrumentation of
3027
// the module.
3128
}
3229

30+
// A call to this method will be injected in the static constructor above for most cases. However, if the
31+
// current assembly is System.Private.CoreLib (or more specifically, defines System.AppDomain), a call directly
32+
// to UnloadModule will be injected in System.AppContext.OnProcessExit.
33+
public static void RegisterUnloadEvents()
34+
{
35+
AppDomain.CurrentDomain.ProcessExit += new EventHandler(UnloadModule);
36+
AppDomain.CurrentDomain.DomainUnload += new EventHandler(UnloadModule);
37+
}
38+
3339
public static void RecordHit(int hitLocationIndex)
3440
{
3541
// Make sure to avoid recording if this is a call to RecordHit within the AppDomain setup code in an

0 commit comments

Comments
 (0)