Rewrite a code object's bytecode in-place with layered instrumentation. Three layers (outermost first): 1. INSTRUMENTED_LINE — wraps line-start instructions (stores original in side-table) 2. INSTRUMENTED_INSTRUCTION — wraps all traceable instructions (stores original in side-table) 3. Regular INSTRUMENTED_* — direct 1:1 opcode swap (no side-table needed) De-instrumentation peels layers in rever
(code: &PyCode, events: u32)
| 235 | /// |
| 236 | /// De-instrumentation peels layers in reverse order. |
| 237 | pub fn instrument_code(code: &PyCode, events: u32) { |
| 238 | use rustpython_compiler_core::bytecode::{self, Instruction}; |
| 239 | |
| 240 | let len = code.code.instructions.len(); |
| 241 | let mut monitoring_data = code.monitoring_data.lock(); |
| 242 | |
| 243 | // === Phase 1-3: De-instrument all layers (outermost first) === |
| 244 | |
| 245 | // Phase 1: Remove INSTRUMENTED_LINE → restore from side-table |
| 246 | if let Some(data) = monitoring_data.as_mut() { |
| 247 | for i in 0..len { |
| 248 | if data.line_opcodes[i] != 0 { |
| 249 | let original = Instruction::try_from(data.line_opcodes[i]) |
| 250 | .expect("invalid opcode in line side-table"); |
| 251 | unsafe { |
| 252 | code.code.instructions.replace_op(i, original); |
| 253 | } |
| 254 | data.line_opcodes[i] = 0; |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | // Phase 2: Remove INSTRUMENTED_INSTRUCTION → restore from side-table |
| 260 | if let Some(data) = monitoring_data.as_mut() { |
| 261 | for i in 0..len { |
| 262 | if data.per_instruction_opcodes[i] != 0 { |
| 263 | let original = Instruction::try_from(data.per_instruction_opcodes[i]) |
| 264 | .expect("invalid opcode in instruction side-table"); |
| 265 | unsafe { |
| 266 | code.code.instructions.replace_op(i, original); |
| 267 | } |
| 268 | data.per_instruction_opcodes[i] = 0; |
| 269 | } |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | // Phase 3: Remove regular INSTRUMENTED_* and specialized opcodes → restore base opcodes. |
| 274 | // Also clear all CACHE entries so specialization starts fresh. |
| 275 | { |
| 276 | let mut i = 0; |
| 277 | while i < len { |
| 278 | let op = code.code.instructions[i].op; |
| 279 | let base_op = op.deoptimize(); |
| 280 | if u8::from(base_op) != u8::from(op) { |
| 281 | unsafe { |
| 282 | code.code.instructions.replace_op(i, base_op); |
| 283 | } |
| 284 | } |
| 285 | let caches = base_op.cache_entries(); |
| 286 | // Zero all CACHE entries (the op+arg bytes may have been overwritten |
| 287 | // by specialization with arbitrary data like pointers). |
| 288 | for c in 1..=caches { |
| 289 | if i + c < len { |
| 290 | unsafe { |
| 291 | code.code.instructions.write_cache_u16(i + c, 0); |
| 292 | } |
| 293 | } |
| 294 | } |
no test coverage detected