/// <summary> /// Get a symbol representing a function that takes an unsigned integer /// parameter. /// </summary> /// <param name="unit">Module calling the function.</param> /// <param name="assembly">Name of the assembly.</param> /// <param name="type">Name of the class.</param> /// <param name="method">Name of the function.</param> /// <returns>FunctionSymbol representing the function.</returns> private static Phx.Symbols.FunctionSymbol GetFunctionSymbol(Phx.PEModuleUnit unit, string assembly, string type, string method) { // Look the function up in the cache Phx.Symbols.FunctionSymbol functionSymbol = null; if (FunctionSymbols.TryGetValue(unit, out functionSymbol) && functionSymbol != null) { return(functionSymbol); } // Create symbols for the assemblies and types Phx.Symbols.AssemblySymbol assemblySymbol = GetAssemblySymbol(unit, assembly); Phx.Symbols.MsilTypeSymbol typeSymbol = GetTypeSymbol(unit, type, assemblySymbol); // Build a symbol reference Phx.Types.FunctionTypeBuilder builder = Phx.Types.FunctionTypeBuilder.New(unit.TypeTable); builder.Begin(); builder.CallingConventionKind = Phx.Types.CallingConventionKind.ClrCall; builder.AppendReturnParameter(unit.TypeTable.VoidType); builder.AppendParameter(unit.TypeTable.UInt32Type); // Create the function symbol Phx.Name name = Phx.Name.New(unit.Lifetime, method); functionSymbol = Phx.Symbols.FunctionSymbol.New(unit.SymbolTable, 0, name, builder.GetFunctionType(), Phx.Symbols.Visibility.GlobalReference); typeSymbol.InsertInLexicalScope(functionSymbol, name); // Create a type Phx.Types.AggregateType aggregate = Phx.Types.AggregateType.NewDynamicSize(unit.TypeTable, typeSymbol); aggregate.IsDefinition = false; aggregate.AppendMethodSymbol(functionSymbol); // Cache the function FunctionSymbols[unit] = functionSymbol; return(functionSymbol); }
/// <summary> /// Write out the instrumented binary. /// </summary> /// <param name="unit">Instrumented unit.</param> protected override void Execute(Phx.Unit unit) { if (!unit.IsPEModuleUnit) { return; } // Write the instrumented binary Phx.PEModuleUnit executable = unit.AsPEModuleUnit; Phx.PE.Writer writer = Phx.PE.Writer.New( Phx.GlobalData.GlobalLifetime, Application.Output.GetValue(null), null, // Writing an instrumented PDB currently crashes executable, executable.SymbolTable, executable.Architecture, executable.Runtime); writer.Write(); }
/// <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(); }
/// <summary> /// Instrument the assembly. /// </summary> /// <returns>Termination mode of the processing.</returns> public static Phx.Term.Mode Process() { string currentAssembly = Application.Input.GetValue(null); // Create the log file with details about the basic blocks using (LogWriter log = new LogWriter(Path.ChangeExtension(currentAssembly, "Coverage.xml"))) { log.Start(); Phx.Output.WriteLine("Instrumenting code coverage for " + currentAssembly + " ..."); log.StartAssembly(currentAssembly); // Get the architecture and runtime from the existing assembly Phx.PEModuleUnit oldModule = Phx.PEModuleUnit.Open(currentAssembly); Phx.Targets.Architectures.Architecture architecture = oldModule.Architecture; Phx.Targets.Runtimes.Runtime runtime = oldModule.Runtime; #if VS2010 string clrVersion = oldModule.ClrVersionString; #endif oldModule.Close(); oldModule.Delete(); // Create an empty program to contain the instrumented code Phx.Lifetime lifetime = Phx.Lifetime.New(Phx.LifetimeKind.Global, null); Phx.ProgramUnit program = Phx.ProgramUnit.New( lifetime, null, Phx.GlobalData.TypeTable, architecture, runtime); Phx.PEModuleUnit module = Phx.PEModuleUnit.New( lifetime, Phx.Name.New(lifetime, Path.GetFullPath(currentAssembly)), program, Phx.GlobalData.TypeTable, architecture, runtime); // Set to metadata version 2 if none was copied #if VS2010 if (clrVersion == null) { clrVersion = PreferredClrVersion; module.ClrVersionString = clrVersion; } #endif // Dev10 Phoenix seems to require this fix #if VS2010 module.RaiseMsilOnly = true; #endif // Create the phase list: // 1. For each function // a. Raise the binary executable code to LIR // b. Instrument function with code coverage calls // c. Encode the instrumented code // 2. Emit the instrumented program as a binary Phx.Phases.PhaseConfiguration phases = Phx.Phases.PhaseConfiguration.New(lifetime, "CodeCoverage Phases"); phases.PhaseList.AppendPhase(Phx.PE.ReaderPhase.New(phases)); Phx.Phases.PhaseList functionPhases = Phx.PE.UnitListPhaseList.New(phases, Phx.PE.UnitListWalkOrder.PrePass); functionPhases.AppendPhase(Phx.PE.RaiseIRPhase.New(phases, Phx.FunctionUnit.LowLevelIRBeforeLayoutFunctionUnitState)); functionPhases.AppendPhase(InstrumentPhase.New(phases, log)); functionPhases.AppendPhase(EncodePhase.New(phases)); functionPhases.AppendPhase(Phx.PE.DiscardIRPhase.New(phases)); phases.PhaseList.AppendPhase(functionPhases); phases.PhaseList.AppendPhase(EmitPhase.New(phases)); Phx.GlobalData.BuildPlugInPhases(phases); // Run Phoenix using our phases phases.PhaseList.DoPhaseList(module); // Close the log file log.EndAssembly(); log.Close(); } return(Phx.Term.Mode.Normal); }