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); } }
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 InstrumentedAssembly InstrumentAssemblyIfNecessary(string assemblyFile) { var assemblyDirectory = Path.GetDirectoryName(assemblyFile); var resolver = new CustomAssemblyResolver(assemblyDirectory); Console.WriteLine($"Assembly resolver search directories:\n{string.Join("\n", resolver.GetSearchDirectories())}\n"); using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyFile, new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver })) { if (!HasSourceFiles(assemblyDefinition)) { return(null); } if (assemblyDefinition.CustomAttributes.Any(a => a.AttributeType.Name == "InstrumentedAttribute")) { throw new Exception($"Assembly \"{assemblyFile}\" is already instrumented"); } Console.WriteLine($"Instrumenting assembly \"{assemblyDefinition.Name.Name}\""); var instrumentedAssembly = new InstrumentedAssembly(assemblyDefinition.Name.Name); var instrumentedAttributeReference = assemblyDefinition.MainModule.ImportReference(instrumentedAttributeConstructor); assemblyDefinition.CustomAttributes.Add(new CustomAttribute(instrumentedAttributeReference)); var enterMethodInfo = hitServiceType.GetMethod("EnterMethod"); var exitMethodInfo = methodContextType.GetMethod("Exit"); var hitInstructionMethodInfo = methodContextType.GetMethod("HitInstruction"); var methodContextClassReference = assemblyDefinition.MainModule.ImportReference(methodContextType); var enterMethodReference = assemblyDefinition.MainModule.ImportReference(enterMethodInfo); var exitMethodReference = assemblyDefinition.MainModule.ImportReference(exitMethodInfo); var hitInstructionReference = assemblyDefinition.MainModule.ImportReference(hitInstructionMethodInfo); var methods = assemblyDefinition.GetAllMethods() .Where(m => !(m.DeclaringType.CustomAttributes .Any(attribute => attribute.AttributeType.FullName == "System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute") || m.DeclaringType.DeclaringType?.CustomAttributes? .Any(attribute => attribute.AttributeType.FullName == "System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute") == true)); var documentsGroups = methods .SelectMany(m => m.DebugInformation.SequencePoints, (method, s) => new { Method = method, SequencePoint = s, s.Document }) .GroupBy(j => j.Document) .ToArray(); foreach (var documentGroup in documentsGroups) { if (!sourceFiles.Contains(documentGroup.Key.Url)) { continue; } var sourceRelativePath = GetSourceRelativePath(documentGroup.Key.Url); if (documentGroup.Key.FileHasChanged()) { Console.WriteLine($"Ignoring modified file \"{documentGroup.Key.Url}\""); continue; } var fileLines = File.ReadAllLines(documentGroup.Key.Url); var methodGroups = documentGroup .GroupBy(j => j.Method, j => j.SequencePoint) .ToArray(); foreach (var methodGroup in methodGroups) { var methodDefinition = methodGroup.Key; InstrumentMethod(methodDefinition, methodGroup, methodContextClassReference, enterMethodReference, exitMethodReference, fileLines, instrumentedAssembly, sourceRelativePath, hitInstructionReference); } } var miniCoverTempPath = GetMiniCoverTempPath(); var instrumentedAssemblyFile = Path.Combine(miniCoverTempPath, $"{Guid.NewGuid()}.dll"); var instrumentedPdbFile = GetPdbFile(instrumentedAssemblyFile); assemblyDefinition.Write(instrumentedAssemblyFile, new WriterParameters { WriteSymbols = true }); instrumentedAssembly.TempAssemblyFile = instrumentedAssemblyFile; instrumentedAssembly.TempPdbFile = instrumentedPdbFile; return(instrumentedAssembly); } }