/// <summary> /// Inject a call to the coverage function with the id of a basic block /// before its first instruction. /// </summary> /// <param name="function">Function being instrumented.</param> /// <param name="coverageVisit"> /// Coverage function to call before each block. /// </param> /// <param name="block"> /// The id of the block to pass to the coverage function. /// </param> /// <param name="blockStartInstruction"> /// First instruction of the basic block. /// </param> private static void InjectCoverageCall(Phx.FunctionUnit function, Phx.Symbols.FunctionSymbol coverageVisit, uint block, Phx.IR.Instruction blockStartInstruction) { Phx.Types.Table typeTable = function.ParentUnit.TypeTable; if (function.FlowGraph == null) { function.BuildFlowGraph(); } // Create the instructions Phx.IR.Operand blockOperand = Phx.IR.ImmediateOperand.New(function, typeTable.UInt32Type, (uint)block); Phx.IR.Instruction loadBlock = Phx.IR.ValueInstruction.NewUnaryExpression(function, Phx.Targets.Architectures.Msil.Opcode.ldc, typeTable.UInt32Type, blockOperand); loadBlock.DestinationOperand.Register = Phx.Targets.Architectures.Msil.Register.SR0; Phx.IR.Instruction callCoverage = Phx.IR.CallInstruction.New(function, Phx.Targets.Architectures.Msil.Opcode.call, coverageVisit); // Insert the instructions blockStartInstruction.InsertBefore(loadBlock); callCoverage.AppendSource(loadBlock.DestinationOperand); loadBlock.DestinationOperand.BreakExpressionTemporary(); loadBlock.DebugTag = blockStartInstruction.DebugTag; blockStartInstruction.InsertBefore(callCoverage); }
/// <summary> /// Inject code coverage information into a function. /// </summary> /// <param name="unit">The function to instrument.</param> protected override void Execute(Phx.Unit unit) { // Only instrument MSIL functions if (!unit.IsFunctionUnit) { return; } Phx.FunctionUnit function = unit.AsFunctionUnit; if (!function.Architecture.NameString.Equals("Msil")) { return; } Phx.PEModuleUnit module = function.ParentUnit.AsPEModuleUnit; Phx.Symbols.FunctionSymbol functionSymbol = function.FunctionSymbol; Phx.Types.FunctionType functionType = functionSymbol.FunctionType; // Create the method signature List <string> parameterTypes = new List <string>(); foreach (Phx.Types.Parameter parameter in functionType.UserDefinedParameters) { parameterTypes.Add(parameter.Type.ToString()); } string methodName = string.Format( CultureInfo.InvariantCulture, "{0}({1})", functionSymbol.NameString, string.Join(", ", parameterTypes.ToArray())); // Build the control flow graph for the current function function.BuildFlowGraph(); Phx.Graphs.FlowGraph flow = function.FlowGraph; // Log method details string typeName = (functionSymbol.EnclosingAggregateType != null) ? functionSymbol.EnclosingAggregateType.TypeSymbol.NameString : "<Module>"; Log.StartMethod( methodName, typeName, flow.StartBlock.FirstInstruction.GetFileName(), flow.StartBlock.FirstInstruction.GetLineNumber()); // Create a mapping of the disassembled from instructions to the // basic block IDs so we can use them for coverage viewing. Dictionary <Phx.IR.Instruction, uint> dissassembly = new Dictionary <Phx.IR.Instruction, uint>(); // Instrument and log the blocks for the current function. Log.StartBlocks(); foreach (Phx.Graphs.BasicBlock block in flow.BasicBlocks) { // Find the first real instruction in the block Phx.IR.Instruction first = null; foreach (Phx.IR.Instruction instruction in block.Instructions) { // Save the instructions to be dumped later dissassembly.Add(instruction, BlockCounter); // Ignore instructions that aren't actually "real" if (first == null && instruction.IsReal) { Phx.Common.Opcode opcode = instruction.Opcode as Phx.Common.Opcode; if (opcode == Phx.Common.Opcode.ReturnFinally || opcode == Phx.Common.Opcode.Leave || opcode == Phx.Common.Opcode.Unreached || opcode == Phx.Common.Opcode.ExitTypeFilter) { continue; } first = instruction; } } // Inject a code coverage visitor call before the first // instruction of the basic block if (first != null) { // Log the basic block Phx.IR.Instruction last = block.LastInstruction; Log.StartBlock( BlockCounter, first.GetMsilOffset(), first.GetFileName(), first.GetLineNumber(), first.GetColumnNumber(), last.GetLineNumberEnd(), last.GetColumnNumberEnd()); Phx.Symbols.FunctionSymbol coverageVisit = GetFunctionSymbol(module, CoverageVisitAssembly, CoverageVisitType, CoverageVisitMethod); InjectCoverageCall(function, coverageVisit, BlockCounter, first); #if LogBlockDissassembly // Dump the disassembly for the current block Log.StartBlockDisassembly(); foreach (Phx.IR.Instruction instruction in block.Instructions) { Log.WriteBlockDisassembly( instruction.GetMsilOffset(), instruction.ToString(), BlockCounter); } Log.EndBlockDisassembly(); #endif Log.EndBlock(); } // Increment the number of basic blocks BlockCounter++; } function.DeleteFlowGraph(); Log.EndBlocks(); // Dump the disassembly for the current method Log.StartMethodDisassembly(); foreach (KeyValuePair <Phx.IR.Instruction, uint> pair in dissassembly) { Log.WriteBlockDisassembly( pair.Key.GetMsilOffset(), pair.Key.ToString(), pair.Value); } Log.EndMethodDisassembly(); Log.EndMethod(); }