/// <summary>A debugging oriented method, that helps assessing the consistency /// of the given array. Throws an exception if inconsistency is found.</summary> /// <param name="blocksPerOffset"></param> private static void AssertConsistency(BlockNode[] blocksPerOffset, List<CfgNode> knownNodes = null) { int arrayLength = blocksPerOffset.Length; for(int index = 0; index < arrayLength; index++) { BlockNode targetBlock = blocksPerOffset[index]; if (null == targetBlock) { continue; } if ( targetBlock.IsCovering((uint)index, 1) && (object.ReferenceEquals(targetBlock, blocksPerOffset[index]))) { continue; } StringBuilder builder = new StringBuilder(); builder.AppendFormat( "Target block #{0} fails to cover code offset 0x{1:X4}. Offset mapped to block #{2}\r\n", targetBlock.NodeId, index, targetBlock.NodeId); if (null != knownNodes) { builder.Append("Known nodes\r\n"); foreach (CfgNode scannedNode in knownNodes) { builder.AppendFormat("#{0} : {1}\r\n", scannedNode.NodeId, scannedNode.AdditionalInfo); } } string exceptionMessage = builder.ToString(); throw new AssertionException(exceptionMessage); } return; }
internal bool IsSuccessor(BlockNode from) { if (null == from) { throw new ArgumentNullException(); } bool result = (null != _predecessors) && _predecessors.Contains(from); #if DBGCFG if ( ( result && ( (null == from._successors) || !from._successors.Contains(this))) || ( !result && (null != from._successors) && from._successors.Contains(this))) { throw new AssertionException(string.Format( "Non matching predecessor/successor between nodes #{0} and #{1}.", this.NodeId, from.NodeId)); } #endif return result; }
/// <summary>Create basic blocks.</summary> /// <param name="rootNode">The root node of the graph.</param> /// <param name="method">The method which body is involved.</param> /// <param name="sparseInstructions">A sparse array of instructions to be analyzed. /// Some of the entries in this array are expected to be null references /// denoting no instruction starts at that particular offset within the method /// bytecode.</param> private static void CreateBasicBlocks(CfgNode rootNode, IMethod method, DalvikInstruction[] sparseInstructions) { #if DBGCFG List<CfgNode> methodNodes = new List<CfgNode>(); #endif List<BlockNode> unreachableBlocks = new List<BlockNode>(); BlockNode currentBlock = null; bool firstBlock = true; // An array that maps each bytecode byte to it's owning block if any. BlockNode[] blocksPerOffset = new BlockNode[method.ByteCodeSize]; blocksPerOffset[0] = currentBlock; bool cfgDebuggingEnabled = rootNode.DebugEnabled; // Create basic blocks. These are set of consecutive instructions with // each instruction having a single successor that is the next instruction. foreach (DalvikInstruction scannedInstruction in sparseInstructions) { if (null == scannedInstruction) { continue; } #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine("@{0:X4} {1}", scannedInstruction.MethodRelativeOffset, scannedInstruction.GetType().Name); } #endif if (null == currentBlock) { // The previous instruction didn't fail in sequence. Either there is already a // block defined for the current offset or we must create a new one. currentBlock = blocksPerOffset[scannedInstruction.MethodRelativeOffset]; if (null == currentBlock) { currentBlock = new BlockNode(cfgDebuggingEnabled); #if DBGCFG methodNodes.Add(currentBlock); #endif blocksPerOffset[scannedInstruction.MethodRelativeOffset] = currentBlock; if (firstBlock) { CfgNode.Link(rootNode, currentBlock); #if DBGCFG Console.WriteLine("Created first block #{0}", currentBlock.NodeId); #endif firstBlock = false; } else { unreachableBlocks.Add(currentBlock); #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (cfgDebuggingEnabled) { Console.WriteLine("Created unreachable block #{0}", currentBlock.NodeId); } #endif } } #if DBGCFG else { if (cfgDebuggingEnabled) { Console.WriteLine("Reusing block #{0}", currentBlock.NodeId); } } #endif } else { // Last instruction failed in sequence. However we may have already defined a // block for the current offset. BlockNode alreadyDefined = blocksPerOffset[scannedInstruction.MethodRelativeOffset]; if ( (null != alreadyDefined) && !object.ReferenceEquals(currentBlock, alreadyDefined)) { #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine( "Linking block #{0} to block #{1} and switching to the later", currentBlock.NodeId, alreadyDefined.NodeId); } #endif // make the already defined the current block. CfgNode.Link(currentBlock, alreadyDefined); currentBlock = alreadyDefined; } #if DBGCFG else { if (cfgDebuggingEnabled) { Console.WriteLine("Continuing with block #{0}", currentBlock.NodeId); } } #endif } currentBlock.Bind(scannedInstruction); for(uint sizeIndex = 0; sizeIndex < scannedInstruction.InstructionSize; sizeIndex++) { int offsetIndex = (int)(scannedInstruction.MethodRelativeOffset + sizeIndex); if ( (null != blocksPerOffset[offsetIndex]) && !object.ReferenceEquals(blocksPerOffset[offsetIndex], currentBlock)) { throw new ApplicationException(); } blocksPerOffset[offsetIndex] = currentBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); #endif } // Scan other targets if any. uint[] otherOffsets = scannedInstruction.AdditionalTargetMethodOffsets; if (null == otherOffsets) { if (!scannedInstruction.ContinueInSequence) { // Must switch to another block. currentBlock = null; } continue; } // Must create a block for each possible target and link current // block to each of those blocks. for (int index = 0; index < otherOffsets.Length; index++) { uint targetOffset = otherOffsets[index]; BlockNode targetBlock = blocksPerOffset[targetOffset]; if (null == targetBlock) { // Block doesn't exists yet. Create and register it. targetBlock = new BlockNode(currentBlock.DebugEnabled); #if DBGCFG methodNodes.Add(targetBlock); #endif blocksPerOffset[targetOffset] = targetBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (targetBlock.DebugEnabled) { Console.WriteLine("Pre-registering block #{0} @{1:X4}", targetBlock.NodeId, targetOffset); Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, targetBlock.NodeId); } #endif // Link current node and next one. CfgNode.Link(currentBlock, targetBlock); continue; } // The target block already exists albeit it may deserve a split. // if (0 == targetOffset) { continue; } BlockNode splitCandidate = targetBlock; bool splitCandidateIsCurrentBlock = object.ReferenceEquals(splitCandidate, currentBlock); bool splitCandidateAlreadyAligned = !object.ReferenceEquals(blocksPerOffset[targetOffset - 1], splitCandidate); bool linkCurrentToSplitted = true; try { if (splitCandidateAlreadyAligned && !splitCandidateIsCurrentBlock) { // The split candidate actually starts at target address // and is not the current block. No split required. continue; } // Need a split. uint splitAt; bool makeSplitResultCurrent; if (!splitCandidateAlreadyAligned) { splitAt = targetOffset; makeSplitResultCurrent = false; } else { // The target is the first instruction of current block. We // split the last instruction from the current block. if (!splitCandidateIsCurrentBlock) { throw new AssertionException(); } splitAt = scannedInstruction.MethodRelativeOffset; linkCurrentToSplitted = false; // From now on consider the newly created block to be the // current one. makeSplitResultCurrent = true; } #if DBGCFG if (targetBlock.DebugEnabled) { Console.WriteLine("Spliting block #{0} @{1:X4}.", splitCandidate.NodeId, splitAt); } #endif targetBlock = splitCandidate.Split(splitAt); #if DBGCFG methodNodes.Add(targetBlock); if (targetBlock.DebugEnabled) { Console.WriteLine("New block #{0} created while spliting block #{1}.", targetBlock.NodeId, splitCandidate.NodeId); } #endif if (makeSplitResultCurrent) { currentBlock = targetBlock; } // Update offset to block mapping for new splited block. Also // transfer instructions from existing block to split result. for (int scannedOffset = (int)splitAt; scannedOffset < blocksPerOffset.Length; scannedOffset++) { if (!object.ReferenceEquals(blocksPerOffset[scannedOffset], splitCandidate)) { break; } blocksPerOffset[scannedOffset] = targetBlock; } #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); #endif } finally { if (linkCurrentToSplitted) { #if DBGCFG if ((null != currentBlock) && currentBlock.DebugEnabled) { Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, targetBlock.NodeId); } #endif // Link current node and next one. CfgNode.Link(currentBlock, targetBlock); } if (unreachableBlocks.Contains(targetBlock)) { unreachableBlocks.Remove(targetBlock); #if DBGCFG if (targetBlock.DebugEnabled) { Console.WriteLine("Previously unreachable block #{0} now reachable.", targetBlock.NodeId); } #endif } } } // Having other targets force a block reset AND the next instruction to be in // a separate block than the current one, provided the current instruction fails // in sequence. if (scannedInstruction.ContinueInSequence) { uint nextInstructionOffset = scannedInstruction.MethodRelativeOffset + scannedInstruction.InstructionSize; BlockNode nextBlock = blocksPerOffset[nextInstructionOffset]; if (null == nextBlock) { nextBlock = new BlockNode(currentBlock.DebugEnabled); #if DBGCFG methodNodes.Add(nextBlock); #endif blocksPerOffset[nextInstructionOffset] = nextBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (nextBlock.DebugEnabled) { Console.WriteLine("Created next block #{0} @{1:X4}", nextBlock.NodeId, nextInstructionOffset); Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, nextBlock.NodeId); } #endif } // Link current node and next one. CfgNode.Link(currentBlock, nextBlock); } // Next block will always be different from the current one. currentBlock = null; } #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine("Basic blocks creation done."); } #endif // Link allunreachable blocks to the root node. foreach (BlockNode scannedBlock in unreachableBlocks) { CfgNode.Link(rootNode, scannedBlock); } return; }
/// <summary>Split the current node in two separate nodes reltively to the given /// method relative offset. Existing block is left with its orginal predecessors, /// newly create block inherit the successors of the original block and both blocks /// (old and new) are linked together.</summary> /// <param name="methodOffset">The offset within the owning method of the instruction /// that must be migrated to the new node.</param> /// <returns>The newly created block. This block is already linked as a successor of /// the block that has been split.</returns> internal BlockNode Split(uint methodOffset) { int splitIndex; #if DBGCFG if (DebugEnabled) { DalvikInstruction lastInstruction = _instructions[_instructions.Count - 1]; Console.WriteLine("[{0}] ({1:X4}|{2:X4})/{3:X4}", NodeId, _instructions[0].MethodRelativeOffset, lastInstruction.MethodRelativeOffset + lastInstruction.InstructionSize - 1, methodOffset); } #endif // Find index of instruction with method relative offset matching the // given method offset. for (splitIndex = 0; splitIndex < _instructions.Count; splitIndex++) { if (_instructions[splitIndex].MethodRelativeOffset == methodOffset) { break; } } if (splitIndex >= _instructions.Count) { throw new ArgumentException(); } int movedRangeSize = _instructions.Count - splitIndex; DalvikInstruction[] movedRange = new DalvikInstruction[movedRangeSize]; this._instructions.CopyTo(splitIndex, movedRange, 0, movedRange.Length); BlockNode result = new BlockNode(this.DebugEnabled); result._instructions = new List<DalvikInstruction>(movedRange); this._instructions.RemoveRange(splitIndex, movedRangeSize); // Also adjust block size for both blocks. uint sizeDelta = 0; foreach (DalvikInstruction movedIndstruction in result._instructions) { sizeDelta += movedIndstruction.InstructionSize; } this._size -= sizeDelta; result._size = sizeDelta; // We must also transfer existing successors from the splited block to the // newly created one. base.TransferSuccessors(result); // This link MUST occur after successors transfer. CfgNode.Link(this, result); return result; }