def __init__(self, frame, bcode): assert bcode.name.startswith("CALL_FUNCTION") self.stack = get_value_stack_top(frame) self.positional_args_count = bcode.arg1 self.keyword_args_count = bcode.arg2 self.args_count = self.positional_args_count + 2*self.keyword_args_count # There are four bytecodes for function calls, that tell use whether # single star (*args) and/or double star (**kwargs) notation was # used: CALL_FUNCTION, CALL_FUNCTION_VAR, CALL_FUNCTION_KW # and CALL_FUNCTION_VAR_KW. self.singlestar = "_VAR" in bcode.name self.doublestar = "_KW" in bcode.name
def __init__(self, frame, bcode): assert bcode.name.startswith("CALL_FUNCTION") self.stack = get_value_stack_top(frame) self.positional_args_count = bcode.arg1 self.keyword_args_count = bcode.arg2 self.args_count = self.positional_args_count + 2 * self.keyword_args_count # There are four bytecodes for function calls, that tell use whether # single star (*args) and/or double star (**kwargs) notation was # used: CALL_FUNCTION, CALL_FUNCTION_VAR, CALL_FUNCTION_KW # and CALL_FUNCTION_VAR_KW. self.singlestar = "_VAR" in bcode.name self.doublestar = "_KW" in bcode.name
def trace(self, frame, event): """Tries to recognize the current event in terms of calls to and returns from C. Currently supported events: * ('c_call', (function, positional_arguments, keyword_arguments)) A call to a C function with given arguments is about to happen. * ('c_return', return_value) A C function returned with given value (it will always be the function for the most recent 'c_call' event). * ('print', value) * ('print_to', (value, output)) A print statement is about to be executed. * ('store_attr', (object, name, value)) * ('delete_attr', (object, name)) An instance variable of object is about to be changed or deleted. * ('load_global', (module, name)) * ('store_global', (module, name, value)) * ('delete_global', (module, name)) A global variable is about to be read, written or deleted. It is a generator and it yields a sequence of events, as a single bytecode may generate more than one event. Canonical example is a sequence of CALL_FUNCTION bytecodes. Execution of the first bytecode causes a 'c_call' event. Execution of the second bytecode causes two consecutive events: 'c_return' and another 'c_call'. """ if event == 'line': if self.call_stack[-1]: self.call_stack.pop() stack = get_value_stack_top(frame) # Rewrite a code object each time it is returned by some # C function. Most commonly that will be the 'compile' function. # TODO: Make sure the old code is garbage collected. if type(stack[-1]) is CodeType: stack[-1] = rewrite_lnotab(stack[-1]) yield 'c_return', stack[-1] bcode = current_bytecode(frame) if bcode.name.startswith("CALL_FUNCTION"): value_stack = ValueStack(frame, bcode) function = value_stack.bottom() # Python functions are handled by the standard trace mechanism, but # we have to make sure any C calls the function makes can be traced # by us later, so we rewrite its bytecode. if not is_c_func(function): rewrite_function(function) return self.call_stack.append(True) pargs = value_stack.positional_args() kargs = value_stack.keyword_args() # Rewrite all callables that may have been passed to the C function. rewrite_all(pargs + kargs.values()) yield 'c_call', (function, pargs, kargs) elif bcode.name == "PRINT_NEWLINE": yield 'print', os.linesep else: stack = get_value_stack_top(frame) if bcode.name == "PRINT_NEWLINE_TO": yield 'print_to', (os.linesep, stack[-1]) elif bcode.name == "PRINT_ITEM": yield 'print', stack[-1] elif bcode.name == "PRINT_ITEM_TO": yield 'print_to', (stack[-2], stack[-1]) elif bcode.name == "STORE_ATTR": yield 'store_attr', (stack[-1], name_from_arg(frame, bcode), stack[-2]) elif bcode.name == "DELETE_ATTR": yield 'delete_attr', (stack[-1], name_from_arg(frame, bcode)) elif bcode.name == "LOAD_GLOBAL": module = frame_module(frame) if module: try: name = name_from_arg(frame, bcode) value = frame.f_globals[name] yield 'load_global', (module.__name__, name, value) except KeyError: pass elif bcode.name == "STORE_GLOBAL": module = frame_module(frame) if module: yield 'store_global', (module.__name__, name_from_arg(frame, bcode), stack[-1]) elif bcode.name == "DELETE_GLOBAL": module = frame_module(frame) if module: yield 'delete_global', (module.__name__, name_from_arg(frame, bcode)) elif event == 'call': self.call_stack.append(False) # When an exception happens in Python >= 2.4 code, 'exception' and # 'return' events are reported in succession. Exceptions raised from # C functions don't generate the 'return' event, so we have to pop # from the stack right away and simulate the 'c_return' event # ourselves. elif event == 'exception' and self.call_stack[-1]: yield 'c_return', None self.call_stack.pop() # Python functions always generate a 'return' event, even when an exception # has been raised, so let's just check for that. elif event == 'return': self.call_stack.pop()