public void InstrumentType( InstrumentationContext context, TypeDefinition typeDefinition, InstrumentedAssembly instrumentedAssembly) { var typeDocuments = typeDefinition.GetAllDocuments(); if (!typeDocuments.Any(d => context.IsSource(d) || context.IsTest(d))) { return; } var methods = typeDefinition.GetAllMethods(); foreach (var methodDefinition in methods) { var methodDocuments = methodDefinition.GetAllDocuments(); var isSource = methodDocuments.Any(d => context.IsSource(d)); var isTest = methodDocuments.Any(d => context.IsTest(d)); if (!isSource && !isTest) { continue; } _methodInstrumenter.InstrumentMethod( context, isSource, methodDefinition, instrumentedAssembly); } }
public InstrumentedAssembly InstrumentAssemblyFile( InstrumentationContext context, FileInfo assemblyFile) { var assemblyDirectory = assemblyFile.Directory; var resolver = new CustomAssemblyResolver(assemblyDirectory, _assemblyResolverLogger); _logger.LogTrace("Assembly resolver search directories: {directories}", new object[] { resolver.GetSearchDirectories() }); try { using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyFile.FullName, new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver })) { return(InstrumentAssemblyDefinition(context, assemblyDefinition)); } } catch (BadImageFormatException) { _logger.LogInformation("Invalid assembly format"); return(null); } }
private void VisitAssemblyGroup( InstrumentationContext context, InstrumentationResult result, IEnumerable <FileInfo> groupFiles) { var firstAssemblyFile = groupFiles.First(); var instrumentedAssembly = _assemblyInstrumenter.InstrumentAssembly( context, firstAssemblyFile); if (instrumentedAssembly == null) { return; } foreach (var assemblyFile in groupFiles) { var pdbFile = FileUtils.GetPdbFile(assemblyFile); var assemblyBackupFile = FileUtils.GetBackupFile(assemblyFile); var pdbBackupFile = FileUtils.GetBackupFile(pdbFile); //Backup File.Copy(assemblyFile.FullName, assemblyBackupFile.FullName, true); File.Copy(pdbFile.FullName, pdbBackupFile.FullName, true); //Override assembly File.Copy(instrumentedAssembly.TempAssemblyFile, assemblyFile.FullName, true); File.Copy(instrumentedAssembly.TempPdbFile, pdbFile.FullName, true); //Copy instrumentation dependencies var assemblyDirectory = assemblyFile.Directory; var hitServicesPath = Path.GetFileName(hitServicesAssembly.Location); var newHitServicesPath = Path.Combine(assemblyDirectory.FullName, hitServicesPath); if (!File.Exists(newHitServicesPath)) { File.Copy(hitServicesAssembly.Location, newHitServicesPath, true); result.AddExtraAssembly(newHitServicesPath); } instrumentedAssembly.AddLocation( assemblyFile.FullName, assemblyBackupFile.FullName, pdbFile.FullName, pdbBackupFile.FullName ); var hitServicesAssemblyVersion = FileVersionInfo.GetVersionInfo(hitServicesAssembly.Location); foreach (var depsJsonFile in assemblyDirectory.GetFiles("*.deps.json")) { DepsJsonUtils.PatchDepsJson(depsJsonFile, hitServicesAssemblyVersion.ProductVersion); } } result.AddInstrumentedAssembly(instrumentedAssembly); File.Delete(instrumentedAssembly.TempAssemblyFile); File.Delete(instrumentedAssembly.TempPdbFile); }
public void InstrumentType( InstrumentationContext context, TypeDefinition typeDefinition, InstrumentedAssembly instrumentedAssembly) { foreach (var methodDefinition in typeDefinition.Methods) { if (!methodDefinition.HasBody || !methodDefinition.DebugInformation.HasSequencePoints) { continue; } var methodDocuments = methodDefinition.GetAllDocuments(); var isSource = methodDocuments.Any(d => context.IsSource(d.Url)); var isTest = methodDocuments.Any(d => context.IsTest(d.Url)); if (!isSource && !isTest) { continue; } _methodInstrumenter.InstrumentMethod( context, isSource, methodDefinition, instrumentedAssembly); } foreach (var nestedType in typeDefinition.NestedTypes) { InstrumentType(context, nestedType, instrumentedAssembly); } }
public InstrumentationResult Execute(InstrumentationContext context) { context.Workdir = context.Workdir.AddEndingDirectorySeparator(); var result = new InstrumentationResult { SourcePath = context.Workdir.FullName, HitsPath = context.HitsPath }; var assemblyGroups = context.Assemblies .Where(ShouldInstrumentAssemblyFile) .GroupBy(FileUtils.GetFileHash) .ToArray(); foreach (var assemblyGroup in assemblyGroups) { VisitAssemblyGroup( context, result, assemblyGroup); } return(result); }
private static string GetSourceRelativePath(InstrumentationContext context, string path) { var file = new Uri(path); var folder = new Uri(context.Workdir.FullName); string relativePath = Uri.UnescapeDataString( folder.MakeRelativeUri(file) .ToString() .Replace('/', Path.DirectorySeparatorChar) ); return(relativePath); }
private InstrumentedAssembly InstrumentAssemblyDefinition( InstrumentationContext context, AssemblyDefinition assemblyDefinition) { if (assemblyDefinition.CustomAttributes.Any(a => a.AttributeType.Name == "InstrumentedAttribute")) { _logger.LogInformation("Already instrumented"); return(null); } var assemblyDocuments = assemblyDefinition.GetAllDocuments(); if (!assemblyDocuments.Any(d => context.IsSource(d.Url) || context.IsTest(d.Url))) { _logger.LogInformation("No link to source files or test files"); return(null); } var changedDocuments = assemblyDocuments.Where(d => d.FileHasChanged()).ToArray(); if (changedDocuments.Any()) { if (_logger.IsEnabled(LogLevel.Debug)) { var changedFiles = changedDocuments.Select(d => d.Url).Distinct().ToArray(); _logger.LogDebug("Source files has changed: {changedFiles}", new object[] { changedFiles }); } else { _logger.LogInformation("Source files has changed"); } return(null); } _logger.LogInformation("Instrumenting"); var instrumentedAssembly = new InstrumentedAssembly(assemblyDefinition.Name.Name); var instrumentedAttributeReference = assemblyDefinition.MainModule.ImportReference(instrumentedAttributeConstructor); assemblyDefinition.CustomAttributes.Add(new CustomAttribute(instrumentedAttributeReference)); foreach (var type in assemblyDefinition.MainModule.GetTypes()) { _typeInstrumenter.InstrumentType( context, type, instrumentedAssembly); } var miniCoverTempPath = GetMiniCoverTempPath(); var instrumentedAssemblyFile = new FileInfo(Path.Combine(miniCoverTempPath, $"{Guid.NewGuid()}.dll")); var instrumentedPdbFile = FileUtils.GetPdbFile(instrumentedAssemblyFile); assemblyDefinition.Write(instrumentedAssemblyFile.FullName, new WriterParameters { WriteSymbols = true }); instrumentedAssembly.TempAssemblyFile = instrumentedAssemblyFile.FullName; instrumentedAssembly.TempPdbFile = instrumentedPdbFile.FullName; return(instrumentedAssembly); }
private void VisitAssemblyGroup( InstrumentationContext context, InstrumentationResult result, IEnumerable <FileInfo> assemblyFiles) { using (_logger.BeginScope("Checking assembly files {assemblies}", assemblyFiles.Select(f => f.FullName), LogLevel.Information)) { var instrumentedAssembly = _assemblyInstrumenter.InstrumentAssemblyFile( context, assemblyFiles.First()); if (instrumentedAssembly == null) { return; } foreach (var assemblyFile in assemblyFiles) { if (_loadedAssemblyFiles.Contains(assemblyFile.FullName)) { _logger.LogInformation("Skipping loaded assembly {assemblyFile}", assemblyFile.FullName); continue; } var pdbFile = FileUtils.GetPdbFile(assemblyFile); var assemblyBackupFile = FileUtils.GetBackupFile(assemblyFile); var pdbBackupFile = FileUtils.GetBackupFile(pdbFile); //Backup File.Copy(assemblyFile.FullName, assemblyBackupFile.FullName, true); File.Copy(pdbFile.FullName, pdbBackupFile.FullName, true); //Override assembly File.Copy(instrumentedAssembly.TempAssemblyFile, assemblyFile.FullName, true); File.Copy(instrumentedAssembly.TempPdbFile, pdbFile.FullName, true); //Copy instrumentation dependencies var assemblyDirectory = assemblyFile.Directory; var hitServicesPath = Path.GetFileName(hitServicesAssembly.Location); var newHitServicesPath = Path.Combine(assemblyDirectory.FullName, hitServicesPath); File.Copy(hitServicesAssembly.Location, newHitServicesPath, true); result.AddExtraAssembly(newHitServicesPath); instrumentedAssembly.AddLocation( assemblyFile.FullName, assemblyBackupFile.FullName, pdbFile.FullName, pdbBackupFile.FullName ); var hitServicesAssemblyVersion = FileVersionInfo.GetVersionInfo(hitServicesAssembly.Location); foreach (var depsJsonFile in assemblyDirectory.GetFiles("*.deps.json")) { DepsJsonUtils.PatchDepsJson(depsJsonFile, hitServicesAssemblyVersion.ProductVersion); } } result.AddInstrumentedAssembly(instrumentedAssembly); File.Delete(instrumentedAssembly.TempAssemblyFile); File.Delete(instrumentedAssembly.TempPdbFile); } }
public InstrumentedAssembly InstrumentAssembly( InstrumentationContext context, FileInfo assemblyFile) { var assemblyDirectory = assemblyFile.Directory; using (_logger.BeginScope("Checking assembly file {assembly}", assemblyFile.FullName, LogLevel.Information)) { if (assemblyFile.Name == "MiniCover.HitServices.dll") { _logger.LogInformation("Skipping HitServices"); return(null); } if (_loadedAssemblyFiles.Contains(assemblyFile.FullName)) { _logger.LogInformation("Can't instrument loaded assembly"); return(null); } var resolver = new CustomAssemblyResolver(assemblyDirectory, _assemblyResolverLogger); _logger.LogTrace("Assembly resolver search directories: {directories}", new object[] { resolver.GetSearchDirectories() }); using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyFile.FullName, new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver })) { if (assemblyDefinition.CustomAttributes.Any(a => a.AttributeType.Name == "InstrumentedAttribute")) { _logger.LogInformation("Already instrumented"); return(null); } var assemblyDocuments = assemblyDefinition.GetAllDocuments(); if (!assemblyDocuments.Any(d => context.IsSource(d) || context.IsTest(d))) { _logger.LogInformation("No link to source files or test files"); return(null); } _logger.LogInformation("Instrumenting"); var instrumentedAssembly = new InstrumentedAssembly(assemblyDefinition.Name.Name); var instrumentedAttributeReference = assemblyDefinition.MainModule.ImportReference(instrumentedAttributeConstructor); assemblyDefinition.CustomAttributes.Add(new CustomAttribute(instrumentedAttributeReference)); foreach (var type in assemblyDefinition.MainModule.GetTypes()) { _typeInstrumenter.InstrumentType( context, type, instrumentedAssembly); } var miniCoverTempPath = GetMiniCoverTempPath(); var instrumentedAssemblyFile = new FileInfo(Path.Combine(miniCoverTempPath, $"{Guid.NewGuid()}.dll")); var instrumentedPdbFile = FileUtils.GetPdbFile(instrumentedAssemblyFile); assemblyDefinition.Write(instrumentedAssemblyFile.FullName, new WriterParameters { WriteSymbols = true }); instrumentedAssembly.TempAssemblyFile = instrumentedAssemblyFile.FullName; instrumentedAssembly.TempPdbFile = instrumentedPdbFile.FullName; return(instrumentedAssembly); } } }
private void InstrumentInstructions( InstrumentationContext context, MethodDefinition methodDefinition, InstrumentedAssembly instrumentedAssembly, IList <(SequencePoint sequencePoint, Instruction instruction)> sequencePointsInstructions,
public void InstrumentMethod( InstrumentationContext context, bool instrumentInstructions, MethodDefinition methodDefinition, InstrumentedAssembly instrumentedAssembly) { var originalMethod = methodDefinition.ResolveOriginalMethod(); var instrumentedMethod = instrumentedAssembly.AddMethod(new InstrumentedMethod { Class = originalMethod.DeclaringType.FullName, Name = originalMethod.Name, FullName = originalMethod.FullName, }); var methodContextClassReference = methodDefinition.Module.GetOrImportReference(methodContextType); var enterMethodReference = methodDefinition.Module.GetOrImportReference(enterMethodInfo); var disposeMethodReference = methodDefinition.Module.GetOrImportReference(disposeMethodInfo); var sequencePointsInstructions = methodDefinition.MapSequencePointsToInstructions().ToArray(); var ilProcessor = methodDefinition.Body.GetILProcessor(); ilProcessor.Body.InitLocals = true; ilProcessor.Body.SimplifyMacros(); var methodContextVariable = new VariableDefinition(methodContextClassReference); ilProcessor.Body.Variables.Add(methodContextVariable); ilProcessor.RemoveTailInstructions(); var endFinally = ilProcessor.EncapsulateWithTryFinally(); ilProcessor.InsertBefore(endFinally, new[] { ilProcessor.Create(OpCodes.Ldloc, methodContextVariable), ilProcessor.Create(OpCodes.Callvirt, disposeMethodReference) }, true); ilProcessor.InsertBefore(ilProcessor.Body.Instructions[0], new[] { ilProcessor.Create(OpCodes.Ldstr, context.HitsPath), ilProcessor.Create(OpCodes.Ldstr, originalMethod.DeclaringType.Module.Assembly.Name.Name), ilProcessor.Create(OpCodes.Ldstr, originalMethod.DeclaringType.FullName), ilProcessor.Create(OpCodes.Ldstr, originalMethod.Name), ilProcessor.Create(OpCodes.Call, enterMethodReference), ilProcessor.Create(OpCodes.Stloc, methodContextVariable) }, true); if (instrumentInstructions && !methodDefinition.IsExcludedFromCodeCoverage()) { InstrumentInstructions( context, methodDefinition, instrumentedAssembly, sequencePointsInstructions, ilProcessor, methodContextVariable, instrumentedMethod); } ilProcessor.Body.OptimizeMacros(); }
private void InstrumentInstructions( InstrumentationContext context, MethodDefinition methodDefinition, InstrumentedAssembly instrumentedAssembly, Dictionary <int, Instruction> instructionsByOffset, ILProcessor ilProcessor, VariableDefinition methodContextVariable, InstrumentedMethod instrumentedMethod) { var hitInstructionReference = methodDefinition.Module.GetOrImportReference(hitInstructionMethodInfo); foreach (var sequencePoint in methodDefinition.DebugInformation.SequencePoints) { var document = sequencePoint.Document; if (document.FileHasChanged()) { _logger.LogInformation("Ignoring modified file {file}", document.Url); continue; } if (sequencePoint.IsHidden) { continue; } var documentUrl = sequencePoint.Document.Url; var documentLines = _fileReader.ReadAllLines(new FileInfo(documentUrl)); var code = documentLines.ExtractCode( sequencePoint.StartLine, sequencePoint.EndLine, sequencePoint.StartColumn, sequencePoint.EndColumn); if (code == null || code == "{" || code == "}") { continue; } var instruction = instructionsByOffset[sequencePoint.Offset]; // if the previous instruction is a Prefix instruction then this instruction MUST go with it. // we cannot put an instruction between the two. if (instruction.Previous != null && instruction.Previous.OpCode.OpCodeType == OpCodeType.Prefix) { continue; } if (!ilProcessor.Body.Instructions.Contains(instruction)) { var methodFullName = $"{methodDefinition.DeclaringType.FullName}.{methodDefinition.Name}"; _logger.LogWarning("Skipping instruction because it was removed from method {method}", methodFullName); continue; } var sourceRelativePath = GetSourceRelativePath(context, documentUrl); var instructionId = ++context.InstructionId; instrumentedAssembly.AddInstruction(sourceRelativePath, new InstrumentedInstruction { Id = instructionId, StartLine = sequencePoint.StartLine, EndLine = sequencePoint.EndLine, StartColumn = sequencePoint.StartColumn, EndColumn = sequencePoint.EndColumn, Instruction = instruction.ToString(), Method = instrumentedMethod, Code = code }); ilProcessor.InsertBefore(instruction, new[] { ilProcessor.Create(OpCodes.Ldloc, methodContextVariable), ilProcessor.Create(OpCodes.Ldc_I4, instructionId), ilProcessor.Create(OpCodes.Callvirt, hitInstructionReference) }, true); } }