Skip to content

Commit d6b814e

Browse files
committed
Add show_jit option to dis to show ENTER_EXECUTOR instructions
1 parent 55f2518 commit d6b814e

3 files changed

Lines changed: 64 additions & 20 deletions

File tree

Lib/dis.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def _try_compile(source, name):
8484
return compile(source, name, 'exec')
8585

8686
def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
87-
show_offsets=False, show_positions=False):
87+
show_offsets=False, show_positions=False, show_jit=False):
8888
"""Disassemble classes, methods, functions, and other compiled objects.
8989
9090
With no argument, disassemble the last traceback.
@@ -95,7 +95,8 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
9595
"""
9696
if x is None:
9797
distb(file=file, show_caches=show_caches, adaptive=adaptive,
98-
show_offsets=show_offsets, show_positions=show_positions)
98+
show_offsets=show_offsets, show_positions=show_positions,
99+
show_jit=show_jit)
99100
return
100101
# Extract functions from methods.
101102
if hasattr(x, '__func__'):
@@ -116,12 +117,14 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
116117
if isinstance(x1, _have_code):
117118
print("Disassembly of %s:" % name, file=file)
118119
try:
119-
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
120+
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
121+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
120122
except TypeError as msg:
121123
print("Sorry:", msg, file=file)
122124
print(file=file)
123125
elif hasattr(x, 'co_code'): # Code object
124-
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
126+
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
127+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
125128
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
126129
labels_map = _make_labels_map(x)
127130
label_width = 4 + len(str(len(labels_map)))
@@ -132,12 +135,13 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
132135
arg_resolver = ArgResolver(labels_map=labels_map)
133136
_disassemble_bytes(x, arg_resolver=arg_resolver, formatter=formatter)
134137
elif isinstance(x, str): # Source code
135-
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
138+
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
139+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
136140
else:
137141
raise TypeError("don't know how to disassemble %s objects" %
138142
type(x).__name__)
139143

140-
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
144+
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
141145
"""Disassemble a traceback (default: last traceback)."""
142146
if tb is None:
143147
try:
@@ -148,7 +152,8 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets
148152
except AttributeError:
149153
raise RuntimeError("no last traceback to disassemble") from None
150154
while tb.tb_next: tb = tb.tb_next
151-
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)
155+
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive,
156+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
152157

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

219-
def _get_code_array(co, adaptive):
224+
def _get_code_array(co, adaptive, show_jit):
220225
if adaptive:
221226
code = co._co_code_adaptive
222227
res = []
223228
found = False
224229
for i in range(0, len(code), 2):
225230
op, arg = code[i], code[i+1]
226-
if op == ENTER_EXECUTOR:
231+
if op == ENTER_EXECUTOR and not show_jit:
227232
try:
228233
ex = get_executor(co, i)
229234
except (ValueError, RuntimeError):
@@ -656,7 +661,7 @@ def get_argval_argrepr(self, op, arg, offset):
656661
argrepr = 'not in' if argval else 'in'
657662
return argval, argrepr
658663

659-
def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
664+
def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False, show_jit=False):
660665
"""Iterator for the opcodes in methods, functions or code
661666
662667
Generates a series of Instruction named tuples giving the details of
@@ -679,7 +684,7 @@ def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
679684
names=co.co_names,
680685
varname_from_oparg=co._varname_from_oparg,
681686
labels_map=_make_labels_map(original_code))
682-
return _get_instructions_bytes(_get_code_array(co, adaptive),
687+
return _get_instructions_bytes(_get_code_array(co, adaptive, show_jit),
683688
linestarts=linestarts,
684689
line_offset=line_offset,
685690
co_positions=co.co_positions(),
@@ -792,6 +797,8 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N
792797
positions = Positions(*next(co_positions, ()))
793798
deop = _deoptop(op)
794799
op = code[offset]
800+
if op == ENTER_EXECUTOR:
801+
arg = code[offset+1]
795802

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

821828

822829
def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
823-
show_offsets=False, show_positions=False):
830+
show_offsets=False, show_positions=False, show_jit=False):
824831
"""Disassemble a code object."""
825832
linestarts = dict(findlinestarts(co))
826833
exception_entries = _parse_exception_table(co)
@@ -840,12 +847,12 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
840847
names=co.co_names,
841848
varname_from_oparg=co._varname_from_oparg,
842849
labels_map=labels_map)
843-
_disassemble_bytes(_get_code_array(co, adaptive), lasti, linestarts,
850+
_disassemble_bytes(_get_code_array(co, adaptive, show_jit), lasti, linestarts,
844851
exception_entries=exception_entries, co_positions=co.co_positions(),
845852
original_code=co.co_code, arg_resolver=arg_resolver, formatter=formatter)
846853

847-
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
848-
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
854+
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
855+
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
849856
if depth is None or depth > 0:
850857
if depth is not None:
851858
depth = depth - 1
@@ -855,7 +862,8 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap
855862
print("Disassembly of %r:" % (x,), file=file)
856863
_disassemble_recursive(
857864
x, file=file, depth=depth, show_caches=show_caches,
858-
adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions
865+
adaptive=adaptive, show_offsets=show_offsets,
866+
show_positions=show_positions, show_jit=show_jit
859867
)
860868

861869

@@ -1054,7 +1062,7 @@ class Bytecode:
10541062
10551063
Iterating over this yields the bytecode operations as Instruction instances.
10561064
"""
1057-
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
1065+
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
10581066
self.codeobj = co = _get_code_object(x)
10591067
if first_line is None:
10601068
self.first_line = co.co_firstlineno
@@ -1070,6 +1078,7 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False
10701078
self.adaptive = adaptive
10711079
self.show_offsets = show_offsets
10721080
self.show_positions = show_positions
1081+
self.show_jit = show_jit
10731082

