Пример #1
0
        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);
            }
        }
Пример #2
0
        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);
            }
        }
Пример #3
0
 private static XElement CreateClassesElement(InstrumentedAssembly assembly, HitsInfo hitsInfo)
 {
     return(new XElement(
                XName.Get("classes"),
                assembly.SourceFiles
                .Select(kv => CreateClassElement(kv.Key, kv.Value, hitsInfo))
                ));
 }
        public static InstrumentedAssembly Instrument(this MethodDefinition methodDefinition)
        {
            var documentsUrls          = methodDefinition.GetAllDocuments().Select(d => d.Url).Distinct().ToArray();
            var instrumentationContext = CreateInstrumentationContext(documentsUrls);
            var instrumentedAssembly   = new InstrumentedAssembly(methodDefinition.Module.Assembly.Name.Name);
            var methodInstrumenter     = CreateMethodInstrumenter();

            methodInstrumenter.InstrumentMethod(instrumentationContext, true, methodDefinition, instrumentedAssembly);
            return(instrumentedAssembly);
        }
Пример #5
0
        public static InstrumentedAssembly Instrument(this TypeDefinition typeDefinition)
        {
            var documents = typeDefinition.GetAllDocuments();
            var instrumentationContext = CreateInstrumentationContext(documents);
            var instrumentedAssembly   = new InstrumentedAssembly(typeDefinition.Module.Assembly.Name.Name);
            var methodInstrumenter     = CreateMethodInstrumenter();
            var typeInstrumenter       = new TypeInstrumenter(methodInstrumenter);

            typeInstrumenter.InstrumentType(instrumentationContext, typeDefinition, instrumentedAssembly);
            return(instrumentedAssembly);
        }
Пример #6
0
 private static CloverCounter CountPackageMetrics(InstrumentedAssembly assembly, HitsInfo hits)
 {
     return(assembly.SourceFiles
            .Select(t => CountFileMetrics(t, hits))
            .Aggregate(new CloverCounter(), (counter, next) =>
     {
         counter.Add(next);
         counter.Files += 1;
         return counter;
     }));
 }
Пример #7
0
 private static IEnumerable <XElement> CreateFilesElement(InstrumentedAssembly assembly, HitsInfo hits)
 {
     return(assembly.SourceFiles.Select(file => new XElement(
                                            XName.Get("file"),
                                            new XAttribute(XName.Get("name"), Path.GetFileName(file.Path)),
                                            new XAttribute(XName.Get("path"), file.Path),
                                            CreateMetricsElement(CountFileMetrics(file, hits)),
                                            CreateClassesElement(file.Sequences, hits),
                                            CreateLinesElement(file.Sequences, hits)
                                            )));
 }
        public static InstrumentedAssembly Instrument(this TypeDefinition typeDefinition)
        {
            var documentsUrls          = typeDefinition.GetAllDocuments(true).Select(d => d.Url).Distinct().ToArray();
            var instrumentationContext = CreateInstrumentationContext(documentsUrls);
            var instrumentedAssembly   = new InstrumentedAssembly(typeDefinition.Module.Assembly.Name.Name);
            var methodInstrumenter     = CreateMethodInstrumenter();
            var typeInstrumenter       = new TypeInstrumenter(methodInstrumenter);

            typeInstrumenter.InstrumentType(instrumentationContext, typeDefinition, instrumentedAssembly);
            return(instrumentedAssembly);
        }
