def add_node(self, node, show_exit): try: not show_exit and BB_EXIT in node.bb.flags except: from trepan.api import debug debug() if not show_exit and BB_EXIT in node.bb.flags: return label = "" style = "" align = "\l" is_exit = False if BB_ENTRY in node.bb.flags: style = '[shape = "oval"]' elif BB_EXIT in node.bb.flags: style = '[shape = "diamond"]' align = "\n" is_exit = True elif not node.bb.predecessors: style = '[style = "dashed"]' pass label = '[label="Basic Block %d%s%s%s"]' % ( node.number, align, self.node_repr(node.bb, align, is_exit), align, ) self.buffer += " block_%d %s%s;\n" % (node.number, style, label)
def deparse_offset(co, name, last_i, errmsg_fn): nodeInfo = None deparsed = deparse_cache.get(co, None) if not deparsed: out = StringIO() try: # FIXME: cache co deparsed = code_deparse(co, out) from trepan.api import debug debug() except: print(sys.exc_info()[1]) if errmsg_fn: errmsg_fn(sys.exc_info()[1]) errmsg_fn("error in deparsing code") deparse_cache[co] = deparsed try: nodeInfo = deparsed_find((name, last_i), deparsed, co) except: if errmsg_fn: errmsg_fn(sys.exc_info()[1]) errmsg_fn("error in deparsing code at offset %d" % last_i) if not nodeInfo: nodeInfo = deparsed_find((name, last_i), deparsed, co) return deparsed, nodeInfo
def run_code(self, code, f_globals=None, f_locals=None, toplevel=True): """run code using f_globals and f_locals in our VM""" frame = self.make_frame(code, f_globals=f_globals, f_locals=f_locals) try: val = self.eval_frame(frame) except Exception: # Until we get test/vmtest.py under control: if self.vmtest_testing: raise if self.last_traceback: self.last_traceback.print_tb() print("%s" % self.last_exception[0].__name__, end="") le1 = self.last_exception[1] tail = "" if le1: if hasattr(le1, "args"): tail = "\n".join(le1.args) else: from trepan.api import debug debug() print(tail) raise # Frame ran to normal completion... check some invariants if toplevel: if self.frames: # pragma: no cover raise PyVMError("Frames left over!") if self.frame and self.frame.stack: # pragma: no cover raise PyVMError("Data left on stack! %r" % self.frame.stack) return val
def run(self): itemq = self.itemq i = 0 for j in range(10): debug() print currentThread(), "Produced One Item:", i itemq.put(i, 1) i += 1 time.sleep(1) pass return
def run(self): itemq=self.itemq i=0 for j in range(10): debug() print(currentThread(), "Produced One Item:", i) itemq.put(i, 1) i+=1 time.sleep(1) pass return
def catch_debug(sig, frame): _logger.info('got signal={0} to go into debugger mode'.format(sig)) from trepan.interfaces import server from trepan.api import debug try: portNum = harvester_config.master.debugger_port except Exception: portNum = 19550 connection_opts = {'IO': 'TCP', 'PORT': portNum} interface = server.ServerInterface(connection_opts=connection_opts) dbg_opts = {'interface': interface} _logger.info('starting debugger on port {0}'.format(portNum)) debug(dbg_opts=dbg_opts)
def gcd(a, b): """ GCD. We assume positive numbers""" # Make: a <= b if a > b: (a, b) = (b, a) if a <= 0: debug(step_ignore=0) return None if a == 1 or b - a == 0: debug(start_opts={'startup-profile': True}) return a return gcd(b - a, a)
def n_c_except_suite(node): node_len = len(node) if not (node_len == 1 and node[0] in ("except_suite", "c_returns")): try: node[1] except: from trepan.api import debug debug() if node_len == 1 and node[0] in ("except_suite", "c_returns"): node = node[0] self.default(node) elif node[1] in ("c_suite_stmts", "c_except_suite"): node = node[1][0] template = ("%+%c%-", 0) self.template_engine(template, node) self.prune()
def wrapped(*args, **kwargs): from trepan.api import debug debug() coro = func(*args, **kwargs) if (coro.__class__ is CoroutineType or coro.__class__ is GeneratorType and coro.gi_code.co_flags & 0x100): # 'coro' is a native coroutine object or an iterable coroutine return coro if (isinstance(coro, _collections_abc.Generator) and not isinstance(coro, _collections_abc.Coroutine)): # 'coro' is either a pure Python generator iterator, or it # implements collections.abc.Generator (and does not implement # collections.abc.Coroutine). return _GeneratorWrapper(coro) # 'coro' is either an instance of collections.abc.Coroutine or # some other object -- pass it through. return coro
def print_tb(tb, limit=None, file=None, verbosity=0): """Print up to 'limit' stack trace entries from the traceback 'tb'. If 'limit' is omitted or None, all entries are printed. If 'file' is omitted or None, the output goes to sys.stderr; otherwise 'file' should be an open file or file-like object with a write() method. """ if file is None: file = sys.stderr if limit is None: if hasattr(sys, 'tracebacklimit'): limit = sys.tracebacklimit n = 0 nodeInfo = None while tb is not None and (limit is None or n < limit): f = tb.tb_frame lineno = tb.tb_lineno co = f.f_code filename = co.co_filename name = co.co_name linecache.checkcache(filename) _print(file, ' File "%s", line %d, in %s' % (filename, lineno, name)) if verbosity > 0: try: deparsed = deparse_code(SYS_VERSION, co) from trepan.api import debug; debug() nodeInfo = deparsed_find((name, f.f_lasti), deparsed, co) except: pass if nodeInfo: extractInfo = deparsed.extract_node_info(nodeInfo) _print(file, extractInfo.selectedLine) _print(file, extractInfo.markerLine) if verbosity > 1: _print(file, "instruction: %s" % (nodeInfo.node)) line = linecache.getline(filename, lineno, f.f_globals) if line: _print(file, ' ' + line.strip()) tb = tb.tb_next n = n+1
def get_constant(self, precision, evaluation, preference="mpmath"): try: d = get_precision(precision, evaluation) except PrecisionValueError: return mpmath_name = self.mpmath_name if d is None: return MachineReal(mp_fn(mpmath_name)) elif preference == "mpmath": result = mp_fn(mpmath_name, d) else: sympy_fn = self.to_sympy() try: result = sympy_fn.n(d) except: from trepan.api import debug; debug() pass return PrecisionReal(result)
def __init__(self, host=CELERY_TREPAN_HOST, port=CELERY_TREPAN_PORT, out=sys.stdout): self.active = True self.out = out self.ident = '{0}:{1}'.format(self.me, port) from trepan.interfaces import server as Mserver connection_opts = {'IO': 'TCP', 'PORT': port} self.intf = Mserver.ServerInterface(connection_opts=connection_opts) host = self.intf.inout.HOST self.host = host if host else '<hostname>' from trepan.api import debug debug() self.port = self.intf.inout.PORT self.dbg_opts = {'interface': self.intf} return
def write(self, *data): from trepan.api import debug debug() if (len(data) == 1) and data[0] == self.indent: diff = max(self.pending_newlines, self.desired_line_number - self.current_line_number) self.f.write('\n' * diff) self.current_line_number += diff self.pending_newlines = 0 if (len(data) == 0) or (len(data) == 1 and data[0] == ''): return out = ''.join((str(j) for j in data)) n = 0 for i in out: if i == '\n': n += 1 if n == len(out): self.pending_newlines = max(self.pending_newlines, n) return elif n: self.pending_newlines = max(self.pending_newlines, n) out = out[n:] break else: break if self.pending_newlines > 0: diff = max(self.pending_newlines, self.desired_line_number - self.current_line_number) self.f.write('\n' * diff) self.current_line_number += diff self.pending_newlines = 0 for i in out[::-1]: if i == '\n': self.pending_newlines += 1 else: break if self.pending_newlines: out = out[:-self.pending_newlines] self.f.write(out)
def test_one(xdis_args, dis_args, has_arg): effect = xstack_effect(*xdis_args) try: check_effect = dis.stack_effect(*dis_args) except: from trepan.api import debug debug() assert effect != -100, ( "%d (%s) needs adjusting; should be: should have effect %d" % (opcode, opname, check_effect)) if has_arg: op_val = "with operand %d" % dis_args[1] else: op_val = "" assert check_effect == effect, ( "%d (%s) %s not okay; effect %d vs %d" % (opcode, opname, op_val, effect, check_effect)) print("%d (%s) is good: effect %d" % (opcode, opname, effect))
def write(self, *data): from trepan.api import debug; debug() if (len(data) == 1) and data[0] == self.indent: diff = max(self.pending_newlines, self.desired_line_number - self.current_line_number) self.f.write('\n'*diff) self.current_line_number += diff self.pending_newlines = 0 if (len(data) == 0) or (len(data) == 1 and data[0] == ''): return out = ''.join((str(j) for j in data)) n = 0 for i in out: if i == '\n': n += 1 if n == len(out): self.pending_newlines = max(self.pending_newlines, n) return elif n: self.pending_newlines = max(self.pending_newlines, n) out = out[n:] break else: break if self.pending_newlines > 0: diff = max(self.pending_newlines, self.desired_line_number - self.current_line_number) self.f.write('\n'*diff) self.current_line_number += diff self.pending_newlines = 0 for i in out[::-1]: if i == '\n': self.pending_newlines += 1 else: break if self.pending_newlines: out = out[:-self.pending_newlines] self.f.write(out)
def test_messages(self): code = ["General::foo", "Foo::bar", "Foo`Bar::baz"] from trepan.api import debug debug() expected = [ [ (MToken.BUILTIN, "General"), (MToken.OPERATOR, "::"), (MToken.MESSAGE, "foo"), ], [(MToken.SYMBOL, "Foo"), (MToken.OPERATOR, "::"), (MToken.MESSAGE, "bar")], [ (MToken.SYMBOL, "Foo`Bar"), (MToken.OPERATOR, "::"), (MToken.MESSAGE, "baz"), ], ] self.verify_all(code, expected)
def build_flowgraph(self, blocks, exit_block): g = DiGraph() self.block_offsets = {} self.block_nodes = {} # Add nodes for block in self.blocks: self.block_offsets[block.start_offset] = block block_node = g.make_add_node(block) self.block_nodes[block] = block_node self.offset2block[block.index[0]] = block_node pass self.exit_block = block_node # Compute a block's immediate predecessors and successors for block in self.blocks: for jump_offset in set( block.jump_offsets) | block.exception_offsets: try: assert jump_offset in self.block_offsets except: from trepan.api import debug debug() successor_block = self.block_offsets[jump_offset] successor_block.predecessors.add(block) block.successors.add(successor_block) if BB_NOFOLLOW in block.flags: exit_block.predecessors.add(block) block.successors.add(exit_block) pass elif (block.follow_offset and (not (jump_flags & block.flags))): assert block.follow_offset in self.block_offsets successor_block = self.block_offsets[block.follow_offset] successor_block.predecessors.add(block) block.successors.add(successor_block) pass assert (len(self.blocks) > 0) self.entry_node = self.blocks[0] sorted_blocks = sorted(self.blocks, key=attrgetter('index')) for i, block in enumerate(sorted_blocks): # Is this this dead code? (Remove self loops in calculation) # Entry node, blocks[0] is never unreachable if not block.predecessors - set([block]) and block != blocks[0]: block.unreachable = True block = sorted_blocks[i] if block.follow_offset: if BB_NOFOLLOW in block.flags: kind = 'no fallthrough' g.make_add_edge(self.block_nodes[block], self.exit_block, 'exit edge') else: kind = 'fallthrough' g.make_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') # Connect the current block to its jump targets for jump_index in block.jump_offsets: target_block = self.block_offsets[jump_index] if jump_index > block.start_offset: if BB_LOOP in block.flags: edge_type = 'forward_scope' else: edge_type = 'forward' else: edge_type = 'backward' pass if self.block_nodes[target_block] == self.block_nodes[block]: edge_type = 'self-loop' g.make_add_edge(self.block_nodes[block], self.block_nodes[target_block], edge_type) pass for jump_index in block.exception_offsets: target_block = self.block_offsets[jump_index] assert jump_index >= block.start_offset edge_type = 'exception' g.make_add_edge(self.block_nodes[block], self.block_nodes[target_block], edge_type) pass pass self.graph = g return
def get_url(q, url): if url == "http://bing.com": from trepan.api import debug; debug() q.put(urllib2.urlopen(url).read())
#!/usr/bin/env python """Something to use to test signal handling. Basically we just need a program that installs a signal handler and sends it a signal. """ import sys, os, signal, time from trepan.api import debug debug() def signal_handler(num, f): print 'signal %d received' % num return signal.signal(signal.SIGUSR1, signal_handler) sleepy_time = 4 print "pid %d" % os.getpid() print "Waiting in time.sleep(%d) for signal USR1." % sleepy_time while True: time.sleep(sleepy_time) os.kill(os.getpid(), signal.SIGUSR1) pass pass
def get_url(q, url): if url == "http://bing.com": from trepan.api import debug debug() q.put(urllib2.urlopen(url).read())
def or_check(self, lhs, n, rule, ast, tokens, first, last): rhs = rule[1] # print("XXX", first, last, rule) # for t in range(first, last): print(tokens[t]) # print("="*40) if (first, last) == (2, 6): from trepan.api import debug; debug() if rhs[0:2] in (("expr_jt", "expr"), ("expr_jitop", "expr"), ("expr_jit", "expr")): if tokens[last] in ASSERT_OPS or tokens[last-1] in ASSERT_OPS: return True # The following test is be the most accurate. It prevents "or" from being # mistake for part of an "assert". # There one might conceivably be "expr or AssertionError" code, but the # likelihood of that is vanishingly small. # The below then is useful until we get better control-flow analysis. # Note it is too hard in the scanner right nowto turn the LOAD_GLOBAL into # int LOAD_ASSERT, however in 3.9ish code generation does this by default. load_global = tokens[last - 1] if load_global == "LOAD_GLOBAL" and load_global.attr == "AssertionError": return True first_offset = tokens[first].off2int() expr_jt = ast[0] if expr_jt == "expr_jitop": jump_true = expr_jt[1] else: jump_true = expr_jt[1][0] jmp_true_target = jump_true.attr last_token = tokens[last] last_token_offset = last_token.off2int() # FIXME: use instructions for all of this if jmp_true_target < first_offset: return False elif jmp_true_target < last_token_offset: return True # If the jmp is backwards if last_token == "POP_JUMP_IF_FALSE" and not self.version in (2.7, 3.5, 3.6): if last_token.attr < last_token_offset: # For a backwards loop, well compare to the instruction *after* # then POP_JUMP... last_token = tokens[last + 1] # HACK alert 3 is the Python < 3.6ish thing. # Convert to using instructions return not ( (last_token_offset <= jmp_true_target <= last_token_offset + 3) or jmp_true_target < tokens[first].off2int() ) elif last_token == "JUMP_FORWARD" and expr_jt.kind != "expr_jitop": # "or" has to fall through to the next statement # FIXME: use instructions for all of this return True return False
def boxes_to_text(self, leaves=None, **options): from trepan.api import debug; debug() if not leaves: leaves = self._leaves return "-CompiledCode-"
def detect_control_flow(self, offset, targets, inst_index): """ Detect type of block structures and their boundaries to fix optimized jumps in python2.3+ """ code = self.code op = self.insts[inst_index].opcode # Detect parent structure parent = self.structs[0] start = parent['start'] end = parent['end'] # Pick inner-most parent for our offset for struct in self.structs: current_start = struct['start'] current_end = struct['end'] if ((current_start <= offset < current_end) and (current_start >= start and current_end <= end)): start = current_start end = current_end parent = struct if op == self.opc.SETUP_LOOP: # We categorize loop types: 'for', 'while', 'while 1' with # possibly suffixes '-loop' and '-else' # Try to find the jump_back instruction of the loop. # It could be a return instruction. start += instruction_size(op, self.opc) target = self.get_target(offset) end = self.restrict_to_parent(target, parent) self.setup_loops[target] = offset if target != end: self.fixed_jumps[offset] = end (line_no, next_line_byte) = self.lines[offset] jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, next_line_byte, False) if jump_back: jump_forward_offset = xdis.next_offset(code[jump_back], self.opc, jump_back) else: jump_forward_offset = None return_val_offset1 = self.prev[self.prev[end]] if (jump_back and jump_back != self.prev_op[end] and self.is_jump_forward(jump_forward_offset)): if (code[self.prev_op[end]] == self.opc.RETURN_VALUE or (code[self.prev_op[end]] == self.opc.POP_BLOCK and code[return_val_offset1] == self.opc.RETURN_VALUE)): jump_back = None if not jump_back: # loop suite ends in return jump_back = self.last_instr(start, end, self.opc.RETURN_VALUE) if not jump_back: return jb_inst = self.get_inst(jump_back) jump_back = self.next_offset(jb_inst.opcode, jump_back) if_offset = None if code[self.prev_op[next_line_byte]] not in self.pop_jump_tf: if_offset = self.prev[next_line_byte] if if_offset: loop_type = 'while' self.ignore_if.add(if_offset) else: loop_type = 'for' target = next_line_byte end = xdis.next_offset(code[jump_back], self.opc, jump_back) else: if self.get_target(jump_back) >= next_line_byte: jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False) jb_inst = self.get_inst(jump_back) jb_next_offset = self.next_offset(jb_inst.opcode, jump_back) if end > jb_next_offset and self.is_jump_forward(end): if self.is_jump_forward(jb_next_offset): if self.get_target(jb_next_offset) == self.get_target(end): self.fixed_jumps[offset] = jb_next_offset end = jb_next_offset elif target < offset: self.fixed_jumps[offset] = jb_next_offset end = jb_next_offset target = self.get_target(jump_back) if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER): loop_type = 'for' else: loop_type = 'while' test = self.prev_op[next_line_byte] if test == offset: loop_type = 'while 1' elif self.code[test] in self.opc.JUMP_OPs: self.ignore_if.add(test) test_target = self.get_target(test) if test_target > (jump_back+3): jump_back = test_target self.not_continue.add(jump_back) self.loops.append(target) self.structs.append({'type': loop_type + '-loop', 'start': target, 'end': jump_back}) after_jump_offset = xdis.next_offset(code[jump_back], self.opc, jump_back) if after_jump_offset != end: self.structs.append({'type': loop_type + '-else', 'start': after_jump_offset, 'end': end}) elif op in self.pop_jump_tf: start = offset + instruction_size(op, self.opc) target = self.insts[inst_index].argval rtarget = self.restrict_to_parent(target, parent) prev_op = self.prev_op # Do not let jump to go out of parent struct bounds if target != rtarget and parent['type'] == 'and/or': self.fixed_jumps[offset] = rtarget return # Does this jump to right after another conditional jump that is # not myself? If so, it's part of a larger conditional. # rocky: if we have a conditional jump to the next instruction, then # possibly I am "skipping over" a "pass" or null statement. pretarget = self.get_inst(prev_op[target]) if (pretarget.opcode in self.pop_jump_if_pop and (target > offset) and pretarget.offset != offset): # FIXME: hack upon hack... # In some cases the pretarget can be a jump to the next instruction # and these aren't and/or's either. We limit to 3.5+ since we experienced there # but it might be earlier versions, or might be a general principle. if self.version < 3.5 or pretarget.argval != target: # FIXME: this is not accurate The commented out below # is what it should be. However grammar rules right now # assume the incorrect offsets. # self.fixed_jumps[offset] = target self.fixed_jumps[offset] = pretarget.offset self.structs.append({'type': 'and/or', 'start': start, 'end': pretarget.offset}) return # The opcode *two* instructions before the target jump offset is important # in making a determination of what we have. Save that. pre_rtarget = prev_op[rtarget] # Is it an "and" inside an "if" or "while" block if op == self.opc.POP_JUMP_IF_FALSE and self.version < 3.6: # Search for another POP_JUMP_IF_FALSE targetting the same op, # in current statement, starting from current offset, and filter # everything inside inner 'or' jumps and midline ifs match = self.rem_or(start, self.next_stmt[offset], self.opc.POP_JUMP_IF_FALSE, target) # If we still have any offsets in set, start working on it if match: is_jump_forward = self.is_jump_forward(pre_rtarget) if (is_jump_forward and pre_rtarget not in self.stmts and self.restrict_to_parent(self.get_target(pre_rtarget), parent) == rtarget): if (code[prev_op[pre_rtarget]] == self.opc.JUMP_ABSOLUTE and self.remove_mid_line_ifs([offset]) and target == self.get_target(prev_op[pre_rtarget]) and (prev_op[pre_rtarget] not in self.stmts or self.get_target(prev_op[pre_rtarget]) > prev_op[pre_rtarget]) and 1 == len(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], self.pop_jump_tf, target)))): pass elif (code[prev_op[pre_rtarget]] == self.opc.RETURN_VALUE and self.remove_mid_line_ifs([offset]) and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], self.pop_jump_tf, target))) | set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], (self.opc.POP_JUMP_IF_FALSE, self.opc.POP_JUMP_IF_TRUE, self.opc.JUMP_ABSOLUTE), pre_rtarget, True)))))): pass else: fix = None jump_ifs = self.inst_matches(start, self.next_stmt[offset], self.opc.POP_JUMP_IF_FALSE) last_jump_good = True for j in jump_ifs: if target == self.get_target(j): # FIXME: remove magic number if self.lines[j].next == j + 3 and last_jump_good: fix = j break else: last_jump_good = False self.fixed_jumps[offset] = fix or match[-1] return else: self.fixed_jumps[offset] = match[-1] return # op == POP_JUMP_IF_TRUE else: next = self.next_stmt[offset] if prev_op[next] == offset: pass elif self.is_jump_forward(next) and target == self.get_target(next): if code[prev_op[next]] == self.opc.POP_JUMP_IF_FALSE: if (code[next] == self.opc.JUMP_FORWARD or target != rtarget or code[prev_op[pre_rtarget]] not in (self.opc.JUMP_ABSOLUTE, self.opc.RETURN_VALUE)): self.fixed_jumps[offset] = prev_op[next] return elif (code[next] == self.opc.JUMP_ABSOLUTE and self.is_jump_forward(target) and self.get_target(target) == self.get_target(next)): self.fixed_jumps[offset] = prev_op[next] return # Don't add a struct for a while test, it's already taken care of if offset in self.ignore_if: return rtarget_is_ja = code[pre_rtarget] == self.opc.JUMP_ABSOLUTE if ( rtarget_is_ja and pre_rtarget in self.stmts and pre_rtarget != offset and prev_op[pre_rtarget] != offset and not (code[rtarget] == self.opc.JUMP_ABSOLUTE and code[rtarget+3] == self.opc.POP_BLOCK and code[prev_op[pre_rtarget]] != self.opc.JUMP_ABSOLUTE)): rtarget = pre_rtarget # Does the "jump if" jump beyond a jump op? # That is, we have something like: # POP_JUMP_IF_FALSE HERE # ... # JUMP_FORWARD # HERE: # # If so, this can be block inside an "if" statement # or a conditional assignment like: # x = 1 if x else 2 # # For 3.5, in addition the JUMP_FORWARD above we could have # JUMP_BACK or CONTINUE # # There are other contexts we may need to consider # like whether the target is "END_FINALLY" # or if the condition jump is to a forward location if self.is_jump_forward(pre_rtarget) or (rtarget_is_ja and self.version >= 3.5): if_end = self.get_target(pre_rtarget) # If the jump target is back, we are looping if (if_end < pre_rtarget and (code[prev_op[if_end]] == self.opc.SETUP_LOOP)): if (if_end > start): return end = self.restrict_to_parent(if_end, parent) self.structs.append({'type': 'if-then', 'start': start, 'end': pre_rtarget}) # FIXME: add this # self.fixed_jumps[offset] = rtarget self.not_continue.add(pre_rtarget) if rtarget < end and ( code[rtarget] not in (self.opc.END_FINALLY, self.opc.JUMP_ABSOLUTE) and code[prev_op[pre_rtarget]] not in (self.opc.POP_EXCEPT, self.opc.END_FINALLY)): self.structs.append({'type': 'else', 'start': rtarget, 'end': end}) self.else_start[rtarget] = end elif self.is_jump_back(pre_rtarget, 0): if_end = rtarget self.structs.append({'type': 'if-then', 'start': start, 'end': pre_rtarget}) self.not_continue.add(pre_rtarget) elif code[pre_rtarget] in (self.opc.RETURN_VALUE, self.opc.BREAK_LOOP): self.structs.append({'type': 'if-then', 'start': start, 'end': rtarget}) # It is important to distingish if this return is inside some sort # except block return jump_prev = prev_op[offset] if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP: if self.opc.cmp_op[code[jump_prev+1]] == 'exception-match': return if self.version >= 3.5: # Python 3.5 may remove as dead code a JUMP # instruction after a RETURN_VALUE. So we check # based on seeing SETUP_EXCEPT various places. if code[rtarget] == self.opc.SETUP_EXCEPT: return # Check that next instruction after pops and jump is # not from SETUP_EXCEPT next_op = rtarget if code[next_op] == self.opc.POP_BLOCK: next_op += instruction_size(self.code[next_op], self.opc) if code[next_op] == self.opc.JUMP_ABSOLUTE: next_op += instruction_size(self.code[next_op], self.opc) if next_op in targets: for try_op in targets[next_op]: come_from_op = code[try_op] if come_from_op == self.opc.SETUP_EXCEPT: return pass pass if code[pre_rtarget] == self.opc.RETURN_VALUE: # If we are at some sort of POP_JUMP_IF and the instruction before was # COMPARE_OP exception-match, then pre_rtarget is not an end_if if not (inst_index > 0 and self.insts[inst_index-1].argval == 'exception-match'): self.return_end_ifs.add(pre_rtarget) else: self.fixed_jumps[offset] = rtarget self.not_continue.add(pre_rtarget) else: # FIXME: this is very convoluted and based on rather hacky # empirical evidence. It should go a way when # we have better control-flow analysis normal_jump = self.version >= 3.6 if self.version == 3.5: j = self.offset2inst_index[target] if j+2 < len(self.insts) and self.insts[j+2].is_jump_target: normal_jump = self.insts[j+1].opname == 'POP_BLOCK' if normal_jump: # For now, we'll only tag forward jump. if target > offset: self.fixed_jumps[offset] = target pass else: # FIXME: This is probably a bug in < 3.5 and we should # instead use the above code. But until we smoke things # out we'll stick with it. if rtarget > offset: self.fixed_jumps[offset] = rtarget elif op == self.opc.SETUP_EXCEPT: target = self.get_target(offset) end = self.restrict_to_parent(target, parent) self.fixed_jumps[offset] = end elif op == self.opc.POP_EXCEPT: next_offset = xdis.next_offset(op, self.opc, offset) target = self.get_target(next_offset) if target is None: from trepan.api import debug; debug() if target > next_offset: next_op = code[next_offset] if (self.opc.JUMP_ABSOLUTE == next_op and self.opc.END_FINALLY != code[xdis.next_offset(next_op, self.opc, next_offset)]): self.fixed_jumps[next_offset] = target self.except_targets[target] = next_offset elif op == self.opc.SETUP_FINALLY: target = self.get_target(offset) end = self.restrict_to_parent(target, parent) self.fixed_jumps[offset] = end elif op in self.jump_if_pop: target = self.get_target(offset) if target > offset: unop_target = self.last_instr(offset, target, self.opc.JUMP_FORWARD, target) if unop_target and code[unop_target+3] != self.opc.ROT_TWO: self.fixed_jumps[offset] = unop_target else: self.fixed_jumps[offset] = self.restrict_to_parent(target, parent) pass pass elif self.version >= 3.5: # 3.5+ has Jump optimization which too often causes RETURN_VALUE to get # misclassified as RETURN_END_IF. Handle that here. # In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF if op == self.opc.RETURN_VALUE: next_offset = xdis.next_offset(op, self.opc, offset) if (next_offset < len(code) and code[next_offset] == self.opc.JUMP_ABSOLUTE and offset in self.return_end_ifs): self.return_end_ifs.remove(offset) pass pass elif op == self.opc.JUMP_FORWARD: # If we have: # JUMP_FORWARD x, [non-jump, insns], RETURN_VALUE, x: # then RETURN_VALUE is not RETURN_END_IF rtarget = self.get_target(offset) rtarget_prev = self.prev[rtarget] if (code[rtarget_prev] == self.opc.RETURN_VALUE and rtarget_prev in self.return_end_ifs): i = rtarget_prev while i != offset: if code[i] in [op3.JUMP_FORWARD, op3.JUMP_ABSOLUTE]: return i = self.prev[i] self.return_end_ifs.remove(rtarget_prev) pass return
def signal_handler(num, f): logger.info("Caught sig handler.") from trepan.api import debug; debug(dbg_opts={'interface':Mserver.ServerInterface(connection_opts={'IO':'FIFO'})}) return
def extract(klass, frame_gen, *, limit=None, lookup_lines=True, capture_locals=False, lasti=None): """Create a StackSummary from a traceback or stack object. :param frame_gen: A generator that yields (frame, lineno) tuples to include in the stack. :param limit: None to include all frames or the number of frames to include. :param lookup_lines: If True, lookup lines for each frame immediately, otherwise lookup is deferred until the frame is rendered. :param capture_locals: If True, the local variables from each frame will be captured as object representations into the FrameSummary. :param last_i: If set, last instruction executed """ if limit is None: limit = getattr(sys, "tracebacklimit", None) if limit is not None and limit < 0: limit = 0 if limit is not None: if limit >= 0: frame_gen = itertools.islice(frame_gen, limit) else: frame_gen = collections.deque(frame_gen, maxlen=-limit) result = klass() fnames = set() try: for f, lineno, lasti in frame_gen: co = f.f_code filename = co.co_filename name = co.co_name fnames.add(filename) linecache.lazycache(filename, f.f_globals) # Must defer line lookups until we have called checkcache. if capture_locals: f_locals = f.f_locals else: f_locals = None result.append( FrameSummary( filename, lineno, name, lookup_line=False, locals=f_locals, code=co, last_i=lasti, )) except: from trepan.api import debug debug() for filename in fnames: linecache.checkcache(filename) # If immediate lookup was desired, trigger lookups now. if lookup_lines: for f in result: f.line return result
def build_flowgraph(self, blocks): g = DiGraph() self.block_offsets = {} self.block_nodes = {} # Add nodes for block in self.blocks: self.block_offsets[block.start_offset] = block block_node = g.make_add_node(block) self.block_nodes[block] = block_node # Compute a block's immediate predecessors and successors for block in self.blocks: for jump_offset in block.jump_offsets: try: assert jump_offset in self.block_offsets except: from trepan.api import debug debug() x = 1 successor_block = self.block_offsets[jump_offset] successor_block.predecessors.add(block) block.successors.add(successor_block) if block.follow_offset and (not (jump_flags & block.flags or (BB_NOFOLLOW in block.flags))): assert block.follow_offset in self.block_offsets successor_block = self.block_offsets[block.follow_offset] successor_block.predecessors.add(block) block.successors.add(successor_block) assert len(self.blocks) > 0 self.entry_node = self.blocks[0] sorted_blocks = sorted(self.blocks, key=attrgetter("index")) for i, block in enumerate(sorted_blocks): # Is this this dead code? (Remove self loops in calculation) # Entry node, blocks[0] is never unreachable if not block.predecessors - set([block]) and block != blocks[0]: block.unreachable = True block = sorted_blocks[i] if block.follow_offset: if BB_NOFOLLOW in block.flags: kind = "no fallthrough" else: kind = "fallthrough" g.make_add_edge( self.block_nodes[block], self.block_nodes[self.block_offsets[block.follow_offset]], kind, ) # Connect the current block to its jump targets for jump_index in block.jump_offsets: target_block = self.block_offsets[jump_index] if jump_index > block.start_offset: if BB_LOOP in block.flags: edge_type = "forward_scope" else: edge_type = "forward" else: edge_type = "backward" if self.block_nodes[target_block] == self.block_nodes[block]: edge_type = "self-loop" g.make_add_edge(self.block_nodes[block], self.block_nodes[target_block], edge_type) pass pass self.graph = g return
def do_draw(self, cr, get_data_fn): if not self.exposed: if self.debug: print("Not exposed") return if self.debug: print("exposed") self.levels, self.pid2node = get_data_fn(self) max_x, max_y, self.levels_x = arrange(self.levels, self.pid2node, self.width, debug=self.debug) old_x, old_y = self.area.get_size_request() if old_x != max_x or old_y != max_y: self.area.set_size_request(max_x, max_y) if len(self.levels) == 0: return # cr.translate (10, 10) layout = PangoCairo.create_layout(cr) desc = Pango.font_description_from_string(self.font) layout.set_font_description(desc) x_offset = 0 text_mid = self.fontHeight >> 1 widths = {} for i, level in enumerate(self.levels): for pid in level: node = self.pid2node[pid] try: vrank, rank, x_offset, y_offset = node.PosInfo except: from trepan.api import debug; debug() # Draw line from process to parent if i > 0: layout.set_text('', -1) ppid = node.PPid parent = self.pid2node[ppid] pp_vrank, pp_i, pp_x, pp_y = parent.PosInfo pp_x += widths[ppid] + self.line_gap cr.move_to(x_offset - self.line_gap, y_offset + text_mid) cr.line_to(pp_x + self.line_gap, pp_y + text_mid) cr.set_source_rgba(0.0, 0.0, 0.0, 0.5) cr.stroke() pass # print(node.Name, node.PosInfo, node.State) PangoCairo.show_layout(cr, layout) cr.move_to(x_offset, y_offset) layout.set_text(node.Name, -1) text_width, text_height = layout.get_pixel_size() widths[pid] = text_width # layout and set process name if node.State[0] == 'R': cr.set_source_rgba(0.0, 1.0, 0.0, 1.0) elif node.State[0] == 'T': cr.set_source_rgba(1.0, 0.0, 0.0, 1.0) else: cr.set_source_rgba(0.0, 0.0, 0.0) if self.debug: print("xy(%d,%d), pid(%ld): %s" % (node.PosInfo[2], node.PosInfo[3], node.Pid, node.Name)) PangoCairo.show_layout(cr, layout) pass return
def err(msg, inst, i): msg += ". Instruction %d:\n%s" % (i, inst) from trepan.api import debug debug() raise RuntimeError(msg)
#!/usr/bin/env python """Something to use to test signal handling. Basically we just need a program that installs a signal handler and sends it a signal. """ import sys, os, signal, time from trepan.api import debug; debug() def signal_handler(num, f): print 'signal %d received' % num return signal.signal(signal.SIGUSR1, signal_handler) sleepy_time = 4 print "pid %d" % os.getpid() print "Waiting in time.sleep(%d) for signal USR1." % sleepy_time while True: time.sleep(sleepy_time) os.kill(os.getpid(), signal.SIGUSR1) pass pass
def detect_control_flow(self, offset: int, targets: Dict[Any, Any], inst_index: int): """ Detect type of block structures and their boundaries to fix optimized jumps in python2.3+ """ code = self.code inst = self.insts[inst_index] op = inst.opcode # Detect parent structure parent: Dict[str, int] = self.structs[0] start: int = parent["start"] end: int = parent["end"] # Pick inner-most parent for our offset for struct in self.structs: current_start = struct["start"] current_end = struct["end"] if (current_start <= offset < current_end) and ( current_start >= start and current_end <= end): start = current_start end = current_end parent = struct if self.version < 3.8 and op == self.opc.SETUP_LOOP: # We categorize loop types: 'for', 'while', 'while 1' with # possibly suffixes '-loop' and '-else' # Try to find the jump_back instruction of the loop. # It could be a return instruction. start += inst.inst_size target = self.get_target(offset) end = self.restrict_to_parent(target, parent) self.setup_loops[target] = offset if target != end: self.fixed_jumps[offset] = end (line_no, next_line_byte) = self.lines[offset] jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, next_line_byte, False) if jump_back: jump_forward_offset = xdis.next_offset(code[jump_back], self.opc, jump_back) else: jump_forward_offset = None return_val_offset1 = self.prev[self.prev[end]] if (jump_back and jump_back != self.prev_op[end] and self.is_jump_forward(jump_forward_offset)): if code[self.prev_op[end]] == self.opc.RETURN_VALUE or ( code[self.prev_op[end]] == self.opc.POP_BLOCK and code[return_val_offset1] == self.opc.RETURN_VALUE): jump_back = None if not jump_back: # loop suite ends in return jump_back = self.last_instr(start, end, self.opc.RETURN_VALUE) if not jump_back: return jb_inst = self.get_inst(jump_back) jump_back = self.next_offset(jb_inst.opcode, jump_back) if_offset = None if code[self.prev_op[next_line_byte]] not in self.pop_jump_tf: if_offset = self.prev[next_line_byte] if if_offset: loop_type = "while" self.ignore_if.add(if_offset) else: loop_type = "for" target = next_line_byte end = xdis.next_offset(code[jump_back], self.opc, jump_back) else: if self.get_target(jump_back) >= next_line_byte: jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False) jb_inst = self.get_inst(jump_back) jb_next_offset = self.next_offset(jb_inst.opcode, jump_back) if end > jb_next_offset and self.is_jump_forward(end): if self.is_jump_forward(jb_next_offset): if self.get_target(jb_next_offset) == self.get_target( end): self.fixed_jumps[offset] = jb_next_offset end = jb_next_offset elif target < offset: self.fixed_jumps[offset] = jb_next_offset end = jb_next_offset target = self.get_target(jump_back) if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER): loop_type = "for" else: loop_type = "while" test = self.prev_op[next_line_byte] if test == offset: loop_type = "while 1" elif self.code[test] in self.opc.JUMP_OPs: self.ignore_if.add(test) test_target = self.get_target(test) if test_target > (jump_back + 3): jump_back = test_target self.not_continue.add(jump_back) self.loops.append(target) self.structs.append({ "type": loop_type + "-loop", "start": target, "end": jump_back }) after_jump_offset = xdis.next_offset(code[jump_back], self.opc, jump_back) if after_jump_offset != end: self.structs.append({ "type": loop_type + "-else", "start": after_jump_offset, "end": end, }) elif op in self.pop_jump_tf: target = inst.argval self.fixed_jumps[offset] = target # FIXME: consider removing the test on 3.8. elif self.version >= 3.8 and inst.is_jump(): self.fixed_jumps[offset] = inst.argval elif self.version < 3.8 and op == self.opc.SETUP_EXCEPT: target = self.get_target(offset) end = self.restrict_to_parent(target, parent) self.fixed_jumps[offset] = end elif self.version < 3.8 and op == self.opc.POP_EXCEPT: next_offset = xdis.next_offset(op, self.opc, offset) target = self.get_target(next_offset) if target > next_offset: next_op = code[next_offset] if (self.opc.JUMP_ABSOLUTE == next_op and self.opc.END_FINALLY != code[xdis.next_offset( next_op, self.opc, next_offset)]): self.fixed_jumps[next_offset] = target from trepan.api import debug debug() self.except_targets[target] = next_offset elif op == self.opc.SETUP_FINALLY: target = self.get_target(offset) end = self.restrict_to_parent(target, parent) self.fixed_jumps[offset] = end elif op in self.jump_if_pop: target = self.get_target(offset) if target > offset: unop_target = self.last_instr(offset, target, self.opc.JUMP_FORWARD, target) if unop_target and code[unop_target + 3] != self.opc.ROT_TWO: self.fixed_jumps[offset] = unop_target else: self.fixed_jumps[offset] = self.restrict_to_parent( target, parent) pass pass else: # 3.5+ has Jump optimization which too often causes RETURN_VALUE to get # misclassified as RETURN_END_IF. Handle that here. # In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF if op == self.opc.RETURN_VALUE: next_offset = xdis.next_offset(op, self.opc, offset) if next_offset < len(code) and ( code[next_offset] == self.opc.JUMP_ABSOLUTE and offset in self.return_end_ifs): self.return_end_ifs.remove(offset) pass pass elif op == self.opc.JUMP_FORWARD: # If we have: # JUMP_FORWARD x, [non-jump, insns], RETURN_VALUE, x: # then RETURN_VALUE is not RETURN_END_IF rtarget = self.get_target(offset) rtarget_prev = self.prev[rtarget] if (code[rtarget_prev] == self.opc.RETURN_VALUE and rtarget_prev in self.return_end_ifs): i = rtarget_prev while i != offset: if code[i] in [op3.JUMP_FORWARD, op3.JUMP_ABSOLUTE]: return i = self.prev[i] self.return_end_ifs.remove(rtarget_prev) pass return