| """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} |