/// <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); }
/// <summary> /// Dumps the DOT representation of the control-flow graph of the input function unit /// in a file (a DAG file labeled with the name of the function unit) that will be /// further processed by the GameTime Python scripts. /// </summary> /// /// <param name="functionUnit">Phoenix function unit whose control-flow graph is /// to be dumped.</param> /// <param name="sourceNodeId">ID of the BasicBlock in the control-flow /// graph to start the analysis from.</param> /// <param name="sinkNodeId">ID of the BasicBlock in the control-flow /// graph to end the analysis at.</param> /// <param name="config">Configuration object that contains /// GameTime configuration information.</param> /// <param name="projectConfig">ProjectConfiguration object that contains /// project configuration information.</param> public static void DumpCfgToFile(Phx.FunctionUnit functionUnit, uint sourceNodeId, uint sinkNodeId, Utilities.Configuration config, ProjectConfiguration projectConfig) { Console.Out.WriteLine("PHOENIX: Dumping a DOT representation of the " + "control-flow graph..."); string funcName = GetFunctionName(functionUnit); Phx.Graphs.FlowGraph flowGraph = functionUnit.FlowGraph; Phx.Lifetime lifetime = functionUnit.Lifetime; if (projectConfig.debugConfig.DUMP_IR) { Console.Out.WriteLine("PHOENIX: Dumping IR..."); string irFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_PHX_IR); StreamWriter irWriter = new StreamWriter(irFileName, true); irWriter.AutoFlush = true; foreach (Phx.Graphs.BasicBlock block in flowGraph.BasicBlocks) { irWriter.WriteLine("*** BASIC BLOCK " + block.Id + " ***"); foreach (Phx.IR.Instruction instruction in block.Instructions) { irWriter.WriteLine(instruction); } } irWriter.Close(); } /* Perform a reachability analysis from the source node: determine * what nodes and edges can be reached from the source node. */ Phx.BitVector.Sparse blocksVisitedFromSource = Phx.BitVector.Sparse.New(lifetime); Phx.BitVector.Sparse blocksToVisitFromSource = Phx.BitVector.Sparse.New(lifetime); Phx.BitVector.Sparse edgesVisitedFromSource = Phx.BitVector.Sparse.New(lifetime); blocksToVisitFromSource.SetBit(sourceNodeId); /* TODO: Make more efficient. */ while (!blocksToVisitFromSource.IsEmpty) { uint blockToVisitId = blocksToVisitFromSource.RemoveFirstBit(); blocksVisitedFromSource.SetBit(blockToVisitId); Phx.Graphs.BasicBlock blockToVisit = flowGraph.Node(blockToVisitId) as Phx.Graphs.BasicBlock; foreach (Phx.Graphs.FlowEdge edge in blockToVisit.SuccessorEdges) { Phx.Graphs.BasicBlock successor = edge.SuccessorNode; uint succId = successor.Id; uint edgeId = edge.Id; if (!blocksVisitedFromSource.GetBit(succId)) { blocksToVisitFromSource.SetBit(succId); edgesVisitedFromSource.SetBit(edgeId); } } } /* Perform a backward reachability analysis from the sink node: determine * what nodes and edges can be reached from the sink node. */ Phx.BitVector.Sparse blocksVisitedFromSink = Phx.BitVector.Sparse.New(lifetime); Phx.BitVector.Sparse blocksToVisitFromSink = Phx.BitVector.Sparse.New(lifetime); Phx.BitVector.Sparse edgesVisitedFromSink = Phx.BitVector.Sparse.New(lifetime); blocksToVisitFromSink.SetBit(sinkNodeId); while (!blocksToVisitFromSink.IsEmpty) { uint blockToVisitId = blocksToVisitFromSink.RemoveFirstBit(); blocksVisitedFromSink.SetBit(blockToVisitId); Phx.Graphs.BasicBlock blockToVisit = flowGraph.Node(blockToVisitId) as Phx.Graphs.BasicBlock; foreach (Phx.Graphs.FlowEdge edge in blockToVisit.PredecessorEdges) { Phx.Graphs.BasicBlock predecessor = edge.PredecessorNode; uint predId = predecessor.Id; uint edgeId = edge.Id; if (!blocksVisitedFromSink.GetBit(predId)) { blocksToVisitFromSink.SetBit(predId); edgesVisitedFromSink.SetBit(edgeId); } } } /* Determine which blocks and edges in the control-flow graph can be reached from * *both* the source and the sink: this is the section of the control-flow graph * that we are interested in. */ blocksVisitedFromSource.And(blocksVisitedFromSink); edgesVisitedFromSource.And(edgesVisitedFromSink); uint numNodes = (uint)blocksVisitedFromSource.GetBitCount(); uint numEdges = (uint)edgesVisitedFromSource.GetBitCount(); uint numNewNodes = numNodes + numNodes; uint numNewEdges = numEdges + numNodes + 1; /* Mapping between old block IDs and new block IDs. */ Dictionary <uint, uint> idMap = new Dictionary <uint, uint>(); /* Create a writer to write the DAG in DOT format to a file. */ string dagFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_DAG); StreamWriter dagWriter = new StreamWriter(dagFileName); dagWriter.AutoFlush = true; /* Qualify the DAG. */ dagWriter.WriteLine("digraph " + funcName + " {"); uint newMappedBlockId = 1; uint newFakeBlockId = numNodes + 1; foreach (Phx.Graphs.BasicBlock basicBlock in flowGraph.BasicBlocks) { uint blockId = basicBlock.Id; /* Is the block in the portion of the control-flow graph * we are concerned about? */ if (blocksVisitedFromSource.GetBit(blockId)) { uint mappedBlockId; if (idMap.ContainsKey(blockId)) { mappedBlockId = idMap[blockId]; } else { idMap.Add(blockId, newMappedBlockId); mappedBlockId = newMappedBlockId++; } /* Keep track of the edges that may be added to the resulting DAG. */ List <Pair <uint, uint> > edges = new List <Pair <uint, uint> >(); /* First edge that may be added to the DAG: it will be added * if there are successors. */ edges.Add(new Pair <uint, uint>(mappedBlockId, newFakeBlockId)); List <Phx.Graphs.FlowEdge> reverseSuccessorList = new List <Phx.Graphs.FlowEdge>(); foreach (Phx.Graphs.FlowEdge edge in basicBlock.SuccessorEdges) { reverseSuccessorList.Add(edge); } reverseSuccessorList.Reverse(); foreach (Phx.Graphs.FlowEdge edge in reverseSuccessorList) { /* Is the block in the portion of the control-flow graph * we are concerned about? */ Phx.Graphs.BasicBlock successor = edge.SuccessorNode; uint succId = successor.Id; if (blocksVisitedFromSource.GetBit(succId)) { uint mappedSuccId; if (idMap.ContainsKey(succId)) { mappedSuccId = idMap[succId]; } else { idMap.Add(succId, newMappedBlockId); mappedSuccId = newMappedBlockId++; } /* Replace each BasicBlock with two edges, * separated by a new, "fake" node. */ edges.Add(new Pair <uint, uint>(newFakeBlockId, mappedSuccId)); } } if (edges.Count > 1) { /* This node has successors in the "snipped" CFG. */ foreach (Pair <uint, uint> edge in edges) { dagWriter.WriteLine(" " + edge.First + " -> " + edge.Second + ";"); } /* Generate the ID of a "fake" node corresponding to * the next node, if any. */ newFakeBlockId++; } } } /* Add a "dummy" edge from the sink. */ dagWriter.WriteLine(" " + idMap[sinkNodeId] + " -> " + numNewNodes + ";"); dagWriter.WriteLine("}"); dagWriter.Close(); /* Copy the mappings to a file. */ string blockIdMapFile = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_DAG_ID_MAP); StreamWriter blockIdMapWriter = new StreamWriter(blockIdMapFile); blockIdMapWriter.AutoFlush = true; foreach (uint key in idMap.Keys) { uint blockVisitedId = key; uint newBlockId = idMap[key]; blockIdMapWriter.WriteLine(newBlockId + " " + blockVisitedId); } blockIdMapWriter.Close(); }