Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def _try_compile(source, name):
return compile(source, name, 'exec')

def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
show_offsets=False, show_positions=False):
show_offsets=False, show_positions=False, show_jit=False):
"""Disassemble classes, methods, functions, and other compiled objects.

With no argument, disassemble the last traceback.
Expand All @@ -95,7 +95,8 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
"""
if x is None:
distb(file=file, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions)
show_offsets=show_offsets, show_positions=show_positions,
show_jit=show_jit)
return
# Extract functions from methods.
if hasattr(x, '__func__'):
Expand All @@ -116,12 +117,14 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
if isinstance(x1, _have_code):
print("Disassembly of %s:" % name, file=file)
try:
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
except TypeError as msg:
print("Sorry:", msg, file=file)
print(file=file)
elif hasattr(x, 'co_code'): # Code object
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
labels_map = _make_labels_map(x)
label_width = 4 + len(str(len(labels_map)))
Expand All @@ -132,12 +135,13 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
arg_resolver = ArgResolver(labels_map=labels_map)
_disassemble_bytes(x, arg_resolver=arg_resolver, formatter=formatter)
elif isinstance(x, str): # Source code
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
else:
raise TypeError("don't know how to disassemble %s objects" %
type(x).__name__)

def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
"""Disassemble a traceback (default: last traceback)."""
if tb is None:
try:
Expand All @@ -148,7 +152,8 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets
except AttributeError:
raise RuntimeError("no last traceback to disassemble") from None
while tb.tb_next: tb = tb.tb_next
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)

# The inspect module interrogates this dictionary to build its
# list of CO_* constants. It is also used by pretty_flags to
Expand Down Expand Up @@ -216,14 +221,14 @@ def _deoptop(op):
name = _all_opname[op]
return _all_opmap[deoptmap[name]] if name in deoptmap else op

def _get_code_array(co, adaptive):
def _get_code_array(co, adaptive, show_jit):
if adaptive:
code = co._co_code_adaptive
res = []
found = False
for i in range(0, len(code), 2):
op, arg = code[i], code[i+1]
if op == ENTER_EXECUTOR:
if op == ENTER_EXECUTOR and not show_jit:
try:
ex = get_executor(co, i)
except (ValueError, RuntimeError):
Expand Down Expand Up @@ -656,7 +661,7 @@ def get_argval_argrepr(self, op, arg, offset):
argrepr = 'not in' if argval else 'in'
return argval, argrepr

def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False, show_jit=False):
"""Iterator for the opcodes in methods, functions or code

Generates a series of Instruction named tuples giving the details of
Expand All @@ -679,7 +684,7 @@ def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
names=co.co_names,
varname_from_oparg=co._varname_from_oparg,
labels_map=_make_labels_map(original_code))
return _get_instructions_bytes(_get_code_array(co, adaptive),
return _get_instructions_bytes(_get_code_array(co, adaptive, show_jit),
linestarts=linestarts,
line_offset=line_offset,
co_positions=co.co_positions(),
Expand Down Expand Up @@ -792,6 +797,8 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N
positions = Positions(*next(co_positions, ()))
deop = _deoptop(op)
op = code[offset]
if op == ENTER_EXECUTOR:
arg = code[offset+1]

if arg_resolver:
argval, argrepr = arg_resolver.get_argval_argrepr(op, arg, offset)
Expand Down Expand Up @@ -820,7 +827,7 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N


def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
show_offsets=False, show_positions=False):
show_offsets=False, show_positions=False, show_jit=False):
"""Disassemble a code object."""
linestarts = dict(findlinestarts(co))
exception_entries = _parse_exception_table(co)
Expand All @@ -840,12 +847,12 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
names=co.co_names,
varname_from_oparg=co._varname_from_oparg,
labels_map=labels_map)
_disassemble_bytes(_get_code_array(co, adaptive), lasti, linestarts,
_disassemble_bytes(_get_code_array(co, adaptive, show_jit), lasti, linestarts,
exception_entries=exception_entries, co_positions=co.co_positions(),
original_code=co.co_code, arg_resolver=arg_resolver, formatter=formatter)

def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
if depth is None or depth > 0:
if depth is not None:
depth = depth - 1
Expand All @@ -855,7 +862,8 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap
print("Disassembly of %r:" % (x,), file=file)
_disassemble_recursive(
x, file=file, depth=depth, show_caches=show_caches,
adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions
adaptive=adaptive, show_offsets=show_offsets,
show_positions=show_positions, show_jit=show_jit
)


Expand Down Expand Up @@ -1054,7 +1062,7 @@ class Bytecode:

Iterating over this yields the bytecode operations as Instruction instances.
"""
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
self.codeobj = co = _get_code_object(x)
if first_line is None:
self.first_line = co.co_firstlineno
Expand All @@ -1070,6 +1078,7 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False
self.adaptive = adaptive
self.show_offsets = show_offsets
self.show_positions = show_positions
self.show_jit = show_jit

def __iter__(self):
co = self.codeobj
Expand All @@ -1079,7 +1088,7 @@ def __iter__(self):
names=co.co_names,
varname_from_oparg=co._varname_from_oparg,
labels_map=labels_map)
return _get_instructions_bytes(_get_code_array(co, self.adaptive),
return _get_instructions_bytes(_get_code_array(co, self.adaptive, self.show_jit),
linestarts=self._linestarts,
line_offset=self._line_offset,
co_positions=co.co_positions(),
Expand Down Expand Up @@ -1111,7 +1120,7 @@ def dis(self):
else:
offset = -1
with io.StringIO() as output:
code = _get_code_array(co, self.adaptive)
code = _get_code_array(co, self.adaptive, self.show_jit)
offset_width = len(str(max(len(code) - 2, 9999))) if self.show_offsets else 0
if self.show_positions:
lineno_width = _get_positions_width(co)
Expand Down
34 changes: 33 additions & 1 deletion Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
import unittest
from test.support import (captured_stdout, requires_debug_ranges,
requires_specialization, cpython_only,
os_helper, import_helper, reset_code)
os_helper, import_helper, reset_code,
requires_jit_enabled)
from test.support.bytecode_helper import BytecodeTestCase


Expand Down Expand Up @@ -1481,6 +1482,37 @@ def f():
self.assertEqual(assem_op, assem_cache)


@cpython_only
@requires_specialization
@requires_jit_enabled
def test_show_jit(self):
def loop(n):
for i in range(n):
pass
for _ in range(10):
loop(500)
line = loop.__code__.co_firstlineno
loop_dis = f"""\
{line} RESUME_CHECK_JIT 0

{line+1} LOAD_GLOBAL_BUILTIN 1 (range + NULL)
LOAD_FAST_BORROW 0 (n)
CALL_BUILTIN_CLASS 1
GET_ITER 0
L1: FOR_ITER_RANGE 3 (to L2)
STORE_FAST 1 (i)

{line+2} ENTER_EXECUTOR 0

{line+1} L2: END_FOR
POP_ITER
LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
got = self.get_disassembly(loop, adaptive=True, show_jit=True)
self.do_disassembly_compare(got, loop_dis)


class DisWithFileTests(DisTests):

# Run the tests again, using the file arg instead of print
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add ``show_jit`` option to ``dis.dis`` to show JIT entry points in the
bytecode. This is useful for visualizing where JIT traces are entered from
the interpreter.
Loading