laike9m
laike9m

Reputation: 19358

In Python 3.7, does the 'opcode' event from sys.settrace give any information about the bytecode itself?

So I'm playing with the sys.settrace function. In Python 3.7, a new opcode event is added

'opcode'
The interpreter is about to execute a new opcode (see dis for opcode details). The local trace function is called; arg is None; the return value specifies the new local trace function. Per-opcode events are not emitted by default: they must be explicitly requested by setting f_trace_opcodes to True on the frame.

I was able to get those opcode events, however there seems to be no further information——I don't even know what the opcode is and what it's doing.

Does this event only brings finer granularity, without giving any extra information?

Upvotes: 2

Views: 830

Answers (1)

Noctis Skytower
Noctis Skytower

Reputation: 22011

If you want further information, you will need to examine the frame argument that is passed into the tracing function. In particular, you want to look at frame.f_lasti to find the last instruction executed and frame.f_code.co_code to access that instruction. Using those two together will give you the actual opcode. If you want the mnemonic, then you will want to use dis.opname; but if you simply what to match it with another opcode, then you can use dis.opmap instead. The following example is contrived, but it demonstrates some of what is possible using the hints just provided:

#! /usr/bin/env python3
import dis
import sys


def main():
    dis.dis(add)
    sys.settrace(get_trace(False, get_callback(celebrate)))
    total = add(1, 2)
    print(f'total = {total}')
    sys.settrace(None)
    total = add(3, 4)
    print(f'total = {total}')
    print('Done')


def get_trace(trace_lines=True, opcode_callback=None):
    trace_opcodes = callable(opcode_callback)

    # noinspection PyUnusedLocal
    def trace(frame, event, arg):
        frame.f_trace_lines = trace_lines
        frame.f_trace_opcodes = trace_opcodes
        if trace_opcodes and event == 'opcode':
            opcode = frame.f_code.co_code[frame.f_lasti]
            opname = dis.opname[opcode]
            opcode_callback(frame, opcode, opname)
        return trace

    return trace


def get_callback(return_handler=None):
    handle_return = callable(return_handler)

    def echo_opcode(frame, opcode, opname):
        print(f'# {opname} ({opcode}) #')
        if handle_return and opcode == dis.opmap['RETURN_VALUE']:
            return_handler(frame)

    return echo_opcode


# noinspection PyUnusedLocal
def celebrate(frame):
    print('/-------------------\\')
    print('| We are returning! |')
    print('\\-------------------/')


def add(a, b):
    return a + b


if __name__ == '__main__':
    main()

Upvotes: 2

Related Questions