Пример #9
0
        private void InstrumentMethod(ModuleDefinition moduleDefinition, MethodDefinition methodDefinition,
                                      IEnumerable <SequencePoint> sequencePoints,
                                      TypeReference methodContextClassReference,
                                      MethodReference enterMethodReference, MethodReference exitMethodReference,
                                      string[] fileLines, InstrumentedAssembly instrumentedAssembly, string sourceRelativePath,
                                      MethodReference hitInstructionReference, string hitsFile)
        {
            var ilProcessor = methodDefinition.Body.GetILProcessor();

            ilProcessor.Body.InitLocals = true;
            ilProcessor.Body.SimplifyMacros();

            var instructions = methodDefinition.Body.Instructions.ToDictionary(i => i.Offset);

            var methodContextVariable = new VariableDefinition(methodContextClassReference);

            methodDefinition.Body.Variables.Add(methodContextVariable);
            var pathParamLoadInstruction     = ilProcessor.Create(OpCodes.Ldstr, hitsFile);
            var enterMethodInstruction       = ilProcessor.Create(OpCodes.Call, enterMethodReference);
            var storeMethodResultInstruction = ilProcessor.Create(OpCodes.Stloc, methodContextVariable);

            var firstInstruction = instructions[0];


            var loadMethodContextInstruction = ilProcessor.Create(OpCodes.Ldloc, methodContextVariable);
            var exitMethodInstruction        = ilProcessor.Create(OpCodes.Callvirt, exitMethodReference);

            ilProcessor.EncapsulateMethodBodyWithTryFinallyBlock(firstInstruction, (processor, instruction) =>
            {
                ilProcessor.InsertBefore(instruction, exitMethodInstruction);
                ilProcessor.InsertBefore(exitMethodInstruction, loadMethodContextInstruction);
            });
            var currentFirstInstruction = ilProcessor.Body.Instructions.First();

            ilProcessor.InsertBefore(currentFirstInstruction, storeMethodResultInstruction);
            ilProcessor.InsertBefore(storeMethodResultInstruction, enterMethodInstruction);
            ilProcessor.InsertBefore(enterMethodInstruction, pathParamLoadInstruction);
            ilProcessor.ReplaceInstructionReferences(currentFirstInstruction, pathParamLoadInstruction);

            //InstrumentInstructions(methodDefinition, sequencePoints, fileLines, instrumentedAssembly, sourceRelativePath, hitInstructionReference, instructions, ilProcessor, methodContextVariable);
            foreach (var instruction in ilProcessor.Body.Instructions.ToArray())
            {
                if (instruction.OpCode == OpCodes.Tail)
                {
                    var noOpInstruction = ilProcessor.Create(OpCodes.Nop);
                    ilProcessor.Replace(instruction, noOpInstruction);
                    ilProcessor.ReplaceInstructionReferences(instruction, noOpInstruction);
                }
            }
            ilProcessor.Body.OptimizeMacros();
        }
Пример #10
0
        public void InstrumentMethod(
            MethodDefinition methodDefinition,
            IEnumerable <SequencePoint> sequencePoints,
            TypeReference methodContextClassReference,
            MethodReference enterMethodReference,
            MethodReference exitMethodReference,
            string[] fileLines,
            InstrumentedAssembly instrumentedAssembly,
            string sourceRelativePath,
            MethodReference hitInstructionReference)
        {
            var ilProcessor = methodDefinition.Body.GetILProcessor();

            ilProcessor.Body.InitLocals = true;
            ilProcessor.Body.SimplifyMacros();

            var methodContextVariable = new VariableDefinition(methodContextClassReference);

            ilProcessor.Body.Variables.Add(methodContextVariable);
            var pathParamLoadInstruction     = ilProcessor.Create(OpCodes.Ldstr, hitsFile);
            var enterMethodInstruction       = ilProcessor.Create(OpCodes.Call, enterMethodReference);
            var storeMethodResultInstruction = ilProcessor.Create(OpCodes.Stloc, methodContextVariable);

            ilProcessor.RemoveTailInstructions();

            var instructions = ilProcessor.Body.Instructions.ToDictionary(i => i.Offset);

            ilProcessor.ForEachReturn((processor, instruction) =>
            {
                var loadMethodContextInstruction = ilProcessor.Create(OpCodes.Ldloc, methodContextVariable);
                var exitMethodInstruction        = ilProcessor.Create(OpCodes.Callvirt, exitMethodReference);
                ilProcessor.InsertBefore(instruction, exitMethodInstruction);
                ilProcessor.InsertBefore(exitMethodInstruction, loadMethodContextInstruction);
                ilProcessor.ReplaceInstructionReferences(instruction, loadMethodContextInstruction);
            });

            var currentFirstInstruction = ilProcessor.Body.Instructions.First();

            ilProcessor.InsertBefore(currentFirstInstruction, storeMethodResultInstruction);
            ilProcessor.InsertBefore(storeMethodResultInstruction, enterMethodInstruction);
            ilProcessor.InsertBefore(enterMethodInstruction, pathParamLoadInstruction);
            ilProcessor.ReplaceInstructionReferences(currentFirstInstruction, pathParamLoadInstruction);

            InstrumentInstructions(methodDefinition, sequencePoints, fileLines, instrumentedAssembly, sourceRelativePath, hitInstructionReference, instructions, ilProcessor, methodContextVariable);

            ilProcessor.Body.OptimizeMacros();
        }
