diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 81741c547b7e3f..55db785f7a8964 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -76,11 +76,12 @@ typedef struct { } _PyCoCached; /* Ancillary data structure used for instrumentation. - Line instrumentation creates an array of - these. One entry per code unit.*/ + Line instrumentation creates this with sufficient + space for one entry per code unit. The total size + of the data will be `bytes_per_entry * Py_SIZE(code)` */ typedef struct { - uint8_t original_opcode; - int8_t line_delta; + uint8_t bytes_per_entry; + uint8_t data[1]; } _PyCoLineInstrumentationData; /* Main data structure used for instrumentation. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-30-15-49-31.gh-issue-127953.B4_6L9.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-30-15-49-31.gh-issue-127953.B4_6L9.rst new file mode 100644 index 00000000000000..f19afcd90b16ea --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-30-15-49-31.gh-issue-127953.B4_6L9.rst @@ -0,0 +1,2 @@ +The time to handle a ``LINE`` event in sys.monitoring (and sys.settrace) is +now independent of the number of lines in the code object. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 6f7b8b54dcfa3c..feb96f78f466ca 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -853,6 +853,9 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) if (addrq < 0) { return co->co_firstlineno; } + if (co->_co_monitoring && co->_co_monitoring->lines) { + return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + } assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); PyCodeAddressRange bounds; _PyCode_InitAddressRange(co, &bounds); diff --git a/Python/ceval.c b/Python/ceval.c index 3985b52649cc82..b0033ea56004b3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -860,17 +860,25 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int { _Py_CODEUNIT *prev = frame->prev_instr; _Py_CODEUNIT *here = frame->prev_instr = next_instr; - _PyFrame_SetStackPointer(frame, stack_pointer); - int original_opcode = _Py_call_instrumentation_line( - tstate, frame, here, prev); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (original_opcode < 0) { - next_instr = here+1; - goto error; + int original_opcode = 0; + if (tstate->tracing) { + PyCodeObject *code = frame->f_code; + int index = (int)(here - _PyCode_CODE(code)); + original_opcode = code->_co_monitoring->lines->data[index*code->_co_monitoring->lines->bytes_per_entry]; } - next_instr = frame->prev_instr; - if (next_instr != here) { - DISPATCH(); + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + original_opcode = _Py_call_instrumentation_line( + tstate, frame, here, prev); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (original_opcode < 0) { + next_instr = here+1; + goto error; + } + next_instr = frame->prev_instr; + if (next_instr != here) { + DISPATCH(); + } } if (_PyOpcode_Caches[original_opcode]) { _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1); diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 2f94f103e43186..f662ba395f830e 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -2,6 +2,7 @@ + #include "Python.h" #include "pycore_call.h" #include "pycore_frame.h" @@ -232,47 +233,42 @@ get_events(_Py_GlobalMonitors *m, int tool_id) return result; } -/* Line delta. - * 8 bit value. - * if line_delta == -128: - * line = None # represented as -1 - * elif line_delta == -127: - * line = PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT)); +/* Module code can have line 0, even though modules start at line 1, + * so -1 is a legal delta. */ +#define NO_LINE (-2) + +/* Returns the line delta. Defined as: + * if line is None: + * line_delta = NO_LINE * else: - * line = first_line + (offset >> OFFSET_SHIFT) + line_delta; + * line_delta = line - first_line */ - -#define NO_LINE -128 -#define COMPUTED_LINE -127 - -#define OFFSET_SHIFT 4 - -static int8_t -compute_line_delta(PyCodeObject *code, int offset, int line) +static int +compute_line_delta(PyCodeObject *code, int line) { if (line < 0) { + assert(line == -1); return NO_LINE; } - int delta = line - code->co_firstlineno - (offset >> OFFSET_SHIFT); - if (delta <= INT8_MAX && delta > COMPUTED_LINE) { - return delta; - } - return COMPUTED_LINE; + int delta = line - code->co_firstlineno; + assert(delta > NO_LINE); + return delta; } static int -compute_line(PyCodeObject *code, int offset, int8_t line_delta) +compute_line(PyCodeObject *code, int line_delta) { - if (line_delta > COMPUTED_LINE) { - return code->co_firstlineno + (offset >> OFFSET_SHIFT) + line_delta; - } if (line_delta == NO_LINE) { - return -1; } - assert(line_delta == COMPUTED_LINE); - /* Look it up */ - return PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT)); + assert(line_delta > NO_LINE); + return code->co_firstlineno + line_delta; +} + +static inline uint8_t +get_original_opcode(_PyCoLineInstrumentationData *line_data, int index) +{ + return line_data->data[index*line_data->bytes_per_entry]; } static int @@ -282,7 +278,7 @@ instruction_length(PyCodeObject *code, int offset) assert(opcode != 0); assert(opcode != RESERVED); if (opcode == INSTRUMENTED_LINE) { - opcode = code->_co_monitoring->lines[offset].original_opcode; + opcode = get_original_opcode(code->_co_monitoring->lines, offset); } if (opcode == INSTRUMENTED_INSTRUCTION) { opcode = code->_co_monitoring->per_instruction_opcodes[offset]; @@ -300,6 +296,51 @@ instruction_length(PyCodeObject *code, int offset) return 1 + _PyOpcode_Caches[opcode]; } +static inline uint8_t * +get_original_opcode_ptr(_PyCoLineInstrumentationData *line_data, int index) +{ + return &line_data->data[index*line_data->bytes_per_entry]; +} + +static inline void +set_original_opcode(_PyCoLineInstrumentationData *line_data, int index, uint8_t opcode) +{ + line_data->data[index*line_data->bytes_per_entry] = opcode; +} + +static inline int +get_line_delta(_PyCoLineInstrumentationData *line_data, int index) +{ + uint8_t *ptr = &line_data->data[index*line_data->bytes_per_entry+1]; + assert(line_data->bytes_per_entry >= 2); + uint32_t value = *ptr; + for (int idx = 2; idx < line_data->bytes_per_entry; idx++) { + ptr++; + int shift = (idx-1)*8; + value |= ((uint32_t)(*ptr)) << shift; + } + assert(value < INT_MAX); + /* NO_LINE is stored as zero. */ + return ((int)value) + NO_LINE; +} + +static inline void +set_line_delta(_PyCoLineInstrumentationData *line_data, int index, int line_delta) +{ + /* Store line_delta + 2 as we need -2 to represent no line number */ + assert(line_delta >= NO_LINE); + uint32_t adjusted = line_delta - NO_LINE; + uint8_t *ptr = &line_data->data[index*line_data->bytes_per_entry+1]; + assert(adjusted < (1ULL << ((line_data->bytes_per_entry-1)*8))); + assert(line_data->bytes_per_entry >= 2); + *ptr = adjusted & 0xff; + for (int idx = 2; idx < line_data->bytes_per_entry; idx++) { + ptr++; + adjusted >>= 8; + *ptr = adjusted & 0xff; + } +} + #ifdef INSTRUMENT_DEBUG static void @@ -319,11 +360,15 @@ dump_instrumentation_data_lines(PyCodeObject *code, _PyCoLineInstrumentationData if (lines == NULL) { fprintf(out, ", lines = NULL"); } - else if (lines[i].original_opcode == 0) { - fprintf(out, ", lines = {original_opcode = No LINE (0), line_delta = %d)", lines[i].line_delta); - } else { - fprintf(out, ", lines = {original_opcode = %s, line_delta = %d)", _PyOpcode_OpName[lines[i].original_opcode], lines[i].line_delta); + int opcode = get_original_opcode(lines, i); + int line_delta = get_line_delta(lines, i); + if (opcode == 0) { + fprintf(out, ", lines = {original_opcode = No LINE (0), line_delta = %d)", line_delta); + } + else { + fprintf(out, ", lines = {original_opcode = %s, line_delta = %d)", _PyOpcode_OpName[opcode], line_delta); + } } } @@ -373,6 +418,12 @@ dump_local_monitors(const char *prefix, _Py_LocalMonitors monitors, FILE*out) } } +/** NOTE: + * Do not use PyCode_Addr2Line to determine the line number in instrumentation, + * as `PyCode_Addr2Line` uses the monitoring data if it is available. + */ + + /* No error checking -- Don't use this for anything but experimental debugging */ static void dump_instrumentation_data(PyCodeObject *code, int star, FILE*out) @@ -390,6 +441,8 @@ dump_instrumentation_data(PyCodeObject *code, int star, FILE*out) dump_local_monitors("Active", data->active_monitors, out); int code_len = (int)Py_SIZE(code); bool starred = false; + PyCodeAddressRange range; + _PyCode_InitAddressRange(code, &range); for (int i = 0; i < code_len; i += instruction_length(code, i)) { _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; int opcode = instr->op.code; @@ -397,7 +450,7 @@ dump_instrumentation_data(PyCodeObject *code, int star, FILE*out) fprintf(out, "** "); starred = true; } - fprintf(out, "Offset: %d, line: %d %s: ", i, PyCode_Addr2Line(code, i*2), _PyOpcode_OpName[opcode]); + fprintf(out, "Offset: %d, line: %d %s: ", i, _PyCode_CheckLineNumber(i*2, &range), _PyOpcode_OpName[opcode]); dump_instrumentation_data_tools(code, data->tools, i, out); dump_instrumentation_data_lines(code, data->lines, i, out); dump_instrumentation_data_line_tools(code, data->line_tools, i, out); @@ -458,10 +511,12 @@ sanity_check_instrumentation(PyCodeObject *code) code->_co_monitoring->active_monitors, active_monitors)); int code_len = (int)Py_SIZE(code); + PyCodeAddressRange range; + _PyCode_InitAddressRange(code, &range); for (int i = 0; i < code_len;) { _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; int opcode = instr->op.code; - int base_opcode = _Py_GetBaseOpcode(code, i); + int base_opcode = _Py_GetBaseCodeUnit(code, i).op.code; CHECK(valid_opcode(opcode)); CHECK(valid_opcode(base_opcode)); if (opcode == INSTRUMENTED_INSTRUCTION) { @@ -476,8 +531,8 @@ sanity_check_instrumentation(PyCodeObject *code) } if (opcode == INSTRUMENTED_LINE) { CHECK(data->lines); - CHECK(valid_opcode(data->lines[i].original_opcode)); - opcode = data->lines[i].original_opcode; + opcode = get_original_opcode(data->lines, i); + CHECK(valid_opcode(opcode)); CHECK(opcode != END_FOR); CHECK(opcode != RESUME); CHECK(opcode != RESUME_CHECK); @@ -492,7 +547,7 @@ sanity_check_instrumentation(PyCodeObject *code) * *and* we are executing a INSTRUMENTED_LINE instruction * that has de-instrumented itself, then we will execute * an invalid INSTRUMENTED_INSTRUCTION */ - CHECK(data->lines[i].original_opcode != INSTRUMENTED_INSTRUCTION); + CHECK(get_original_opcode(data->lines, i) != INSTRUMENTED_INSTRUCTION); } if (opcode == INSTRUMENTED_INSTRUCTION) { CHECK(data->per_instruction_opcodes[i] != 0); @@ -507,9 +562,9 @@ sanity_check_instrumentation(PyCodeObject *code) } CHECK(active_monitors.tools[event] != 0); } - if (data->lines && base_opcode != END_FOR) { - int line1 = compute_line(code, i, data->lines[i].line_delta); - int line2 = PyCode_Addr2Line(code, i*sizeof(_Py_CODEUNIT)); + if (data->lines && get_original_opcode(data->lines, i)) { + int line1 = compute_line(code, get_line_delta(data->lines, i)); + int line2 = _PyCode_CheckLineNumber(i*sizeof(_Py_CODEUNIT), &range); CHECK(line1 == line2); } CHECK(valid_opcode(opcode)); @@ -542,7 +597,7 @@ int _Py_GetBaseOpcode(PyCodeObject *code, int i) { int opcode = _PyCode_CODE(code)[i].op.code; if (opcode == INSTRUMENTED_LINE) { - opcode = code->_co_monitoring->lines[i].original_opcode; + opcode = get_original_opcode(code->_co_monitoring->lines, i); } if (opcode == INSTRUMENTED_INSTRUCTION) { opcode = code->_co_monitoring->per_instruction_opcodes[i]; @@ -566,7 +621,7 @@ de_instrument(PyCodeObject *code, int i, int event) uint8_t *opcode_ptr = &instr->op.code; int opcode = *opcode_ptr; if (opcode == INSTRUMENTED_LINE) { - opcode_ptr = &code->_co_monitoring->lines[i].original_opcode; + opcode_ptr = get_original_opcode_ptr(code->_co_monitoring->lines, i); opcode = *opcode_ptr; } if (opcode == INSTRUMENTED_INSTRUCTION) { @@ -592,10 +647,10 @@ de_instrument_line(PyCodeObject *code, int i) if (opcode != INSTRUMENTED_LINE) { return; } - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; - int original_opcode = lines->original_opcode; + _PyCoLineInstrumentationData *lines = code->_co_monitoring->lines; + int original_opcode = get_original_opcode(lines, i); if (original_opcode == INSTRUMENTED_INSTRUCTION) { - lines->original_opcode = code->_co_monitoring->per_instruction_opcodes[i]; + set_original_opcode(lines, i, code->_co_monitoring->per_instruction_opcodes[i]); } CHECK(original_opcode != 0); CHECK(original_opcode == _PyOpcode_Deopt[original_opcode]); @@ -613,7 +668,7 @@ de_instrument_per_instruction(PyCodeObject *code, int i) uint8_t *opcode_ptr = &instr->op.code; int opcode = *opcode_ptr; if (opcode == INSTRUMENTED_LINE) { - opcode_ptr = &code->_co_monitoring->lines[i].original_opcode; + opcode_ptr = get_original_opcode_ptr(code->_co_monitoring->lines, i); opcode = *opcode_ptr; } if (opcode != INSTRUMENTED_INSTRUCTION) { @@ -640,8 +695,7 @@ instrument(PyCodeObject *code, int i) uint8_t *opcode_ptr = &instr->op.code; int opcode =*opcode_ptr; if (opcode == INSTRUMENTED_LINE) { - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; - opcode_ptr = &lines->original_opcode; + opcode_ptr = get_original_opcode_ptr(code->_co_monitoring->lines, i); opcode = *opcode_ptr; } if (opcode == INSTRUMENTED_INSTRUCTION) { @@ -670,9 +724,8 @@ instrument_line(PyCodeObject *code, int i) if (opcode == INSTRUMENTED_LINE) { return; } - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; - lines->original_opcode = _PyOpcode_Deopt[opcode]; - CHECK(lines->original_opcode > 0); + set_original_opcode(code->_co_monitoring->lines, i, _PyOpcode_Deopt[opcode]); + CHECK(get_line_delta(code->_co_monitoring->lines, i) > NO_LINE); *opcode_ptr = INSTRUMENTED_LINE; } @@ -683,8 +736,7 @@ instrument_per_instruction(PyCodeObject *code, int i) uint8_t *opcode_ptr = &instr->op.code; int opcode = *opcode_ptr; if (opcode == INSTRUMENTED_LINE) { - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; - opcode_ptr = &lines->original_opcode; + opcode_ptr = get_original_opcode_ptr(code->_co_monitoring->lines, i); opcode = *opcode_ptr; } if (opcode == INSTRUMENTED_INSTRUCTION) { @@ -1092,18 +1144,16 @@ _Py_call_instrumentation_exc2( call_instrumentation_vector_protected(tstate, event, frame, instr, 4, args); } - int _Py_Instrumentation_GetLine(PyCodeObject *code, int index) { _PyCoMonitoringData *monitoring = code->_co_monitoring; assert(monitoring != NULL); assert(monitoring->lines != NULL); - assert(index >= code->_co_firsttraceable); assert(index < Py_SIZE(code)); - _PyCoLineInstrumentationData *line_data = &monitoring->lines[index]; - int8_t line_delta = line_data->line_delta; - int line = compute_line(code, index, line_delta); + _PyCoLineInstrumentationData *line_data = monitoring->lines; + int line_delta = get_line_delta(line_data, index); + int line = compute_line(code, line_delta); return line; } @@ -1117,14 +1167,14 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, int i = (int)(instr - _PyCode_CODE(code)); _PyCoMonitoringData *monitoring = code->_co_monitoring; - _PyCoLineInstrumentationData *line_data = &monitoring->lines[i]; + _PyCoLineInstrumentationData *line_data = monitoring->lines; if (tstate->tracing) { goto done; } PyInterpreterState *interp = tstate->interp; - int8_t line_delta = line_data->line_delta; - int line = compute_line(code, i, line_delta); + int line = _Py_Instrumentation_GetLine(code, i); assert(line >= 0); + assert(prev != NULL); int prev_index = (int)(prev - _PyCode_CODE(code)); int prev_line = _Py_Instrumentation_GetLine(code, prev_index); if (prev_line == line) { @@ -1202,7 +1252,7 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, Py_DECREF(line_obj); uint8_t original_opcode; done: - original_opcode = line_data->original_opcode; + original_opcode = get_original_opcode(line_data, i); assert(original_opcode != 0); assert(original_opcode < INSTRUMENTED_LINE); assert(_PyOpcode_Deopt[original_opcode] == original_opcode); @@ -1282,7 +1332,7 @@ initialize_tools(PyCodeObject *code) _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; int opcode = instr->op.code; if (opcode == INSTRUMENTED_LINE) { - opcode = code->_co_monitoring->lines[i].original_opcode; + opcode = get_original_opcode(code->_co_monitoring->lines, i); } if (opcode == INSTRUMENTED_INSTRUCTION) { opcode = code->_co_monitoring->per_instruction_opcodes[i]; @@ -1325,54 +1375,56 @@ initialize_tools(PyCodeObject *code) } } -#define NO_LINE -128 - static void -initialize_lines(PyCodeObject *code) +initialize_lines(PyCodeObject *code, int bytes_per_entry) { _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; assert(line_data != NULL); + line_data->bytes_per_entry = bytes_per_entry; int code_len = (int)Py_SIZE(code); PyCodeAddressRange range; _PyCode_InitAddressRange(code, &range); - for (int i = 0; i < code->_co_firsttraceable && i < code_len; i++) { - line_data[i].original_opcode = 0; - line_data[i].line_delta = -127; - } int current_line = -1; - for (int i = code->_co_firsttraceable; i < code_len; ) { + for (int i = 0; i < code_len; ) { int opcode = _Py_GetBaseOpcode(code, i); int line = _PyCode_CheckLineNumber(i*(int)sizeof(_Py_CODEUNIT), &range); - line_data[i].line_delta = compute_line_delta(code, i, line); + int line_delta = compute_line_delta(code, line); + set_line_delta(line_data, i, line_delta); int length = instruction_length(code, i); - switch (opcode) { - case END_ASYNC_FOR: - case END_FOR: - case END_SEND: - case RESUME: - /* END_FOR cannot start a line, as it is skipped by FOR_ITER - * END_SEND cannot start a line, as it is skipped by SEND - * RESUME must not be instrumented with INSTRUMENT_LINE */ - line_data[i].original_opcode = 0; - break; - default: - /* Set original_opcode to the opcode iff the instruction - * starts a line, and thus should be instrumented. - * This saves having to perform this check every time the - * we turn instrumentation on or off, and serves as a sanity - * check when debugging. - */ - if (line != current_line && line >= 0) { - line_data[i].original_opcode = opcode; - } - else { - line_data[i].original_opcode = 0; - } - current_line = line; + if (i < code->_co_firsttraceable) { + set_original_opcode(line_data, i, 0); + } + else { + switch (opcode) { + case END_ASYNC_FOR: + case END_FOR: + case END_SEND: + case RESUME: + /* END_FOR cannot start a line, as it is skipped by FOR_ITER + * END_SEND cannot start a line, as it is skipped by SEND + * RESUME and POP_ITER must not be instrumented with INSTRUMENTED_LINE */ + set_original_opcode(line_data, i, 0); + break; + default: + /* Set original_opcode to the opcode iff the instruction + * starts a line, and thus should be instrumented. + * This saves having to perform this check every time the + * we turn instrumentation on or off, and serves as a sanity + * check when debugging. + */ + if (line != current_line && line >= 0) { + set_original_opcode(line_data, i, opcode); + CHECK(get_line_delta(line_data, i) != NO_LINE); + } + else { + set_original_opcode(line_data, i, 0); + } + current_line = line; + } } for (int j = 1; j < length; j++) { - line_data[i+j].original_opcode = 0; - line_data[i+j].line_delta = NO_LINE; + set_original_opcode(line_data, i+j, 0); + set_line_delta(line_data, i+j, line_delta); } i += length; } @@ -1414,8 +1466,9 @@ initialize_lines(PyCodeObject *code) continue; } assert(target >= 0); - if (line_data[target].line_delta != NO_LINE) { - line_data[target].original_opcode = _Py_GetBaseOpcode(code, target); + if (get_line_delta(line_data, target) != NO_LINE) { + int opcode = _Py_GetBaseOpcode(code, target); + set_original_opcode(line_data, target, opcode); } } /* Scan exception table */ @@ -1437,9 +1490,8 @@ initialize_lines(PyCodeObject *code) * END_ASYNC_FOR is a bit special as it marks the end of * an `async for` loop, which should not generate its own * line event. */ - if (line_data[handler].line_delta != NO_LINE && - original_opcode != END_ASYNC_FOR) { - line_data[handler].original_opcode = original_opcode; + if (get_line_delta(line_data, handler) != NO_LINE && original_opcode != END_ASYNC_FOR) { + set_original_opcode(line_data, handler, original_opcode); } } } @@ -1497,12 +1549,39 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) } if (all_events.tools[PY_MONITORING_EVENT_LINE]) { if (code->_co_monitoring->lines == NULL) { - code->_co_monitoring->lines = PyMem_Malloc(code_len * sizeof(_PyCoLineInstrumentationData)); + PyCodeAddressRange range; + _PyCode_InitAddressRange(code, &range); + int max_line = code->co_firstlineno + 1; + _PyCode_InitAddressRange(code, &range); + for (int i = code->_co_firsttraceable; i < code_len; ) { + int line = _PyCode_CheckLineNumber(i*(int)sizeof(_Py_CODEUNIT), &range); + if (line > max_line) { + max_line = line; + } + int length = instruction_length(code, i); + i += length; + } + int bytes_per_entry; + int max_delta = max_line - code->co_firstlineno; + /* We store delta+2 in the table, so 253 is max for one byte */ + if (max_delta < 256+NO_LINE) { + bytes_per_entry = 2; + } + else if (max_delta < (1 << 16)+NO_LINE) { + bytes_per_entry = 3; + } + else if (max_delta < (1 << 24)+NO_LINE) { + bytes_per_entry = 4; + } + else { + bytes_per_entry = 5; + } + code->_co_monitoring->lines = PyMem_Malloc(1 + code_len * bytes_per_entry); if (code->_co_monitoring->lines == NULL) { PyErr_NoMemory(); return -1; } - initialize_lines(code); + initialize_lines(code, bytes_per_entry); } if (multitools && code->_co_monitoring->line_tools == NULL) { code->_co_monitoring->line_tools = PyMem_Malloc(code_len); @@ -1639,7 +1718,7 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp) if (removed_line_tools) { _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; for (int i = code->_co_firsttraceable; i < code_len;) { - if (line_data[i].original_opcode) { + if (get_original_opcode(line_data, i)) { if (removed_line_tools) { remove_line_tools(code, i, removed_line_tools); } @@ -1669,7 +1748,7 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp) if (new_line_tools) { _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; for (int i = code->_co_firsttraceable; i < code_len;) { - if (line_data[i].original_opcode) { + if (get_original_opcode(line_data, i)) { if (new_line_tools) { add_line_tools(code, i, new_line_tools); }