From 102839e078f1989d05f5104ffe29aea3ccabf6fe Mon Sep 17 00:00:00 2001 From: "R. Bernstein" Date: Mon, 18 Nov 2024 09:35:52 -0500 Subject: [PATCH] Post dominator refactor (#9) * Color join edges in cfg with dominators * Use peripheries to indicate dominator region heads * Show cfg offsets flags info always * for loop exit is now MidnightBlue * entry block is *not* unreachable, by definition * rename "forward-scope" to "for-finish" * Simplify dotiio "fallthrough" edge handling * Add JOIN_POINT pseudo-op. Add EXTENDED_OPMAP * misc graphviz improvements * Make sure we add a "no-follow' edge after a return * Allow alternate node heads to be brown * Mark join branch when we have a common dominator * Start a better example description * Update README.rst - Clarify dominator region marks - Clarify color in dominator regions better * Color looping arrows green * More info in bb.. like the line number if a block starts on a line. * Print augmented instructions on request * Finish BLOCK_JOIN first pass... Things work more or less. More small changes and tweaks are probably needed. Like adding an edge in a "FOR" statement, moving pseudo op positions around, adding more info in assembly listing, and adding a line number for block 0. But that is for later! --- README.rst | 101 ++++++- control_flow/augment_disasm.py | 249 +++++++++--------- control_flow/bb.py | 105 +++++--- control_flow/build_control_flow.py | 26 +- control_flow/cfg.py | 108 ++++++-- control_flow/dominators.py | 4 +- control_flow/dotio.py | 118 +++++---- control_flow/graph.py | 48 +++- doc-example/count-bits.cpython-38.pyc | Bin 0 -> 249 bytes ...+dom-3.8--count-bits.cpython-38-module.dot | 38 +++ ...+dom-3.8--count-bits.cpython-38-module.png | Bin 0 -> 80639 bytes ...flow-3.8--count-bits.cpython-38-module.png | Bin 0 -> 58891 bytes ...-dom-3.8--count-bits.cpython-38-module.dot | 34 +++ pytest/test_bb.py | 3 +- pytest/test_cfg.py | 3 +- pytest/test_dom.py | 3 +- test/test-all-examples.py | 2 +- test/test-bb.py | 31 ++- test/test-bb2.py | 54 +++- 19 files changed, 651 insertions(+), 276 deletions(-) create mode 100644 doc-example/count-bits.cpython-38.pyc create mode 100644 doc-example/flow+dom-3.8--count-bits.cpython-38-module.dot create mode 100644 doc-example/flow+dom-3.8--count-bits.cpython-38-module.png create mode 100644 doc-example/flow-3.8--count-bits.cpython-38-module.png create mode 100644 doc-example/flow-dom-3.8--count-bits.cpython-38-module.dot diff --git a/README.rst b/README.rst index 53fa9c2..a5646e6 100644 --- a/README.rst +++ b/README.rst @@ -1,14 +1,99 @@ -*Note: the branch ``post-dominator-refactor`` branch README.rst https://github.com/rocky/python-control-flow/blob/post-dominator-refactor/README.rst has the more information.* +Introduction +------------ -This is a Toolkit for getting control flow informaion from Python bytecode +This is a Toolkit for getting control flow information from Python bytecode. Specifically: -* creates basic blocks from Python bytecode -* creates control-flow graph from the basic blocks -* creates a dominator tree -* Graphs via dot the control-flow graph and dominator tree +* Creates basic blocks from Python bytecode. +* Creates control-flow graph from the basic blocks. +* Creates dominator trees and dominator regions for the control flow. +* Graphs via `dot `_ the control-flow graph and dominator tree. -I've used some routines from Romain Gaucher's equip as a starting point. -equip is (c) 2014 by Romain Gaucher +I've used some routines from Romain Gaucher's `equip `_ as a starting point. + +Example +------- + +For now the Python in ``test/test_bb2.py`` show what's up the best. + +Consider this simple Python program taken from my `BlackHat Asia 2024 talk `_: + +.. code-block:: python + + # Program to count the number of bits in the integer 6. + i: int = 6 + zero_bits = 0 + one_bits = 0 + while i > 0: # loop point + # loop alternative + if i % 0: + # first alternative + one_bits += 1 + else: + # second alternative + zero_bits += 1 + # join point + i << 1 + # loop-end join point + +You can find this byte-compiled to Python 3.8 bytecode in `doc-example/count-bits.cpython-38.pyc `_. +We can get control flow information using for this program using:: + + python ./test/test-bb2.py doc-example/count-bits.cpython-38.pyc + +After running, in ``/tmp`` you'll find some ``.dot`` files and some ``.png`` images generated for the main routine. + +``flow-3.8--count-bits.cpython-38-module.png`` is a PNG image for the control flow. + +.. image:: doc-example/flow-3.8--count-bits.cpython-38-module.png + +Here is what the colors on the arrows indicate: + +red + the first alternative of a group of two alternatives + +blue + the second alternative of a group of two alternatives + +green + a looping (backwards) jump + +Here is what the line styles on the arrows indicate: + +solid + an unconditional (and forward) jump + +dashed + the fallthough path of a conditional jump + +dotted + the jump path of a conditional jump + +If there is no arrow head on an arrow, then the block follows the +previous block in the bytecode although there is not control flow to +it. We aligng blocks linarly using the offset addresses. You can find +the offset ranges listed inside the block. The entry block has is +marked with an additional border. We also show the basic block number +and block flags. + +Control-Flow with Dominator Regions ++++++++++++++++++++++++++++++++++++ + +In addition to the basic control flow, we also mark and color boxes with dominator regions. + +.. image:: doc-example/flow+dom-3.8--count-bits.cpython-38-module.png + + +Regions with the the same nesting level have the same color. So Basic blocks 3 and 7 are at the same nesting level. Blocks 4 and 5 are at the same nesting level and are the same color. However even though Block 6 is the same color it is not at the same nesting level, although it *is* inside the same dominator region. + +Colors get darker as the region is more nested. + +Here the additional border indicates that a block is part of some non-trivial dominator region. (A "trivial" dominator region is where the block just dominates itself.) + +In addition, if a jump or fallthough jumps out of its dominator region that is shown in brown. If any basic block is jumped to using a jump-out (or end scope) kind of edge, then the box has a brown outline. + +Inside the block text we now add the dominator region number of for a block in parenthesis. For example Basic blocks, 4 and 5 are in dominator region 3 and so are marked "(3)" after their basic block number. The dominator number for a basic block is the same as its basic block number. So Basic Block 3 is also Dominator Region 3. + +Note that even though basic blocks 4 and 5 are at the same indentation level, they are in different *scopes* under basic block 3. diff --git a/control_flow/augment_disasm.py b/control_flow/augment_disasm.py index 6453b14..a785a5e 100644 --- a/control_flow/augment_disasm.py +++ b/control_flow/augment_disasm.py @@ -19,7 +19,14 @@ from control_flow.bb import BBMgr, BasicBlock from control_flow.cfg import ControlFlowGraph -from control_flow.graph import Node, BB_FOR, BB_LOOP, BB_NOFOLLOW +from control_flow.graph import ( + Node, + BB_FOR, + BB_JOIN_POINT, + BB_LOOP, + BB_NOFOLLOW, + ScopeEdgeKind, +) class JumpTarget(IntEnum): @@ -210,6 +217,18 @@ class _ExtendedInstruction(NamedTuple): dominator: Optional[Node] = None +EXTENDED_OPMAP = { + "BB_END": 1001, + "BB_START": 1002, + "BREAK_FOR": 1003, + "BREAK_LOOP": 1004, + "BLOCK_END_FALLTHROUGH_JOIN": 1005, + "BLOCK_END_JUMP_JOIN": 1006, + "JUMP_FOR": 1007, + "JUMP_LOOP": 1008, +} + + class ExtendedInstruction(_ExtendedInstruction, Instruction): """Details for an extended bytecode operation @@ -372,10 +391,11 @@ def augment_instructions( """Augment instructions in fn_or_code with dominator information""" current_block = cfg.entry_node - dom_tree = cfg.dom_tree - bb2dom_node = {node.bb: node for node in dom_tree.nodes} - version_tuple = opc.version_tuple - # block_stack = [current_block] + # Create a mapping from a basic block, which has dominator information, to a graph node. + # Note: unreachable basic blocks do not have a "doms" field. + bb2dom_node = { + bb: next(iter(bb.doms - bb.dom_set)) for bb in cfg.blocks if hasattr(bb, "doms") + } starts = {current_block.start_offset: current_block} dom_reach_ends = {} @@ -405,6 +425,8 @@ def augment_instructions( # These are done for basic blocks, dominators, # and jump target locations. offset = inst.offset + opname = inst.opname + opcode = inst.opcode new_bb = starts.get(offset, None) if new_bb: @@ -414,21 +436,47 @@ def augment_instructions( new_dom = bb2dom_node.get(bb, dom) if new_dom is not None: dom = new_dom - dom_number = dom.bb.number + # dom_number = dom.bb.number reach_ends = dom_reach_ends.get(dom.reach_offset, []) reach_ends.append(dom) dom_reach_ends[dom.reach_offset] = reach_ends - if inst.opcode in bb_mgr.FOR_INSTRUCTIONS or BB_LOOP in bb.flags: + if opcode in bb_mgr.FOR_INSTRUCTIONS or BB_LOOP in bb.flags: # Use the basic block of the block loop successor, # this is the main body of the loop, as the block to # check for leaving the loop. loop_block_dom_set = tuple(dom.bb.successors)[0].doms loop_stack.append((dom, loop_block_dom_set, inst)) + # For now we will assume that edges are sorted so in outermost-to-innermost nesting order. + # Add any psuedo-token join markers + if offset in cfg.offset2edges: + for edge in reversed(cfg.offset2edges[offset]): + if edge.scoping_kind == ScopeEdgeKind.Join: + from_bb_number = edge.source.bb.number + op_name = "BLOCK_END_FALLTHROUGH_JOIN" if edge.kind == "fallthrough" else "BLOCK_END_JUMP_JOIN" + pseudo_inst = ExtendedInstruction( + opname=op_name, + opcode=EXTENDED_OPMAP[op_name], + optype="pseudo", + inst_size=0, + arg=from_bb_number, + argval=edge, + argrepr=f"from basic block #{from_bb_number}", + has_arg=True, + offset=offset, + starts_line=None, + is_jump_target=False, + has_extended_arg=False, + positions=None, + basic_block=bb, + dominator=dom, + ) + augmented_instrs.append(pseudo_inst) + pseudo_inst = ExtendedInstruction( opname="BB_START", - opcode=1001, + opcode=EXTENDED_OPMAP["BB_START"], optype="pseudo", inst_size=0, arg=bb.number, @@ -457,41 +505,41 @@ def augment_instructions( # FIXME: this shouldn't be needed bb = dom.bb - if inst.opcode in opc.JUMP_OPS: + if opcode in opc.JUMP_OPS: jump_target = inst.argval target_inst = instructions[offset2inst_index[jump_target]] target_bb = offset2bb[target_inst.offset] target_dom_set = target_bb.dom_set if inst.argval < offset: - # Classify backward loop jumps - pseudo_op_name = ( - "JUMP_FOR" - if target_inst.opcode in bb_mgr.FOR_INSTRUCTIONS - else "JUMP_LOOP" - ) - pseudo_inst = ExtendedInstruction( - opname=pseudo_op_name, - opcode=1001, - optype="pseudo", - inst_size=0, - arg=target_dom_set, - argval=target_dom_set, - argrepr=f"{target_dom_set}", - has_arg=True, - offset=offset, - starts_line=None, - is_jump_target=False, - has_extended_arg=False, - positions=None, - basic_block=bb, - dominator=dom, - ) - augmented_instrs.append(pseudo_inst) + if opcode in bb_mgr.JUMP_UNCONDITIONAL: + # Classify backward loop jumps + pseudo_op_name = ( + "JUMP_FOR" + if target_inst.opcode in bb_mgr.FOR_INSTRUCTIONS + else "JUMP_LOOP" + ) + pseudo_inst = ExtendedInstruction( + opname=pseudo_op_name, + opcode=EXTENDED_OPMAP[pseudo_op_name], + optype="pseudo", + inst_size=0, + arg=target_dom_set, + argval=target_dom_set, + argrepr=f"{target_dom_set}", + has_arg=True, + offset=offset, + starts_line=None, + is_jump_target=False, + has_extended_arg=False, + positions=None, + basic_block=bb, + dominator=dom, + ) + augmented_instrs.append(pseudo_inst) else: # Not backward jump, Note: if jump == offset, then we have an # infinite loop. We won't check for that here though. # Check for jump break out of a loop - loop_related_jump = False if len(loop_stack) > 0: # Check for loop-related jumps such as those that # can occur from break, continue. Note: we also @@ -511,7 +559,7 @@ def augment_instructions( pseudo_op_name = "BREAK_LOOP" pseudo_inst = ExtendedInstruction( opname=pseudo_op_name, - opcode=1002, + opcode=EXTENDED_OPMAP[pseudo_op_name], optype="pseudo", inst_size=0, arg=target_dom_set, @@ -528,33 +576,7 @@ def augment_instructions( dominator=dom, ) augmented_instrs.append(pseudo_inst) - loop_related_jump = True pass - if not loop_related_jump: - # Classify jumps that jump to the join of some - # high-level Python block - # We find the join offset using reverse dominators? - # FIXME: complete... - - # if jump_target == follow_bb_offset: - # pseudo_inst = ExtendedInstruction( - # "JUMP_END_BLOCK", - # 1002, - # "pseudo", - # 0, - # target_dom_set, - # target_dom_set, - # f"{target_dom_set}", - # True, - # offset, - # None, - # False, - # False, - # bb, - # dom, - # ) - # augmented_instrs.append(pseudo_inst) - pass block_kind = jump_target_kind.get(offset) if block_kind is not None: @@ -580,8 +602,8 @@ def augment_instructions( augmented_instrs.append(pseudo_inst) extended_inst = ExtendedInstruction( - opname=inst.opname, - opcode=inst.opcode, + opname=opname, + opcode=opcode, optype=inst.optype, inst_size=inst.inst_size, arg=inst.arg, @@ -604,7 +626,7 @@ def augment_instructions( if bb: pseudo_inst = ExtendedInstruction( opname="BB_END", - opcode=1002, + opcode=EXTENDED_OPMAP["BB_END"], optype="pseudo", inst_size=0, arg=bb.number, @@ -624,72 +646,45 @@ def augment_instructions( if bb.flags in [BB_FOR, BB_LOOP]: loop_stack.pop() - dom_list = dom_reach_ends.get(offset, None) - if dom_list is not None: - for dom in reversed(dom_list): - dom_number = dom.bb.number - post_end_set = post_ends(dom.bb) - if post_end_set: - pseudo_inst = ExtendedInstruction( - opname="BLOCK_END_JOIN", - opcode=1003, - optype="pseudo", - inst_size=0, - arg=dom_number, - argval=dom_number, - argrepr=f"Basic Block {post_end_set}", - has_arg=True, - offset=offset, - starts_line=None, - is_jump_target=False, - has_extended_arg=False, - positions=None, - start_offset=None, - basic_block=dom.bb, - dominator=dom, - ) - augmented_instrs.append(pseudo_inst) - pass - pass - - # We have a dummy bb at the end+1. - # Add the end dominator info for that which should exist - if version_tuple >= (3, 6): - offset += 2 - else: - offset += 1 - # FIXME: DRY with above - dom_list = dom_reach_ends.get(offset, None) - if dom_list is not None: - block_end_join_added = False - for dom in reversed(dom_list): - dom_number = dom.bb.number - post_end_set = post_ends(dom.bb) - if post_end_set and not block_end_join_added: - pseudo_inst = ExtendedInstruction( - opname="BLOCK_END_JOIN_NO_ARG", - opcode=1003, - optype="pseudo", - inst_size=0, - arg=dom_number, - argval=dom_number, - argrepr=f"Basic Block {post_end_set}", - has_arg=False, - offset=offset, - starts_line=None, - is_jump_target=False, - has_extended_arg=False, - positions=None, - basic_block=dom.bb, - dominator=dom, - start_offset=None, - ) - augmented_instrs.append(pseudo_inst) - block_end_join_added = True - pass + # # We have a dummy bb at the end+1. + # # Add the end dominator info for that which should exist + # if version_tuple >= (3, 6): + # offset += 2 + # else: + # offset += 1 + # # FIXME: DRY with above + # dom_list = dom_reach_ends.get(offset, None) + # if dom_list is not None: + # block_end_join_added = False + # for dom in reversed(dom_list): + # dom_number = dom.bb.number + # post_end_set = post_ends(dom.bb) + # if post_end_set and not block_end_join_added: + # pseudo_inst = ExtendedInstruction( + # opname="BLOCK_END_JOIN_NO_ARG", + # opcode=1003, + # optype="pseudo", + # inst_size=0, + # arg=dom_number, + # argval=dom_number, + # argrepr=f"Basic Block {post_end_set}", + # has_arg=False, + # offset=offset, + # starts_line=None, + # is_jump_target=False, + # has_extended_arg=False, + # positions=None, + # basic_block=dom.bb, + # dominator=dom, + # start_offset=None, + # ) + # augmented_instrs.append(pseudo_inst) + # block_end_join_added = True + # pass # for inst in augmented_instrs: # print(inst) + return augmented_instrs diff --git a/control_flow/bb.py b/control_flow/bb.py index 3ca00c2..f22db7a 100644 --- a/control_flow/bb.py +++ b/control_flow/bb.py @@ -1,30 +1,30 @@ # Copyright (c) 2021, 2023-2024 by Rocky Bernstein import sys - from typing import Optional from xdis import next_offset -from xdis.version_info import PYTHON_VERSION_TRIPLE, IS_PYPY from xdis.bytecode import get_instructions_bytes from xdis.op_imports import get_opcode_module +from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE + from control_flow.graph import ( - BB_POP_BLOCK, - BB_SINGLE_POP_BLOCK, - BB_STARTS_POP_BLOCK, - BB_EXCEPT, + BB_BREAK, + BB_END_FINALLY, BB_ENTRY, - BB_TRY, + BB_EXCEPT, BB_EXIT, BB_FINALLY, - BB_END_FINALLY, BB_FOR, - BB_BREAK, BB_JUMP_CONDITIONAL, - BB_JUMP_UNCONDITIONAL, BB_JUMP_TO_FALLTHROUGH, + BB_JUMP_UNCONDITIONAL, BB_LOOP, BB_NOFOLLOW, + BB_POP_BLOCK, BB_RETURN, + BB_SINGLE_POP_BLOCK, + BB_STARTS_POP_BLOCK, + BB_TRY, FLAG2NAME, ) @@ -51,7 +51,7 @@ def get_jump_val(jump_arg: int, version: tuple) -> int: return jump_arg * 2 if version[:2] >= (3, 10) else jump_arg -class BasicBlock(object): +class BasicBlock: """Extended Basic block from the bytecode. An extended basic block has a single entry. It can have multiple exits though, @@ -73,12 +73,13 @@ class BasicBlock(object): def __init__( self, - start_offset, - end_offset, - follow_offset, - loop_offset, + start_offset: int, + end_offset: int, + follow_offset: int, + loop_offset: int, flags=set(), jump_offsets=set(), + starts_line=None, ): global end_bb @@ -110,6 +111,8 @@ def __init__( # "Flags" is a set of interesting bits about the basic block. # Elements of the bits are BB_... constants self.flags = flags + + self.starts_line = starts_line self.index = (start_offset, end_offset) # Lists of predecessor and successor basic blocks. @@ -156,7 +159,8 @@ def __repr__(self): flag_text = ", flags={%s}" % flag_str else: flag_text = "" - return "BasicBlock(#%d range: %s%s, follow_offset=%s, edge_count=%d%s%s)" % ( + line_text = "" if self.starts_line is None else f", line {self.starts_line}" + return "BasicBlock(#%d range: %s%s, follow_offset=%s, edge_count=%d%s%s%s)" % ( self.number, self.index, flag_text, @@ -164,6 +168,7 @@ def __repr__(self): self.edge_count, jump_text, exception_text, + line_text, ) def __str__(self): @@ -175,11 +180,13 @@ def __str__(self): exception_text = f", exceptions={sorted(self.exception_offsets)}" else: exception_text = "" - return "BasicBlock(#%d range: %s, %s%s)" % ( + line_text = "" if self.starts_line is None else f", line {self.starts_line}" + return "BasicBlock(#%d range: %s%s%s%s)" % ( self.number, self.index, jump_text, exception_text, + line_text, ) # Define "<" so we can compare and sort basic blocks. @@ -199,7 +206,14 @@ def __init__(self, version=PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY): self.opcode = opcode = get_opcode_module(version) - self.EXCEPT_INSTRUCTIONS = {opcode.opmap["POP_TOP"]} + # FIXME: why is POP_TOP *ever* an except instruction? + # If it can be a start an except instruction, then we need + # something more to determine this. + if version < (3, 10): + self.EXCEPT_INSTRUCTIONS = {opcode.opmap["POP_TOP"]} + else: + self.EXCEPT_INSTRUCTIONS = set() + if "SETUP_FINALLY" in opcode.opmap: self.FINALLY_INSTRUCTIONS = {opcode.opmap["SETUP_FINALLY"]} self.FOR_INSTRUCTIONS = {opcode.opmap["FOR_ITER"]} @@ -207,7 +221,10 @@ def __init__(self, version=PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY): self.JREL_INSTRUCTIONS = set(opcode.hasjrel) self.JUMP_INSTRUCTIONS = self.JABS_INSTRUCTIONS | self.JREL_INSTRUCTIONS if "JUMP_ABSOLUTE" in opcode.opmap: - self.JUMP_UNCONDITIONAL = {opcode.opmap["JUMP_ABSOLUTE"], opcode.opmap["JUMP_FORWARD"]} + self.JUMP_UNCONDITIONAL = { + opcode.opmap["JUMP_ABSOLUTE"], + opcode.opmap["JUMP_FORWARD"], + } self.POP_BLOCK_INSTRUCTIONS = set() if "POP_BLOCK" in opcode.opmap: @@ -222,8 +239,6 @@ def __init__(self, version=PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY): self.LOOP_INSTRUCTIONS = set() self.TRY_INSTRUCTIONS = set() self.END_FINALLY_INSTRUCTIONS = set() - self.LOOP_INSTRUCTIONS = set() - self.TRY_INSTRUCTIONS = set() if version < (3, 10): if version < (3, 8): @@ -234,7 +249,6 @@ def __init__(self, version=PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY): # FIXME: add WITH_EXCEPT_START self.END_FINALLY_INSTRUCTIONS = {opcode.opmap["END_FINALLY"]} pass - else: self.EXCEPT_INSTRUCTIONS.add(opcode.opmap["RAISE_VARARGS"]) @@ -248,8 +262,11 @@ def __init__(self, version=PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY): if opname in opcode.opmap: self.JUMP_CONDITIONAL.add(opcode.opmap[opname]) - self.NOFOLLOW_INSTRUCTIONS = {opcode.opmap["RETURN_VALUE"], opcode.opmap["YIELD_VALUE"], - opcode.opmap["RAISE_VARARGS"]} + self.NOFOLLOW_INSTRUCTIONS = { + opcode.opmap["RETURN_VALUE"], + opcode.opmap["YIELD_VALUE"], + opcode.opmap["RAISE_VARARGS"], + } if "RERAISE" in opcode.opmap: self.NOFOLLOW_INSTRUCTIONS.add(opcode.opmap["RAISE_VARARGS"]) @@ -266,7 +283,14 @@ def __init__(self, version=PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY): self.JUMP_UNCONDITIONAL.add(opcode.opmap[opname]) def add_bb( - self, start_offset, end_offset, loop_offset, follow_offset, flags, jump_offsets + self, + start_offset: int, + end_offset: int, + loop_offset: int, + follow_offset: int, + flags: int, + jump_offsets: set, + starts_line: Optional[int] = None, ): if BB_STARTS_POP_BLOCK in flags and start_offset == end_offset: @@ -280,6 +304,7 @@ def add_bb( flags=flags, jump_offsets=jump_offsets, loop_offset=loop_offset, + starts_line=starts_line, ) self.bb_list.append(block) @@ -295,6 +320,7 @@ def add_bb( def basic_blocks( code, + linestarts: dict, offset2inst_index, version_tuple=PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY, @@ -314,12 +340,13 @@ def basic_blocks( loop_targets = set() instructions = list( get_instructions_bytes( - code.co_code, - bb.opcode, - code.co_varnames, - code.co_names, - code.co_consts, - code.co_cellvars, + bytecode=code.co_code, + opc=bb.opcode, + varnames=code.co_varnames, + names=code.co_names, + constants=code.co_consts, + cells=code.co_cellvars, + linestarts=linestarts ) ) for i, inst in enumerate(instructions): @@ -351,9 +378,7 @@ def basic_blocks( else: end_bb_offset = end_offset + 1 - end_block, _, _ = bb.add_bb( - end_bb_offset, end_bb_offset, None, None, {BB_EXIT}, [] - ) + end_block, _, _ = bb.add_bb(end_bb_offset, end_bb_offset, None, None, {BB_EXIT}, []) start_offset = 0 end_offset = -1 @@ -389,7 +414,6 @@ def basic_blocks( loop_offset = offset elif offset == endloop_offsets[-1]: endloop_offsets.pop() - pass if op in bb.LOOP_INSTRUCTIONS: flags.add(BB_LOOP) @@ -403,6 +427,7 @@ def basic_blocks( follow_offset, flags, jump_offsets, + inst.starts_line, ) loop_offset = None if BB_TRY in block.flags: @@ -421,6 +446,7 @@ def basic_blocks( end_offset, flags, jump_offsets, + inst.starts_line, ) loop_offset = None if BB_TRY in block.flags: @@ -473,6 +499,7 @@ def basic_blocks( follow_offset, flags, jump_offsets, + inst.starts_line, ) loop_offset = None start_offset = follow_offset @@ -491,7 +518,10 @@ def basic_blocks( flags.add(BB_JUMP_UNCONDITIONAL) if jump_offset == follow_offset: flags.add(BB_JUMP_TO_FALLTHROUGH) - pass + else: + # Also note that the edge does not + # fall through to the next block. + flags.add(BB_NOFOLLOW) block, flags, jump_offsets = bb.add_bb( start_offset, end_offset, @@ -499,6 +529,7 @@ def basic_blocks( follow_offset, flags, jump_offsets, + inst.starts_line, ) loop_offset = None if BB_TRY in block.flags: @@ -517,6 +548,7 @@ def basic_blocks( follow_offset, flags, jump_offsets, + inst.starts_line, ) loop_offset = None if BB_TRY in block.flags: @@ -535,6 +567,7 @@ def basic_blocks( follow_offset, flags, jump_offsets, + inst.starts_line, ) loop_offset = None start_offset = follow_offset diff --git a/control_flow/build_control_flow.py b/control_flow/build_control_flow.py index 6b5d052..aa3cdb3 100644 --- a/control_flow/build_control_flow.py +++ b/control_flow/build_control_flow.py @@ -2,7 +2,6 @@ import sys from xdis.codetype.base import iscode -from xdis.disasm import disco from xdis.op_imports import get_opcode_module from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE @@ -22,6 +21,8 @@ def build_and_analyze_control_flow( code_version_tuple=PYTHON_VERSION_TRIPLE[:2], func_or_code_timestamp=None, func_or_code_name: str = "", + debug: dict = {}, + file_part: str = "", ): """ Compute control-flow graph, dominator information, and @@ -52,7 +53,8 @@ def build_and_analyze_control_flow( opc = get_opcode_module(code_version_tuple, VARIANT) offset2inst_index = {} - bb_mgr = basic_blocks(code, offset2inst_index, code_version_tuple) + linestarts = dict(opc.findlinestarts(code, dup_lines=True)) + bb_mgr = basic_blocks(code, linestarts, offset2inst_index, code_version_tuple) # for bb in bb_mgr.bb_list: # print("\t", bb) @@ -63,15 +65,16 @@ def build_and_analyze_control_flow( version = ".".join((str(n) for n in code_version_tuple[:2])) if graph_options in ("all", "control-flow"): write_dot( - func_or_code_name, + f"{file_part}{func_or_code_name}", f"/tmp/flow-{version}-", cfg.graph, write_png=True, exit_node=cfg.exit_node, ) + assert cfg.graph is not None try: - DominatorTree.compute_dominators_in_cfg(cfg, debug_dict.get("dom", False)) + cfg.dom_tree = DominatorTree.compute_dominators_in_cfg(cfg, debug_dict.get("dom", False)) for node in cfg.graph.nodes: if node.bb.nesting_depth < 0: node.is_dead_code = True @@ -83,16 +86,17 @@ def build_and_analyze_control_flow( if graph_options in ("all", "dominators"): write_dot( - func_or_code_name, + f"{file_part}{func_or_code_name}", f"/tmp/flow-dom-{version}-", - cfg.dom_tree, + cfg.dom_forest, write_png=True, exit_node=cfg.exit_node, ) + cfg.classify_edges() if graph_options in ("all",): write_dot( - func_or_code_name, + f"{file_part}{func_or_code_name}", f"/tmp/flow+dom-{version}-", cfg.graph, write_png=True, @@ -102,12 +106,14 @@ def build_and_analyze_control_flow( assert cfg.graph - # print("=" * 30) augmented_instrs = augment_instructions( func_or_code, cfg, opc, offset2inst_index, bb_mgr ) - # for inst in augmented_instrs: - # print(inst.disassemble(opc)) + if graph_options in ("all", "augmented-instructions"): + print("=" * 30) + print("Augmented Instructions:") + for inst in augmented_instrs: + print(inst.disassemble(opc)) # return cs_str except Exception: diff --git a/control_flow/cfg.py b/control_flow/cfg.py index 24810d0..a85c3bb 100644 --- a/control_flow/cfg.py +++ b/control_flow/cfg.py @@ -1,12 +1,15 @@ # Copyright (c) 2021, 2024 by Rocky Bernstein # from operator import attrgetter -from typing import Dict, Optional, Tuple +from typing import Dict, List, Optional, Tuple from control_flow.graph import ( DiGraph, + Edge, Node, + ScopeEdgeKind, TreeGraph, jump_flags, + BB_JOIN_POINT, BB_JUMP_CONDITIONAL, BB_LOOP, BB_NOFOLLOW, @@ -29,16 +32,21 @@ def __init__(self, bb_mgr): self.blocks = bb_mgr.bb_list self.offset2block: Dict[int, Node] = {} self.offset2block_sorted: Tuple[int, Node] = tuple() + self.offset2edges: Dict[int, List[Edge]] = {} self.block_nodes = {} self.graph = None self.entry_node = None self.exit_node = bb_mgr.exit_block # - self.dom_tree: Optional[TreeGraph] = None - # Maximum nesting in control flow grapy. -1 means this hasn't been + # Maximum nesting in control flow graph. -1 means this hasn't been # computed. It is computed when self.dom_tree is computed and also is # stored in there. + + # Result from running dfs_forest. + # FIXME: organize this better. + self.dom_forest: Optional[TreeGraph] = None + self.max_nesting_depth: int = -1 self.analyze(self.blocks, bb_mgr.exit_block) @@ -55,8 +63,22 @@ def analyze(self, blocks, exit_block): self.build_flowgraph(blocks, exit_block) def build_flowgraph(self, blocks, exit_block): + """ + Build a control-flow graph from basic blocks `blocks`. + The exit block is `exit_block`. + """ + g = DiGraph() + def add_edge(source_node, dest_node, edge_kind: str) -> Edge: + new_edge = g.make_add_edge(source_node, dest_node, edge_kind) + target_offset = new_edge.dest.bb.start_offset + if target_offset not in self.offset2edges: + self.offset2edges[target_offset] = [new_edge] + else: + self.offset2edges[target_offset].append(new_edge) + return new_edge + self.block_nodes = {} # Add nodes @@ -132,25 +154,26 @@ def build_flowgraph(self, blocks, exit_block): # Is this dead code? (Remove self loops in calculation) # Entry node, blocks[0] is never unreachable - if not block.predecessors - {block} and block != blocks[0]: + if not (block.predecessors - {block} and block != blocks[0] + or BB_ENTRY in block.flags): block.unreachable = True block = sorted_blocks[i] if block.follow_offset: if BB_NOFOLLOW in block.flags: kind = "no fallthrough" - g.make_add_edge( + add_edge( self.block_nodes[block], self.exit_block, "exit edge" ) else: kind = "fallthrough" - g.make_add_edge( + add_edge( self.block_nodes[block], self.block_nodes[self.block_offsets[block.follow_offset]], kind, ) elif BB_EXIT not in block.flags: - g.make_add_edge(self.block_nodes[block], self.exit_block, "exit edge") + add_edge(self.block_nodes[block], self.exit_block, "exit edge") # Connect the current block to its jump targets for jump_index in block.jump_offsets: @@ -160,31 +183,31 @@ def build_flowgraph(self, blocks, exit_block): target_block = self.block_offsets[jump_index] if jump_index > block.start_offset: if BB_LOOP in block.flags: - edge_type = "forward-scope" + edge_kind = "for-finish" elif BB_JUMP_CONDITIONAL in self.block_nodes[block].flags: - edge_type = "forward-conditional" + edge_kind = "forward-conditional" else: - edge_type = "forward" + edge_kind = "forward" else: - edge_type = "looping" + edge_kind = "looping" pass if self.block_nodes[target_block] == self.block_nodes[block]: - edge_type = "self-loop" + edge_kind = "self-loop" - g.make_add_edge( + add_edge( self.block_nodes[block], self.block_nodes[target_block], - edge_type, + edge_kind, ) pass pass for jump_index in block.exception_offsets: source_block = self.block_offsets[jump_index] assert jump_index <= source_block.start_offset - edge_type = "exception" - g.make_add_edge( - self.block_nodes[source_block], self.block_nodes[block], edge_type + edge_kind = "exception" + add_edge( + self.block_nodes[source_block], self.block_nodes[block], edge_kind ) pass pass @@ -192,6 +215,57 @@ def build_flowgraph(self, blocks, exit_block): self.graph = g return + def classify_edges(self): + """ + Classify edges into alternate edges, looping edges, or join edges. + There is a lower-level classification going on in edge.kind. + """ + + for edge in self.graph.edges: + + if edge.kind == "no fallthrough": + # Edge is not to be followed. + continue + + # If the immediate dominator of the source and destination + # node is the same, then we have an alternate edge. + # If the the edge is a backwards jump, then it is a looping edge + # If the edge is not looping and the immediate dominator is + # not the same, then we have a join edge. + + # Looping edges have already been classified, so use those when + # we can. + if edge.kind in ("looping", "self-loop"): + edge.scoping_kind = ScopeEdgeKind.Looping + continue + source_block = edge.source.bb + target_block = edge.dest.bb + + if source_block.unreachable: + continue + + # print(f"Block #{source_block.number} -> Block #{target_block.number}") + # if (source_block.number, target_block.number) == (2, 4): + # from trepan.api import debug; debug() + + if source_block.number == self.dom_tree.doms[target_block].number: + # Jump to target starts a new scope. + # Example: + # if then ... end + edge.scoping_kind = ScopeEdgeKind.Alternate + elif (self.dom_tree.doms[source_block] > self.dom_tree.doms[target_block] + or self.dom_tree.doms[source_block] == self.dom_tree.doms[target_block]): + # The source block is jumping or falling out of a scope: its + # `dom` or `scope number` is more nested than the target scope. + # Examples: + # "if ... else ... end" or + # "if ... end" or + # "while ... break ... end + edge.scoping_kind = ScopeEdgeKind.Join + target_block.flags.add(BB_JOIN_POINT) + pass + return + def get_node(self, offset: int) -> Node: block = self.offset2block.get(offset, None) if block is not None: diff --git a/control_flow/dominators.py b/control_flow/dominators.py index b79d854..3639141 100644 --- a/control_flow/dominators.py +++ b/control_flow/dominators.py @@ -20,7 +20,7 @@ def __str__(self) -> str: class DominatorTree: """Handles the dominator trees, dominator, post-dominator - releation, and the computation of the dominance/post-dominance + relation, and the computation of the dominance/post-dominance frontier. """ @@ -37,7 +37,7 @@ def __init__(self, cfg, debug=False): @classmethod def compute_dominators_in_cfg(cls, cfg, debug): - DominatorTree(cfg, debug) + return DominatorTree(cfg, debug) def build(self): entry = self.cfg.entry_node diff --git a/control_flow/dotio.py b/control_flow/dotio.py index 8fc29cf..11b8f1f 100644 --- a/control_flow/dotio.py +++ b/control_flow/dotio.py @@ -14,24 +14,16 @@ BB_ENTRY, BB_EXIT, BB_END_FINALLY, + BB_JOIN_POINT, BB_JUMP_TO_FALLTHROUGH, BB_JUMP_UNCONDITIONAL, BB_NOFOLLOW, - Node, + ScopeEdgeKind, format_flags_with_width, ) -DOT_STYLE: Final = """ - graph[fontsize=10 fontname="DejaVu Sans Mono"]; - - mclimit=1.5; - rankdir=TD; ordering=out; - color="#efefef"; - - node[shape=box style=filled fontsize=10 fontname="DejaVu Sans Mono" - fillcolor="#efefef", width=2]; - edge[fontsize=10 fontname="Verdana"]; -""" +DARK_GREEN = "#006400" +GRAY92 = "#ededed" BB_LEVEL_BACKGROUNDS = ( {"name": "DodgerBlue4", "hex": "#104e8b", "bg": "white"}, @@ -46,15 +38,27 @@ {"name": "LightSteelBlue1", "hex": "#cae1ff", "bg": "black"}, ) +DOT_STYLE: Final = f""" + graph[fontsize=10 fontname="DejaVu Sans Mono"]; + + mclimit=1.5; + rankdir=TD; ordering=out; + color="{GRAY92}"; + + node[shape=box style=filled fontsize=10 fontname="DejaVu Sans Mono" + fillcolor="{GRAY92}", width=2]; + edge[fontsize=10 fontname="Verdana"]; +""" + + MAX_COLOR_LEVELS: Final = len(BB_LEVEL_BACKGROUNDS) - 1 flags_prefix: Final = "flags=" FEL: Final = len(flags_prefix) NODE_TEXT_WIDTH = 26 + FEL - -class DotConverter(object): - def __init__(self, graph, exit_node: Optional[Node] = None): +class DotConverter: + def __init__(self, graph): self.g = graph self.exit_node = graph self.buffer = "" @@ -74,8 +78,8 @@ def get_node_colors(self, nesting_depth: int) -> Tuple[str, str]: return color_info["hex"], color_info["bg"] @staticmethod - def process(graph, exit_node: Optional[BasicBlock], is_dominator_format: bool): - converter = DotConverter(graph, exit_node) + def process(graph, exit_node: BasicBlock, is_dominator_format: bool): + converter = DotConverter(graph) converter.run(exit_node, is_dominator_format) return converter.buffer @@ -131,30 +135,31 @@ def add_edge(self, edge, exit_node: BasicBlock, edge_seen): dest_port = "" weight = 1 - if edge.is_join: + if edge.scoping_kind == ScopeEdgeKind.Join: arrow_color = ":brown;0.01" else: arrow_color = "" - color = f'[color="blue:{arrow_color}"]' if edge.is_conditional_jump() else "" + color = f'[color="blue{arrow_color}"]' if edge.is_conditional_jump() else "" if edge.kind in ( "fallthrough", "no fallthrough", - "follow", "exit edge", "dom-edge", "pdom-edge", ): - if edge.kind == "follow": - style = '[style="invis"]' + if edge.kind == "no fallthrough": + style = '[style="dashed"] [arrowhead="none"]' elif edge.kind == "fallthrough": color = f'[color="red{arrow_color}"]' + if BB_NOFOLLOW in edge.source.flags: + style = '[style="dashed"] [arrowhead="none"]' pass if edge.kind != "exit edge": weight = 10 elif edge.kind == "exception": - style = '[color="red"]' + style = f'[color="red{arrow_color}"]' if edge.source.bb.number + 1 == edge.dest.bb.number: weight = 10 else: @@ -167,8 +172,9 @@ def add_edge(self, edge, exit_node: BasicBlock, edge_seen): # edge_port = '[headport=nw] [tailport=sw]'; # edge_port = '[headport=_] [tailport=_]'; else: - if edge.kind == "forward-scope": + if edge.kind == "for-finish": style = '[style="dotted"]' + color = '[color="MediumBlue"]' if edge.source.bb.number + 1 == edge.dest.bb.number: weight = 10 source_port = ":c" @@ -179,16 +185,16 @@ def add_edge(self, edge, exit_node: BasicBlock, edge_seen): dest_port = ":ne" pass elif edge.kind == "self-loop": - edge_port = '[headport=ne, tailport=se, color="#006400"]' + edge_port = f"[headport=ne, tailport=se, color='{DARK_GREEN}{arrow_color}']" pass elif edge.kind == "looping": + color = f'[color="{DARK_GREEN}{arrow_color}"]' if edge.dest.bb.number + 1 == edge.source.bb.number: # For a loop to the immediate predecessor we use # a somewhat straight centered backward arrow. source_port = ":c" dest_port = ":c" else: - color = f'[color="#006400{arrow_color}"]' source_port = ":nw" dest_port = ":sw" pass @@ -215,9 +221,6 @@ def add_edge(self, edge, exit_node: BasicBlock, edge_seen): source_port = ":se" dest_port = ":ne" pass - elif BB_NOFOLLOW in edge.source.flags: - style = '[style="dashed"] [arrowhead="none"]' - weight = 10 if style == "" and edge.source.bb.unreachable: style = '[style="dashed"] [arrowhead="empty"]' @@ -256,37 +259,36 @@ def node_repr(self, node, align, is_exit, is_dominator_format: bool): jump_text = "" reach_offset_text = "" flag_text = "" - if not is_dominator_format: - if not is_exit and len(node.jump_offsets) > 0: - jump_text = f"\\ljumps={sorted(node.jump_offsets)}" - pass + if not is_exit and len(node.jump_offsets) > 0: + jump_text = f"\\ljumps={sorted(node.jump_offsets)}" + pass - if node.flags: - flag_text = "%s%s%s" % ( - align, - flags_prefix, - format_flags_with_width( - node.flags, - NODE_TEXT_WIDTH - FEL, - align + (" " * (len("flags="))), - ), - ) - else: - flag_text = "" - pass + if node.flags: + flag_text = "%s%s%s" % ( + align, + flags_prefix, + format_flags_with_width( + node.flags, + NODE_TEXT_WIDTH - FEL, + align + (" " * (len("flags="))), + ), + ) + else: + flag_text = "" + pass - if hasattr(node, "reach_offset"): - reach_offset_text = "\\lreach_offset=%d" % node.reach_offset - pass + if hasattr(node, "reach_offset"): + reach_offset_text = "\\lreach_offset=%d" % node.reach_offset pass + pass if is_exit: return "flags=exit" - offset_text = "offset: %d..%d" % (node.start_offset, node.end_offset) - l = len(offset_text) - if l < NODE_TEXT_WIDTH: - offset_text += " " * (NODE_TEXT_WIDTH - l) + offset_text = f"offset: {node.start_offset}..{node.end_offset}" + text_len = len(offset_text) + if text_len < NODE_TEXT_WIDTH: + offset_text += " " * (NODE_TEXT_WIDTH - text_len) return f"{offset_text}{flag_text}{jump_text}{reach_offset_text}" @@ -306,9 +308,9 @@ def add_node( if exit_node in {node.bb for node in node.bb.dom_set}: dom_set_len -= 1 if BB_ENTRY in node.bb.flags or dom_set_len > 0: - style = '[shape = "box3d"]' + style = '[shape = "box", peripheries=2]' elif BB_EXIT in node.bb.flags: - style = '[shape = "diamond"]' + style = '[style = "rounded"]' align = "\n" is_exit = True elif not node.bb.predecessors: @@ -318,10 +320,14 @@ def add_node( if is_dominator_format: fillcolor, fontcolor = self.get_node_colors(node.bb.nesting_depth) # print("XXX", node.bb, node.bb.nesting_depth, fillcolor, fontcolor) - style += f'[fontcolor = "{fontcolor}", fillcolor = "{fillcolor}"]' + color = 'color=brown, ' if BB_JOIN_POINT in node.bb.flags else "" + style += f'[{color}fontcolor = "{fontcolor}", fillcolor = "{fillcolor}"]' level = " (%d)" % (node.bb.nesting_depth) if node.bb.nesting_depth >= 0 else "" + if node.bb.starts_line is not None: + level += f", Line {node.bb.starts_line} " + label = '[label="Basic Block %d%s%s%s%s"]' % ( node.number, level, diff --git a/control_flow/graph.py b/control_flow/graph.py index 00790ea..66d49a6 100644 --- a/control_flow/graph.py +++ b/control_flow/graph.py @@ -8,13 +8,16 @@ """ from typing import Optional, Set +from enum import Enum # First or Basic block that we entered on. Usually # at offset 0. # Does this need to be a set? BB_ENTRY = 0 -# Block is at the end and doesn't have a following instruction. +# Block is at the end, and doesn't have a following instruction. +# We have though an edge to the successor *instruction* for assisting displaying +# the control-flow graph the way the program was written. BB_NOFOLLOW = 1 # a SETUP_LOOP instruction marking the beginning of a loop. @@ -77,9 +80,12 @@ # sure the jump arrow points straight down. BB_JUMP_TO_FALLTHROUGH = 15 +# The beginning of the basic block is a join. +BB_JOIN_POINT = 16 + # Basic block ends in a return or an raise that is not inside # a "try" block. -BB_RETURN = 16 +BB_RETURN = 17 # Unreachable block BB_DEAD_CODE = 17 @@ -94,6 +100,7 @@ BB_SINGLE_POP_BLOCK: "single pop block", BB_STARTS_POP_BLOCK: "starts with pop block", BB_EXCEPT: "except", + BB_JOIN_POINT: "join block", BB_JUMP_UNCONDITIONAL: "unconditional", BB_JUMP_CONDITIONAL: "conditional jump", BB_JUMP_TO_FALLTHROUGH: "jump to fallthough", @@ -104,9 +111,31 @@ BB_RETURN: "return", } +# FIXME: some of the classifications may be overkill. +ScopeEdgeKind = Enum( + "ScopeEdgeKind", + [ + # Edge hasn't been computed yet: + "Unknown", + # Edge starts a new scope. + # Example: + # if then ... end + "NewScope", + # Edge jumps from one alternate to the next one + # Example: + # if ... elif ... end + "Alternate", + # Edge joins from an inner scope to an outer one, e.g. + # "if ... else ... end" or + # "if ... end" or + # "while ... break ... end + "Join", + # Edge jumps to a loop head + "Looping", + ], +) jump_flags = set([BB_JUMP_UNCONDITIONAL, BB_BREAK]) -nofollow_flags = set([BB_NOFOLLOW]) def format_flags(flags): @@ -200,15 +229,10 @@ def __init__(self, source, dest, kind, data): self.source = source self.dest = dest self.kind = kind + self.scoping_kind = ScopeEdgeKind.Unknown self.flags = set() self.data = data - # True edge is a "join" edge. Note that a "join" edge - # can be an implicit fallthrough edge. - # Join edges are a non-loop edges where the source - # node's nesting depth jumps to a target of lesser depth. - self.is_join = False - @classmethod def reset(self): self.GLOBAL_COUNTER = 0 @@ -308,7 +332,7 @@ def make_add_node(self, bb): self.add_node(node) return node - def make_add_edge(self, source=None, dest=None, kind=None, data=None): + def make_add_edge(self, source=None, dest=None, kind=None, data=None) -> Edge: edge = DiGraph.make_edge(source=source, dest=dest, kind=kind, data=data) self.add_edge(edge) return edge @@ -380,14 +404,14 @@ def write_dot( return path_safe = name.translate(name.maketrans(" <>", "_[]")) - dot_path = f"{prefix}{path_safe}.dot" + dot_path = f"{prefix}-{path_safe}.dot" open(dot_path, "w").write(graph.to_dot(exit_node, is_dominator_format)) if debug: print(f"{dot_path} written") if write_png: import os - png_path = f"{prefix}{path_safe}.png" + png_path = f"{prefix}-{path_safe}.png" os.system(f"dot -Tpng {dot_path} > {png_path}") if debug: print(f"{png_path} written") diff --git a/doc-example/count-bits.cpython-38.pyc b/doc-example/count-bits.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3325ebd13561c32a85d744f2c6d3a667c87ad849 GIT binary patch literal 249 zcmYjJJ8r^25S{%HHdd^p@*P}&(xtI1l!7u%M6=Dxuv1vTZdk7ixKR#}%ebY8e1$YH z>nd+F@4fkM@;n2=Pxgy9(rYUILr}4z?AAmZu%H1Ma?2{PTnkExVm&KUi8!Qjod wjh$BB;pDy{%;{pHzkKI=7Z+(``E?#tb@aABV4Z$ block_4:nw or + # block_0 -> block_3:ne + # See https://stackoverflow.com/questions/53468814/how-can-i-influence-graphviz-dot-to-prefer-which-edges-can-cross/53472852#53472852 + + block_6:nw -> block_2:sw [weight=1][color="#006400"]; + block_6 -> block_7 [weight=10][style="dashed"] [arrowhead="none"]; + block_5 -> block_6 [weight=10][color="red:brown;0.01"][style="dashed"]; + block_4 -> block_5 [weight=10][style="dashed"] [arrowhead="none"]; + block_4 -> block_6 [weight=1]; + block_3 -> block_4 [weight=10][color="red"][style="dashed"]; + block_3 -> block_5 [weight=1][color="blue"][style="dotted"]; + block_2 -> block_3 [weight=10][color="red"][style="dashed"]; + block_2:se -> block_7:ne [weight=1][color="MediumBlue"][style="dotted"]; + block_1 -> block_2 [weight=10][color="red"][style="dashed"]; +} diff --git a/doc-example/flow+dom-3.8--count-bits.cpython-38-module.png b/doc-example/flow+dom-3.8--count-bits.cpython-38-module.png new file mode 100644 index 0000000000000000000000000000000000000000..f97547951ab8788b30aa806a2a73d2e11a195c58 GIT binary patch literal 80639 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

Q@KHf<(;(LEeeqhY2 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 literal 0 HcmV?d00001 diff --git a/doc-example/flow-3.8--count-bits.cpython-38-module.png b/doc-example/flow-3.8--count-bits.cpython-38-module.png new file mode 100644 index 0000000000000000000000000000000000000000..430b41e68ad26a8556dfd3aecff9923efff5d72e GIT binary patch literal 58891 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%0D35<(&&Gh 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 literal 0 HcmV?d00001 diff --git a/doc-example/flow-dom-3.8--count-bits.cpython-38-module.dot b/doc-example/flow-dom-3.8--count-bits.cpython-38-module.dot new file mode 100644 index 0000000..10d79fc --- /dev/null +++ b/doc-example/flow-dom-3.8--count-bits.cpython-38-module.dot @@ -0,0 +1,34 @@ +digraph G { + graph[fontsize=10 fontname="DejaVu Sans Mono"]; + + mclimit=1.5; + rankdir=TD; ordering=out; + color="#efefef"; + + node[shape=box style=filled fontsize=10 fontname="DejaVu Sans Mono" + fillcolor="#efefef", width=2]; + edge[fontsize=10 fontname="Verdana"]; + + # basic blocks: + block_1 [shape = "box", peripheries=2][label="Basic Block 1 (0)\loffset: 0..20 \lflags=entry \lreach_offset=70\l"]; + block_2 [shape = "box", peripheries=2][label="Basic Block 2 (1)\loffset: 22..28 \lflags=loop, conditional jump \ljumps=[66]\lreach_offset=70\l"]; + block_3 [shape = "box", peripheries=2][label="Basic Block 3 (2)\loffset: 30..36 \lflags=conditional jump \ljumps=[48]\lreach_offset=64\l"]; + block_4 [label="Basic Block 4 (3)\loffset: 38..46 \lflags=no fallthrough,\l unconditional\ljumps=[56]\lreach_offset=46\l"]; + block_5 [label="Basic Block 5 (3)\loffset: 48..54 \lreach_offset=54\l"]; + block_6 [label="Basic Block 6 (3)\loffset: 56..64 \lflags=no fallthrough, except,\l unconditional\ljumps=[22]\lreach_offset=64\l"]; + block_7 [label="Basic Block 7 (2)\loffset: 66..68 \lflags=no fallthrough, return \lreach_offset=68\l"]; + + # Edges should be ordered from innermost block edges to outmost. + # If layout gives ugly edge crossing, change the order or the edges + # and/or add port directions on nodes For example: + # block_1:sw -> block_4:nw or + # block_0 -> block_3:ne + # See https://stackoverflow.com/questions/53468814/how-can-i-influence-graphviz-dot-to-prefer-which-edges-can-cross/53472852#53472852 + + block_3 -> block_4 [weight=10]; + block_3 -> block_5 [weight=10][color="blue"]; + block_3 -> block_6 [weight=10]; + block_2 -> block_3 [weight=10]; + block_2 -> block_7 [weight=10][color="blue"]; + block_1 -> block_2 [weight=10]; +} diff --git a/pytest/test_bb.py b/pytest/test_bb.py index 85fa849..458d3fa 100644 --- a/pytest/test_bb.py +++ b/pytest/test_bb.py @@ -45,7 +45,8 @@ def test_basic(): print(f"{fn_name}: ") dis.dis(fn) print() - bb_mgr = basic_blocks(fn.__code__, offset2inst_index) + # FIXME: add linestarts instead of None below + bb_mgr = basic_blocks(fn.__code__, None, offset2inst_index) check_blocks(bb_mgr.bb_list, fn_name) diff --git a/pytest/test_cfg.py b/pytest/test_cfg.py index ef41cf9..3644904 100644 --- a/pytest/test_cfg.py +++ b/pytest/test_cfg.py @@ -78,7 +78,8 @@ def test_basic(): print(fn.__name__) dis.dis(fn) print() - bb_mgr = basic_blocks(fn.__code__, offset2inst_index) + # FIXME: add linestarts instead of None below + bb_mgr = basic_blocks(fn.__code__, None, offset2inst_index) cfg = ControlFlowGraph(bb_mgr) if DEBUG: write_dot(fn.__name__, f"/tmp/test_cfg-{version}-", cfg.graph, write_png=True) diff --git a/pytest/test_dom.py b/pytest/test_dom.py index e063a87..b99291b 100755 --- a/pytest/test_dom.py +++ b/pytest/test_dom.py @@ -60,7 +60,8 @@ def test_basic(): print(name) dis.dis(fn) print() - bb_mgr = basic_blocks(fn.__code__, offset2inst_index) + # FIXME: add linestarts instead of None below + bb_mgr = basic_blocks(fn.__code__, None, offset2inst_index) cfg = ControlFlowGraph(bb_mgr) if DEBUG: write_dot(name, f"/tmp/test_dom-{version}-", cfg.graph, write_png=True) diff --git a/test/test-all-examples.py b/test/test-all-examples.py index 97e0a89..efccf18 100755 --- a/test/test-all-examples.py +++ b/test/test-all-examples.py @@ -9,7 +9,7 @@ def testing(): assert ( False ), ( - "This should have been replaced via read-in python script with a function called" + "This should have been replaced via a read-in Python script with a function called" " testing" ) diff --git a/test/test-bb.py b/test/test-bb.py index 315678a..036683e 100755 --- a/test/test-bb.py +++ b/test/test-bb.py @@ -15,6 +15,21 @@ def trivial_or(a, b): return a or b +def and3(a, b, c): + return a and b and c + + +def and4(a, b, c, d): + return a and b and c and d + + +def for_simple(a): + x = 0 + for i in a: + x += i + return x + + def if_else(a): if a: a += 1 @@ -86,6 +101,18 @@ def foo2(a): return a +def or2(a, b): + return a or b + + +def or3(a, b, c): + return a or b or c + + +def or4(a, b, c, d): + return a or b or c or d + + def while_if_continue(a): a += 1 while a > 5: @@ -131,7 +158,7 @@ def for_break(): def try_except(): try: a = 1 - except: + except Exception: a = 2 return a @@ -140,7 +167,7 @@ def try_finally(): x = 1 except RuntimeError: x = 2 - except: + except Exception: x = 3 finally: x = 4 diff --git a/test/test-bb2.py b/test/test-bb2.py index 9cbde3a..72bd344 100755 --- a/test/test-bb2.py +++ b/test/test-bb2.py @@ -1,7 +1,11 @@ #!/usr/bin/env python import sys +import os import os.path as osp -from control_flow.__main__ import main +from control_flow.build_control_flow import build_and_analyze_control_flow +from types import CodeType +from xdis.load import check_object_path, load_module +from xdis.version_info import PYTHON_VERSION_TRIPLE def testing(): @@ -18,10 +22,56 @@ def testing(): filename = sys.argv[1] short = "" +stat = os.stat(filename) if filename.endswith(".py"): exec(open(filename).read()) short = osp.basename(filename)[0:-3] + source = open(filename, "r").read() + co = compile(source, filename, "exec") + timestamp = stat.st_mtime + version_tuple = PYTHON_VERSION_TRIPLE + + name = co.co_name + if name.startswith("<"): + name = name[1:] + if name.endswith(">"): + name = name[:-1] + elif filename.endswith(".pyc"): + timestamp = stat.st_mtime short = osp.basename(filename)[0:-4] + pyc_filename = check_object_path(filename) + ( + version_tuple, + timestamp, + _, # magic_int, + co, + _, # is_pypy, + _, # source_size, + _ # sip_hash, + ) = load_module(pyc_filename) + +func_name="" +if len(sys.argv) == 3: + func_name = sys.argv[2] + func_codes = [const for const in co.co_consts if isinstance(const, CodeType) and const.co_name == func_name] + len_func_codes = len(func_codes) + if len_func_codes == 0: + print(f"Did not find a code object named {func_name}") + sys.exit(1) + elif len_func_codes == 1: + co = func_codes[0] + elif len_func_codes > 1: + print(f"Found too many code objects named {func_name}:\n{func_codes}") + sys.exit(1) + -main(testing, short) +build_and_analyze_control_flow( + co, + graph_options="all", + code_version_tuple=version_tuple, + func_or_code_timestamp=timestamp, + func_or_code_name=func_name, + debug={}, + file_part=f"{short}-" +)