Пример #11
0
        private static XElement CreatePackageElement(InstrumentedAssembly assembly, HitsInfo hitsInfo)
        {
            var allLines = assembly.SourceFiles
                           .SelectMany(kvFile => kvFile.Value.Sequences)
                           .SelectMany(i => i.GetLines())
                           .Distinct()
                           .Count();

            var coveredLines = assembly.SourceFiles
                               .SelectMany(kvFile => kvFile.Value.Sequences)
                               .Where(h => hitsInfo.WasHit(h.HitId))
                               .SelectMany(i => i.GetLines())
                               .Distinct()
                               .Count();

            var allBranches = assembly.SourceFiles
                              .SelectMany(kvFile => kvFile.Value.Sequences)
                              .SelectMany(i => i.Conditions)
                              .SelectMany(c => c.Branches)
                              .Count();

            var coveredBranches = assembly.SourceFiles
                                  .SelectMany(kvFile => kvFile.Value.Sequences)
                                  .SelectMany(i => i.Conditions)
                                  .SelectMany(c => c.Branches)
                                  .Where(b => hitsInfo.WasHit(b.HitId))
                                  .Count();

            var lineRate   = allLines == 0 ? 1d : (double)coveredLines / (double)allLines;
            var branchRate = allBranches == 0 ? 1d : (double)coveredBranches / (double)allBranches;

            return(new XElement(
                       XName.Get("package"),
                       new XAttribute(XName.Get("name"), assembly.Name),
                       new XAttribute(XName.Get("line-rate"), lineRate),
                       new XAttribute(XName.Get("branch-rate"), branchRate),
                       new XAttribute(XName.Get("complexity"), 0),
                       CreateClassesElement(assembly, hitsInfo)
                       ));
        }
Пример #12
0
        private void InstrumentInstructions(MethodDefinition methodDefinition, IEnumerable <SequencePoint> sequencePoints, string[] fileLines,
                                            InstrumentedAssembly instrumentedAssembly, string sourceRelativePath, MethodReference hitInstructionReference,
                                            Dictionary <int, Instruction> instructions, ILProcessor ilProcessor, VariableDefinition methodContextVariable)
        {
            foreach (var sequencePoint in sequencePoints)
            {
                var code = sequencePoint.ExtractCode(fileLines);
                if (code == null || code == "{" || code == "}")
                {
                    continue;
                }

                var instruction = instructions[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;
                }

                var instructionId = ++id;

                instrumentedAssembly.AddInstruction(sourceRelativePath, new InstrumentedInstruction
                {
                    Id             = instructionId,
                    StartLine      = sequencePoint.StartLine,
                    EndLine        = sequencePoint.EndLine,
                    StartColumn    = sequencePoint.StartColumn,
                    EndColumn      = sequencePoint.EndColumn,
                    Class          = methodDefinition.DeclaringType.FullName,
                    Method         = methodDefinition.Name,
                    MethodFullName = methodDefinition.FullName,
                    Instruction    = instruction.ToString()
                });

                InstrumentInstruction(instructionId, instruction, hitInstructionReference, ilProcessor,
                                      methodContextVariable);
            }
        }
