mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
93 lines
3.2 KiB
Python
93 lines
3.2 KiB
Python
|
|
"""Opcode utilities for bytecode-level profiler visualization.
|
||
|
|
|
||
|
|
This module provides utilities to get opcode names and detect specialization
|
||
|
|
status using the opcode module's metadata. Used by heatmap and flamegraph
|
||
|
|
collectors to display which bytecode instructions are executing at each
|
||
|
|
source line, including Python's adaptive specialization optimizations.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import opcode
|
||
|
|
|
||
|
|
# Build opcode name mapping: opcode number -> opcode name
|
||
|
|
# This includes both standard opcodes and specialized variants (Python 3.11+)
|
||
|
|
_OPCODE_NAMES = dict(enumerate(opcode.opname))
|
||
|
|
if hasattr(opcode, '_specialized_opmap'):
|
||
|
|
for name, op in opcode._specialized_opmap.items():
|
||
|
|
_OPCODE_NAMES[op] = name
|
||
|
|
|
||
|
|
# Build deopt mapping: specialized opcode number -> base opcode number
|
||
|
|
# Python 3.11+ uses adaptive specialization where generic opcodes like
|
||
|
|
# LOAD_ATTR can be replaced at runtime with specialized variants like
|
||
|
|
# LOAD_ATTR_INSTANCE_VALUE. This mapping lets us show both forms.
|
||
|
|
_DEOPT_MAP = {}
|
||
|
|
if hasattr(opcode, '_specializations') and hasattr(opcode, '_specialized_opmap'):
|
||
|
|
for base_name, variant_names in opcode._specializations.items():
|
||
|
|
base_opcode = opcode.opmap.get(base_name)
|
||
|
|
if base_opcode is not None:
|
||
|
|
for variant_name in variant_names:
|
||
|
|
variant_opcode = opcode._specialized_opmap.get(variant_name)
|
||
|
|
if variant_opcode is not None:
|
||
|
|
_DEOPT_MAP[variant_opcode] = base_opcode
|
||
|
|
|
||
|
|
|
||
|
|
def get_opcode_info(opcode_num):
|
||
|
|
"""Get opcode name and specialization info from an opcode number.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
opcode_num: The opcode number (0-255 or higher for specialized)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
A dict with keys:
|
||
|
|
- 'opname': The opcode name (e.g., 'LOAD_ATTR_INSTANCE_VALUE')
|
||
|
|
- 'base_opname': The base opcode name (e.g., 'LOAD_ATTR')
|
||
|
|
- 'is_specialized': True if this is a specialized instruction
|
||
|
|
"""
|
||
|
|
opname = _OPCODE_NAMES.get(opcode_num)
|
||
|
|
if opname is None:
|
||
|
|
return {
|
||
|
|
'opname': f'<{opcode_num}>',
|
||
|
|
'base_opname': f'<{opcode_num}>',
|
||
|
|
'is_specialized': False,
|
||
|
|
}
|
||
|
|
|
||
|
|
base_opcode = _DEOPT_MAP.get(opcode_num)
|
||
|
|
if base_opcode is not None:
|
||
|
|
base_opname = _OPCODE_NAMES.get(base_opcode, f'<{base_opcode}>')
|
||
|
|
return {
|
||
|
|
'opname': opname,
|
||
|
|
'base_opname': base_opname,
|
||
|
|
'is_specialized': True,
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
'opname': opname,
|
||
|
|
'base_opname': opname,
|
||
|
|
'is_specialized': False,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def format_opcode(opcode_num):
|
||
|
|
"""Format an opcode for display, showing base opcode for specialized ones.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
opcode_num: The opcode number (0-255 or higher for specialized)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
A formatted string like 'LOAD_ATTR' or 'LOAD_ATTR_INSTANCE_VALUE (LOAD_ATTR)'
|
||
|
|
"""
|
||
|
|
info = get_opcode_info(opcode_num)
|
||
|
|
if info['is_specialized']:
|
||
|
|
return f"{info['opname']} ({info['base_opname']})"
|
||
|
|
return info['opname']
|
||
|
|
|
||
|
|
|
||
|
|
def get_opcode_mapping():
|
||
|
|
"""Get opcode name and deopt mappings for JavaScript consumption.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
A dict with keys:
|
||
|
|
- 'names': Dict mapping opcode numbers to opcode names
|
||
|
|
- 'deopt': Dict mapping specialized opcode numbers to base opcode numbers
|
||
|
|
"""
|
||
|
|
return {"names": _OPCODE_NAMES, "deopt": _DEOPT_MAP}
|