Skip to content

Commit 12c02f6

Browse files
committed
Add news entry
1 parent dac78a5 commit 12c02f6

File tree

4 files changed

+22
-16
lines changed

4 files changed

+22
-16
lines changed

Lib/profiling/sampling/opcode_utils.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# Build opcode name mapping: opcode number -> opcode name
1212
# This includes both standard opcodes and specialized variants (Python 3.11+)
1313
_OPCODE_NAMES = dict(enumerate(opcode.opname))
14-
if hasattr(opcode, '_specialized_opmap'):
14+
if hasattr(opcode, "_specialized_opmap"):
1515
for name, op in opcode._specialized_opmap.items():
1616
_OPCODE_NAMES[op] = name
1717

@@ -20,7 +20,9 @@
2020
# LOAD_ATTR can be replaced at runtime with specialized variants like
2121
# LOAD_ATTR_INSTANCE_VALUE. This mapping lets us show both forms.
2222
_DEOPT_MAP = {}
23-
if hasattr(opcode, '_specializations') and hasattr(opcode, '_specialized_opmap'):
23+
if hasattr(opcode, "_specializations") and hasattr(
24+
opcode, "_specialized_opmap"
25+
):
2426
for base_name, variant_names in opcode._specializations.items():
2527
base_opcode = opcode.opmap.get(base_name)
2628
if base_opcode is not None:
@@ -45,24 +47,24 @@ def get_opcode_info(opcode_num):
4547
opname = _OPCODE_NAMES.get(opcode_num)
4648
if opname is None:
4749
return {
48-
'opname': f'<{opcode_num}>',
49-
'base_opname': f'<{opcode_num}>',
50-
'is_specialized': False,
50+
"opname": f"<{opcode_num}>",
51+
"base_opname": f"<{opcode_num}>",
52+
"is_specialized": False,
5153
}
5254

5355
base_opcode = _DEOPT_MAP.get(opcode_num)
5456
if base_opcode is not None:
55-
base_opname = _OPCODE_NAMES.get(base_opcode, f'<{base_opcode}>')
57+
base_opname = _OPCODE_NAMES.get(base_opcode, f"<{base_opcode}>")
5658
return {
57-
'opname': opname,
58-
'base_opname': base_opname,
59-
'is_specialized': True,
59+
"opname": opname,
60+
"base_opname": base_opname,
61+
"is_specialized": True,
6062
}
6163

6264
return {
63-
'opname': opname,
64-
'base_opname': opname,
65-
'is_specialized': False,
65+
"opname": opname,
66+
"base_opname": opname,
67+
"is_specialized": False,
6668
}
6769

6870

@@ -76,9 +78,9 @@ def format_opcode(opcode_num):
7678
A formatted string like 'LOAD_ATTR' or 'LOAD_ATTR_INSTANCE_VALUE (LOAD_ATTR)'
7779
"""
7880
info = get_opcode_info(opcode_num)
79-
if info['is_specialized']:
81+
if info["is_specialized"]:
8082
return f"{info['opname']} ({info['base_opname']})"
81-
return info['opname']
83+
return info["opname"]
8284

8385

8486
def get_opcode_mapping():

Lib/test/test_profiling/test_sampling_profiler/_live_collector_helpers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
THREAD_STATUS_ON_CPU,
66
)
77

8-
from .mocks import LocationInfo, MockFrameInfo
98

109

1110
class MockThreadInfo:

Lib/test/test_profiling/test_sampling_profiler/test_integration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
skip_if_not_supported,
3535
PROCESS_VM_READV_SUPPORTED,
3636
)
37-
from .mocks import MockFrameInfo, MockThreadInfo, MockInterpreterInfo, LocationInfo
37+
from .mocks import MockFrameInfo, MockThreadInfo, MockInterpreterInfo
3838

3939
# Duration for profiling tests - long enough for process to complete naturally
4040
PROFILING_TIMEOUT = str(int(SHORT_TIMEOUT))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Add bytecode-level instruction profiling to the sampling profiler via the
2+
new ``--opcodes`` flag. When enabled, the profiler captures which bytecode
3+
opcode is executing at each sample, including Python 3.11+ adaptive
4+
specializations, and visualizes this data in the heatmap, flamegraph, gecko,
5+
and live output formats. Patch by Pablo Galindo

0 commit comments

Comments
 (0)