Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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()
Exemplo n.º 4
0
    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()