Пример #13
0
 private void InstrumentInstructions(
     IInstrumentationContext context,
     MethodDefinition methodDefinition,
     InstrumentedAssembly instrumentedAssembly,
     IList <(SequencePoint sequencePoint,
Пример #14
0
        public void InstrumentMethod(
            IInstrumentationContext context,
            bool instrumentInstructions,
            MethodDefinition methodDefinition,
            InstrumentedAssembly instrumentedAssembly)
        {
            var originalMethod = methodDefinition.ResolveOriginalMethod();

            var instrumentedMethod = instrumentedAssembly.GetOrAddMethod(
                originalMethod.DeclaringType.FullName,
                originalMethod.Name,
                originalMethod.FullName
                );

            var methodContextClassReference = methodDefinition.Module.GetOrImportReference(methodScopeType);
            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();
        }
Пример #15
0
        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);
        }
Пример #16
0
        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);
            }
        }
Пример #17
0
        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);
                }
            }
        }
Пример #18
0
        private InstrumentedAssembly InstrumentAssemblyIfNecessary(string assemblyFile)
        {
            var assemblyDirectory = Path.GetDirectoryName(assemblyFile);

            var resolver = new DefaultAssemblyResolver();

            resolver.AddSearchDirectory(assemblyDirectory);

            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();

                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);
            }
        }
Пример #19
0
        private InstrumentedAssembly InstrumentAssemblyDefinition(
            IInstrumentationContext context,
            AssemblyDefinition assemblyDefinition)
        {
            if (assemblyDefinition.CustomAttributes.Any(a => a.AttributeType.Name == "InstrumentedAttribute"))
            {
                _logger.LogInformation("Already instrumented");
                return(null);
            }

            var assemblyDocuments = assemblyDefinition.GetAllDocuments();

            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);
            }

            var instrumentedAssembly           = new InstrumentedAssembly(assemblyDefinition.Name.Name);
            var instrumentedAttributeReference = assemblyDefinition.MainModule.ImportReference(instrumentedAttributeConstructor);

            assemblyDefinition.CustomAttributes.Add(new CustomAttribute(instrumentedAttributeReference));

            foreach (var typeDefinition in assemblyDefinition.MainModule.GetTypes())
            {
                if (typeDefinition.FullName == "<Module>" ||
                    typeDefinition.FullName == "AutoGeneratedProgram" ||
                    typeDefinition.DeclaringType != null)
                {
                    continue;
                }

                _typeInstrumenter.InstrumentType(
                    context,
                    typeDefinition,
                    instrumentedAssembly);
            }

            if (!instrumentedAssembly.Methods.Any())
            {
                _logger.LogInformation("Nothing to instrument");
                return(null);
            }

            _logger.LogInformation("Assembly instrumented");

            var miniCoverTempPath = GetMiniCoverTempPath();

            var instrumentedAssemblyFile = _fileSystem.FileInfo.FromFileName(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);
        }