10741083
def __iter__(self):
10751084
co = self.codeobj
@@ -1079,7 +1088,7 @@ def __iter__(self):
10791088
names=co.co_names,
10801089
varname_from_oparg=co._varname_from_oparg,
10811090
labels_map=labels_map)
1082-
return _get_instructions_bytes(_get_code_array(co, self.adaptive),
1091+
return _get_instructions_bytes(_get_code_array(co, self.adaptive, self.show_jit),
10831092
linestarts=self._linestarts,
10841093
line_offset=self._line_offset,
10851094
co_positions=co.co_positions(),
@@ -1111,7 +1120,7 @@ def dis(self):
11111120
else:
11121121
offset = -1
11131122
with io.StringIO() as output:
1114-
code = _get_code_array(co, self.adaptive)
1123+
code = _get_code_array(co, self.adaptive, self.show_jit)
11151124
offset_width = len(str(max(len(code) - 2, 9999))) if self.show_offsets else 0
11161125
if self.show_positions:
11171126
lineno_width = _get_positions_width(co)

Lib/test/test_dis.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
import unittest
1616
from test.support import (captured_stdout, requires_debug_ranges,
1717
requires_specialization, cpython_only,
18-
os_helper, import_helper, reset_code)
18+
os_helper, import_helper, reset_code,
19+
requires_jit_enabled)
1920
from test.support.bytecode_helper import BytecodeTestCase
2021

2122

@@ -1481,6 +1482,37 @@ def f():
14811482
self.assertEqual(assem_op, assem_cache)
14821483

14831484

1485+
@cpython_only
1486+
@requires_specialization
1487+
@requires_jit_enabled
1488+
def test_show_jit(self):
1489+
def loop(n):
1490+
for i in range(n):
1491+
pass
1492+
for _ in range(10):
1493+
loop(500)
1494+
line = loop.__code__.co_firstlineno
1495+
loop_dis = f"""\
1496+
{line} RESUME_CHECK_JIT 0
1497+
1498+
{line+1} LOAD_GLOBAL_BUILTIN 1 (range + NULL)
1499+
LOAD_FAST_BORROW 0 (n)
1500+
CALL_BUILTIN_CLASS 1
1501+
GET_ITER 0
1502+
L1: FOR_ITER_RANGE 3 (to L2)
1503+
STORE_FAST 1 (i)
1504+
1505+
{line+2} ENTER_EXECUTOR 0
1506+
1507+
{line+1} L2: END_FOR
1508+
POP_ITER
1509+
LOAD_COMMON_CONSTANT 7 (None)
1510+
RETURN_VALUE
1511+
"""
1512+
got = self.get_disassembly(loop, adaptive=True, show_jit=True)
1513+
self.do_disassembly_compare(got, loop_dis)
1514+
1515+
14841516
class DisWithFileTests(DisTests):
14851517

14861518
# Run the tests again, using the file arg instead of print
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add ``show_jit`` option to ``dis.dis`` to show JIT entry points in the
2+
bytecode. This is useful for visualizing where JIT traces are entered from
3+
the interpreter.

0 commit comments

Comments
 (0)