Пример #20
0
        private InstrumentedAssembly InstrumentAssemblyIfNecessary(string assemblyFile)
        {
            var assemblyDirectory = Path.GetDirectoryName(assemblyFile);

            var resolver = new DefaultAssemblyResolver();

            resolver.AddSearchDirectory(assemblyDirectory);

            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();

                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;

                        var ilProcessor = methodDefinition.Body.GetILProcessor();

                        ilProcessor.Body.SimplifyMacros();

                        var instructions = methodDefinition.Body.Instructions.ToDictionary(i => i.Offset);

                        var methodContextVariable = new VariableDefinition(methodContextClassReference);
                        methodDefinition.Body.Variables.Add(methodContextVariable);
                        var pathParamLoadInstruction     = ilProcessor.Create(OpCodes.Ldstr, hitsFile);
                        var enterMethodInstruction       = ilProcessor.Create(OpCodes.Call, enterMethodReference);
                        var storeMethodResultInstruction = ilProcessor.Create(OpCodes.Stloc, methodContextVariable);
                        ilProcessor.InsertBefore(instructions[0], storeMethodResultInstruction);
                        ilProcessor.InsertBefore(storeMethodResultInstruction, enterMethodInstruction);
                        ilProcessor.InsertBefore(enterMethodInstruction, pathParamLoadInstruction);
                        UpdateInstructionReferences(methodDefinition, instructions[0], pathParamLoadInstruction);

                        foreach (var instruction in instructions.Values)
                        {
                            if (instruction.OpCode == OpCodes.Ret)
                            {
                                var loadMethodContextInstruction = ilProcessor.Create(OpCodes.Ldloc, methodContextVariable);
                                var exitMethodInstruction        = ilProcessor.Create(OpCodes.Callvirt, exitMethodReference);
                                ilProcessor.InsertBefore(instruction, exitMethodInstruction);
                                ilProcessor.InsertBefore(exitMethodInstruction, loadMethodContextInstruction);
                                UpdateInstructionReferences(methodDefinition, instruction, loadMethodContextInstruction);
                            }
                        }

                        foreach (var sequencePoint in methodGroup)
                        {
                            var code = sequencePoint.ExtractCode(fileLines);
                            if (code == null || code == "{" || code == "}")
                            {
                                continue;
                            }

                            var instruction = instructions[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;
                            }

                            var instructionId = ++id;

                            instrumentedAssembly.AddInstruction(sourceRelativePath, new InstrumentedInstruction
                            {
                                Id             = instructionId,
                                StartLine      = sequencePoint.StartLine,
                                EndLine        = sequencePoint.EndLine,
                                StartColumn    = sequencePoint.StartColumn,
                                EndColumn      = sequencePoint.EndColumn,
                                Class          = methodDefinition.DeclaringType.FullName,
                                Method         = methodDefinition.Name,
                                MethodFullName = methodDefinition.FullName,
                                Instruction    = instruction.ToString()
                            });

                            InstrumentInstruction(instructionId, instruction, hitInstructionReference, methodDefinition, ilProcessor, methodContextVariable);
                        }

                        ilProcessor.Body.OptimizeMacros();
                    }
                }

                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);
            }
        }
Пример #21
0
        public void InstrumentMethod(
            InstrumentationContext context,
            bool instrumentInstructions,
            MethodDefinition methodDefinition,
            InstrumentedAssembly instrumentedAssembly)
        {
            var originalMethod = ResolveOriginalMethod(methodDefinition);

            var instrumentedMethod = instrumentedAssembly.AddMethod(new InstrumentedMethod
            {
                Class    = originalMethod.DeclaringType.FullName,
                Name     = originalMethod.Name,
                FullName = originalMethod.FullName,
            });

            var enterMethodInfo   = hitServiceType.GetMethod("EnterMethod");
            var disposeMethodInfo = methodContextType.GetMethod("Dispose");

            var methodContextClassReference = methodDefinition.Module.GetOrImportReference(methodContextType);
            var enterMethodReference        = methodDefinition.Module.GetOrImportReference(enterMethodInfo);
            var disposeMethodReference      = methodDefinition.Module.GetOrImportReference(disposeMethodInfo);

            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 instructions = ilProcessor.Body.Instructions.ToDictionary(i => i.Offset);

            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,
                    instructions,
                    ilProcessor,
                    methodContextVariable,
                    instrumentedMethod);
            }

            ilProcessor.Body.OptimizeMacros();
        }