GH-122548: Implement branch taken and not taken events for sys.monitoring (GH-122564)

This commit is contained in:
Mark Shannon 2024-12-19 16:59:51 +00:00 committed by GitHub
parent 7b811d0562
commit d2f1d917e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 998 additions and 583 deletions

View file

@ -148,6 +148,8 @@ dummy_func(
RESUME_CHECK,
};
macro(NOT_TAKEN) = NOP;
op(_CHECK_PERIODIC, (--)) {
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
QSBR_QUIESCENT_STATE(tstate);
@ -2723,7 +2725,7 @@ dummy_func(
int flag = PyStackRef_IsFalse(cond);
DEAD(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
JUMPBY(oparg * flag);
JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
replaced op(_POP_JUMP_IF_TRUE, (cond -- )) {
@ -2731,7 +2733,7 @@ dummy_func(
int flag = PyStackRef_IsTrue(cond);
DEAD(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
JUMPBY(oparg * flag);
JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
op(_IS_NONE, (value -- b)) {
@ -2923,13 +2925,11 @@ dummy_func(
macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER;
inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) {
_Py_CODEUNIT *target;
_PyStackRef iter_stackref = TOP();
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next != NULL) {
PUSH(PyStackRef_FromPyObjectSteal(next));
target = next_instr;
}
else {
if (_PyErr_Occurred(tstate)) {
@ -2946,9 +2946,9 @@ dummy_func(
STACK_SHRINK(1);
PyStackRef_CLOSE(iter_stackref);
/* Skip END_FOR and POP_TOP */
target = next_instr + oparg + 2;
_Py_CODEUNIT *target = next_instr + oparg + 2;
INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH);
}
op(_ITER_CHECK_LIST, (iter -- iter)) {
@ -4736,6 +4736,10 @@ dummy_func(
INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP);
}
inst(INSTRUMENTED_NOT_TAKEN, ( -- )) {
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
}
macro(INSTRUMENTED_JUMP_BACKWARD) =
unused/1 +
_CHECK_PERIODIC +
@ -4744,51 +4748,43 @@ dummy_func(
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) {
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsTrue(cond);
int offset = flag * oparg;
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
int jump = PyStackRef_IsTrue(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
}
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1 -- )) {
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsFalse(cond);
int offset = flag * oparg;
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
int jump = PyStackRef_IsFalse(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
}
inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1 -- )) {
_PyStackRef value_stackref = POP();
int flag = PyStackRef_IsNone(value_stackref);
int offset;
if (flag) {
offset = oparg;
int jump = PyStackRef_IsNone(value_stackref);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
PyStackRef_CLOSE(value_stackref);
offset = 0;
}
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
}
inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1 -- )) {
_PyStackRef value_stackref = POP();
int offset;
int nflag = PyStackRef_IsNone(value_stackref);
if (nflag) {
offset = 0;
}
else {
int jump = !PyStackRef_IsNone(value_stackref);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
PyStackRef_CLOSE(value_stackref);
offset = oparg;
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | !nflag;
#endif
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
}
tier1 inst(EXTENDED_ARG, ( -- )) {

View file

@ -406,7 +406,13 @@ codegen_addop_j(instr_sequence *seq, location loc,
assert(IS_JUMP_TARGET_LABEL(target));
assert(OPCODE_HAS_JUMP(opcode) || IS_BLOCK_PUSH_OPCODE(opcode));
assert(!IS_ASSEMBLER_OPCODE(opcode));
return _PyInstructionSequence_Addop(seq, opcode, target.id, loc);
if (_PyInstructionSequence_Addop(seq, opcode, target.id, loc) != SUCCESS) {
return ERROR;
}
if (IS_CONDITIONAL_JUMP_OPCODE(opcode) || opcode == FOR_ITER) {
return _PyInstructionSequence_Addop(seq, NOT_TAKEN, 0, NO_LOCATION);
}
return SUCCESS;
}
#define ADDOP_JUMP(C, LOC, OP, O) \
@ -682,7 +688,6 @@ codegen_setup_annotations_scope(compiler *c, location loc,
ADDOP_I(c, loc, COMPARE_OP, (Py_GT << 5) | compare_masks[Py_GT]);
NEW_JUMP_TARGET_LABEL(c, body);
ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, body);
ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, CONSTANT_NOTIMPLEMENTEDERROR);
ADDOP_I(c, loc, RAISE_VARARGS, 1);
USE_LABEL(c, body);

View file

@ -5668,6 +5668,8 @@
/* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
/* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 because it is instrumented */
/* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 because it is instrumented */
/* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 because it is instrumented */

View file

@ -557,6 +557,12 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
if (backwards_jump == NULL) {
return ERROR;
}
assert(b->b_next->b_iused > 0);
assert(b->b_next->b_instr[0].i_opcode == NOT_TAKEN);
b->b_next->b_instr[0].i_opcode = NOP;
b->b_next->b_instr[0].i_loc = NO_LOCATION;
RETURN_IF_ERROR(
basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc));
RETURN_IF_ERROR(
basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc));
last->i_opcode = reversed_opcode;

View file

@ -4618,7 +4618,6 @@
next_instr += 2;
INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER);
/* Skip 1 cache entry */
_Py_CODEUNIT *target;
_PyStackRef iter_stackref = TOP();
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
_PyFrame_SetStackPointer(frame, stack_pointer);
@ -4626,7 +4625,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
if (next != NULL) {
PUSH(PyStackRef_FromPyObjectSteal(next));
target = next_instr;
}
else {
if (_PyErr_Occurred(tstate)) {
@ -4647,9 +4645,9 @@
STACK_SHRINK(1);
PyStackRef_CLOSE(iter_stackref);
/* Skip END_FOR and POP_TOP */
target = next_instr + oparg + 2;
_Py_CODEUNIT *target = next_instr + oparg + 2;
INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH);
DISPATCH();
}
@ -4754,6 +4752,15 @@
GO_TO_INSTRUCTION(LOAD_SUPER_ATTR);
}
TARGET(INSTRUMENTED_NOT_TAKEN) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 1;
INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN);
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
DISPATCH();
}
TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
@ -4762,10 +4769,11 @@
/* Skip 1 cache entry */
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsFalse(cond);
int offset = flag * oparg;
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
int jump = PyStackRef_IsFalse(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
DISPATCH();
}
@ -4776,17 +4784,14 @@
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE);
/* Skip 1 cache entry */
_PyStackRef value_stackref = POP();
int flag = PyStackRef_IsNone(value_stackref);
int offset;
if (flag) {
offset = oparg;
int jump = PyStackRef_IsNone(value_stackref);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
PyStackRef_CLOSE(value_stackref);
offset = 0;
}
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
DISPATCH();
}
@ -4797,19 +4802,12 @@
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE);
/* Skip 1 cache entry */
_PyStackRef value_stackref = POP();
int offset;
int nflag = PyStackRef_IsNone(value_stackref);
if (nflag) {
offset = 0;
}
else {
int jump = !PyStackRef_IsNone(value_stackref);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
PyStackRef_CLOSE(value_stackref);
offset = oparg;
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | !nflag;
#endif
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
DISPATCH();
}
@ -4821,10 +4819,11 @@
/* Skip 1 cache entry */
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsTrue(cond);
int offset = flag * oparg;
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
int jump = PyStackRef_IsTrue(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
DISPATCH();
}
@ -6659,6 +6658,13 @@
DISPATCH();
}
TARGET(NOT_TAKEN) {
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(NOT_TAKEN);
DISPATCH();
}
TARGET(POP_EXCEPT) {
frame->instr_ptr = next_instr;
next_instr += 1;
@ -6687,7 +6693,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsFalse(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
JUMPBY(oparg * flag);
JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
@ -6719,7 +6725,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsTrue(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
JUMPBY(oparg * flag);
JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@ -6752,7 +6758,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsFalse(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
JUMPBY(oparg * flag);
JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@ -6770,7 +6776,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsTrue(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
JUMPBY(oparg * flag);
JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();

View file

@ -85,22 +85,24 @@ static const int8_t EVENT_FOR_OPCODE[256] = {
[INSTRUMENTED_YIELD_VALUE] = PY_MONITORING_EVENT_PY_YIELD,
[JUMP_FORWARD] = PY_MONITORING_EVENT_JUMP,
[JUMP_BACKWARD] = PY_MONITORING_EVENT_JUMP,
[POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH,
[POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH,
[POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH,
[POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH,
[POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[INSTRUMENTED_JUMP_FORWARD] = PY_MONITORING_EVENT_JUMP,
[INSTRUMENTED_JUMP_BACKWARD] = PY_MONITORING_EVENT_JUMP,
[INSTRUMENTED_POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH,
[INSTRUMENTED_POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH,
[INSTRUMENTED_POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH,
[INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH,
[FOR_ITER] = PY_MONITORING_EVENT_BRANCH,
[INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH,
[INSTRUMENTED_POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[INSTRUMENTED_POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[INSTRUMENTED_POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION,
[INSTRUMENTED_END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION,
[END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION,
[INSTRUMENTED_END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION,
[NOT_TAKEN] = PY_MONITORING_EVENT_BRANCH_LEFT,
[INSTRUMENTED_NOT_TAKEN] = PY_MONITORING_EVENT_BRANCH_LEFT,
};
static const uint8_t DE_INSTRUMENT[256] = {
@ -120,6 +122,7 @@ static const uint8_t DE_INSTRUMENT[256] = {
[INSTRUMENTED_END_FOR] = END_FOR,
[INSTRUMENTED_END_SEND] = END_SEND,
[INSTRUMENTED_LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR,
[INSTRUMENTED_NOT_TAKEN] = NOT_TAKEN,
};
static const uint8_t INSTRUMENTED_OPCODES[256] = {
@ -155,6 +158,8 @@ static const uint8_t INSTRUMENTED_OPCODES[256] = {
[INSTRUMENTED_FOR_ITER] = INSTRUMENTED_FOR_ITER,
[LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR,
[INSTRUMENTED_LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR,
[NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN,
[INSTRUMENTED_NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN,
[INSTRUMENTED_LINE] = INSTRUMENTED_LINE,
[INSTRUMENTED_INSTRUCTION] = INSTRUMENTED_INSTRUCTION,
@ -323,33 +328,8 @@ _PyInstruction_GetLength(PyCodeObject *code, int offset)
{
ASSERT_WORLD_STOPPED_OR_LOCKED(code);
int opcode =
FT_ATOMIC_LOAD_UINT8_RELAXED(_PyCode_CODE(code)[offset].op.code);
assert(opcode != 0);
assert(opcode != RESERVED);
if (opcode == INSTRUMENTED_LINE) {
opcode = code->_co_monitoring->lines[offset].original_opcode;
}
if (opcode == INSTRUMENTED_INSTRUCTION) {
opcode = code->_co_monitoring->per_instruction_opcodes[offset];
}
int deinstrumented = DE_INSTRUMENT[opcode];
if (deinstrumented) {
opcode = deinstrumented;
}
else {
opcode = _PyOpcode_Deopt[opcode];
}
assert(opcode != 0);
if (opcode == ENTER_EXECUTOR) {
int exec_index = _PyCode_CODE(code)[offset].op.arg;
_PyExecutorObject *exec = code->co_executors->executors[exec_index];
opcode = _PyOpcode_Deopt[exec->vm_data.opcode];
}
assert(!is_instrumented(opcode));
assert(opcode != ENTER_EXECUTOR);
assert(opcode == _PyOpcode_Deopt[opcode]);
return 1 + _PyOpcode_Caches[opcode];
_Py_CODEUNIT inst = _Py_GetBaseCodeUnit(code, offset);
return 1 + _PyOpcode_Caches[inst.op.code];
}
#ifdef INSTRUMENT_DEBUG
@ -599,16 +579,15 @@ _Py_GetBaseCodeUnit(PyCodeObject *code, int i)
int opcode = inst.op.code;
if (opcode < MIN_INSTRUMENTED_OPCODE) {
inst.op.code = _PyOpcode_Deopt[opcode];
assert(inst.op.code <= RESUME);
assert(inst.op.code < MIN_SPECIALIZED_OPCODE);
return inst;
}
if (opcode == ENTER_EXECUTOR) {
_PyExecutorObject *exec = code->co_executors->executors[inst.op.arg];
opcode = _PyOpcode_Deopt[exec->vm_data.opcode];
inst.op.code = opcode;
assert(opcode <= RESUME);
inst.op.arg = exec->vm_data.oparg;
assert(inst.op.code <= RESUME);
assert(inst.op.code < MIN_SPECIALIZED_OPCODE);
return inst;
}
if (opcode == INSTRUMENTED_LINE) {
@ -1084,6 +1063,8 @@ static const char *const event_names [] = {
[PY_MONITORING_EVENT_INSTRUCTION] = "INSTRUCTION",
[PY_MONITORING_EVENT_JUMP] = "JUMP",
[PY_MONITORING_EVENT_BRANCH] = "BRANCH",
[PY_MONITORING_EVENT_BRANCH_LEFT] = "BRANCH_LEFT",
[PY_MONITORING_EVENT_BRANCH_RIGHT] = "BRANCH_RIGHT",
[PY_MONITORING_EVENT_C_RETURN] = "C_RETURN",
[PY_MONITORING_EVENT_PY_THROW] = "PY_THROW",
[PY_MONITORING_EVENT_RAISE] = "RAISE",
@ -1111,6 +1092,10 @@ call_instrumentation_vector(
/* Offset visible to user should be the offset in bytes, as that is the
* convention for APIs involving code offsets. */
int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT);
if (event == PY_MONITORING_EVENT_BRANCH_LEFT) {
assert(EVENT_FOR_OPCODE[_Py_GetBaseCodeUnit(code, offset-2).op.code] == PY_MONITORING_EVENT_BRANCH_RIGHT);
bytes_offset -= 4;
}
PyObject *offset_obj = PyLong_FromLong(bytes_offset);
if (offset_obj == NULL) {
return -1;
@ -1191,7 +1176,8 @@ _Py_call_instrumentation_jump(
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target)
{
assert(event == PY_MONITORING_EVENT_JUMP ||
event == PY_MONITORING_EVENT_BRANCH);
event == PY_MONITORING_EVENT_BRANCH_RIGHT ||
event == PY_MONITORING_EVENT_BRANCH_LEFT);
assert(frame->instr_ptr == instr);
int to = (int)(target - _PyFrame_GetBytecode(frame));
PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT));
@ -1427,19 +1413,6 @@ _Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame*
return next_opcode;
}
PyObject *
_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj)
{
PyInterpreterState *is = _PyInterpreterState_GET();
assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
assert(0 <= event_id && event_id < _PY_MONITORING_EVENTS);
PyObject *callback = _Py_atomic_exchange_ptr(&is->monitoring_callables[tool_id][event_id],
Py_XNewRef(obj));
return callback;
}
static void
initialize_tools(PyCodeObject *code)
{
@ -2312,6 +2285,10 @@ monitoring_set_events_impl(PyObject *module, int tool_id, int event_set)
return NULL;
}
event_set &= ~C_RETURN_EVENTS;
if (event_set & (1 << PY_MONITORING_EVENT_BRANCH)) {
event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH);
event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT);
}
if (_PyMonitoring_SetEvents(tool_id, event_set)) {
return NULL;
}
@ -2384,6 +2361,10 @@ monitoring_set_local_events_impl(PyObject *module, int tool_id,
return NULL;
}
event_set &= ~C_RETURN_EVENTS;
if (event_set & (1 << PY_MONITORING_EVENT_BRANCH)) {
event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH);
event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT);
}
if (event_set < 0 || event_set >= (1 << _PY_MONITORING_LOCAL_EVENTS)) {
PyErr_Format(PyExc_ValueError, "invalid local event set 0x%x", event_set);
return NULL;
@ -2711,7 +2692,27 @@ _PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int3
assert(state->active);
PyObject *args[4] = { NULL, NULL, NULL, target_offset };
return capi_call_instrumentation(state, codelike, offset, args, 3,
PY_MONITORING_EVENT_BRANCH);
PY_MONITORING_EVENT_BRANCH_RIGHT);
}
int
_PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset)
{
assert(state->active);
PyObject *args[4] = { NULL, NULL, NULL, target_offset };
return capi_call_instrumentation(state, codelike, offset, args, 3,
PY_MONITORING_EVENT_BRANCH_RIGHT);
}
int
_PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset)
{
assert(state->active);
PyObject *args[4] = { NULL, NULL, NULL, target_offset };
return capi_call_instrumentation(state, codelike, offset, args, 3,
PY_MONITORING_EVENT_BRANCH_LEFT);
}
int
@ -2849,3 +2850,213 @@ _PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelik
Py_DECREF(exc);
return exception_event_teardown(err, NULL);
}
/* Handle legacy BRANCH event */
typedef struct _PyLegacyBranchEventHandler {
PyObject_HEAD
vectorcallfunc vectorcall;
PyObject *handler;
bool right;
int tool_id;
} _PyLegacyBranchEventHandler;
static void
dealloc_branch_handler(_PyLegacyBranchEventHandler *self)
{
Py_CLEAR(self->handler);
PyObject_Free((PyObject *)self);
}
static PyTypeObject _PyLegacyBranchEventHandler_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"sys.monitoring.branch_event_handler",
sizeof(_PyLegacyBranchEventHandler),
.tp_dealloc = (destructor)dealloc_branch_handler,
.tp_vectorcall_offset = offsetof(_PyLegacyBranchEventHandler, vectorcall),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_DISALLOW_INSTANTIATION,
.tp_call = PyVectorcall_Call,
};
static PyObject *
branch_handler(
_PyLegacyBranchEventHandler *self, PyObject *const *args,
size_t nargsf, PyObject *kwnames
) {
PyObject *res = PyObject_Vectorcall(self->handler, args, nargsf, kwnames);
if (res == &_PyInstrumentation_DISABLE) {
// Find the other instrumented instruction and remove tool
assert(PyVectorcall_NARGS(nargsf) >= 2);
PyObject *offset_obj = args[1];
int bytes_offset = PyLong_AsLong(offset_obj);
if (PyErr_Occurred()) {
return NULL;
}
PyCodeObject *code = (PyCodeObject *)args[0];
if (!PyCode_Check(code) || (bytes_offset & 1)) {
return res;
}
int offset = bytes_offset / 2;
/* We need FOR_ITER and POP_JUMP_ to be the same size */
assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1);
if (self->right) {
offset += 2;
}
if (offset >= Py_SIZE(code)) {
return res;
}
int other_event = self->right ?
PY_MONITORING_EVENT_BRANCH_LEFT : PY_MONITORING_EVENT_BRANCH_RIGHT;
LOCK_CODE(code);
remove_tools(code, offset, other_event, 1 << self->tool_id);
UNLOCK_CODE();
}
return res;
}
static PyObject *make_branch_handler(int tool_id, PyObject *handler, bool right)
{
_PyLegacyBranchEventHandler *callback =
PyObject_NEW(_PyLegacyBranchEventHandler, &_PyLegacyBranchEventHandler_Type);
if (callback == NULL) {
return NULL;
}
callback->vectorcall = (vectorcallfunc)branch_handler;
callback->handler = Py_NewRef(handler);
callback->right = right;
callback->tool_id = tool_id;
return (PyObject *)callback;
}
/* Consumes a reference to obj */
static PyObject *exchange_callables(int tool_id, int event_id, PyObject *obj)
{
PyInterpreterState *is = _PyInterpreterState_GET();
return _Py_atomic_exchange_ptr(&is->monitoring_callables[tool_id][event_id], obj);
}
PyObject *
_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj)
{
assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
assert(0 <= event_id && event_id < _PY_MONITORING_EVENTS);
PyObject *res;
if (event_id == PY_MONITORING_EVENT_BRANCH) {
PyObject *left, *right;
if (obj == NULL) {
left = NULL;
right = NULL;
}
else {
right = make_branch_handler(tool_id, obj, true);
if (right == NULL) {
return NULL;
}
left = make_branch_handler(tool_id, obj, false);
if (left == NULL) {
Py_DECREF(right);
return NULL;
}
}
Py_XDECREF(exchange_callables(tool_id, PY_MONITORING_EVENT_BRANCH_RIGHT, right));
res = exchange_callables(tool_id, PY_MONITORING_EVENT_BRANCH_LEFT, left);
}
else {
res = exchange_callables(tool_id, event_id, Py_XNewRef(obj));
}
if (res != NULL && Py_TYPE(res) == &_PyLegacyBranchEventHandler_Type) {
_PyLegacyBranchEventHandler *wrapper = (_PyLegacyBranchEventHandler *)res;
res = Py_NewRef(wrapper->handler);
Py_DECREF(wrapper);
}
return res;
}
/* Branch Iterator */
typedef struct {
PyObject_HEAD
PyCodeObject *bi_code;
int bi_offset;
} branchesiterator;
static PyObject *
int_triple(int a, int b, int c) {
PyObject *obja = PyLong_FromLong(a);
PyObject *objb = NULL;
PyObject *objc = NULL;
if (obja == NULL) {
goto error;
}
objb = PyLong_FromLong(b);
if (objb == NULL) {
goto error;
}
objc = PyLong_FromLong(c);
if (objc == NULL) {
goto error;
}
PyObject *array[3] = { obja, objb, objc };
return _PyTuple_FromArraySteal(array, 3);
error:
Py_XDECREF(obja);
Py_XDECREF(objb);
Py_XDECREF(objc);
return NULL;
}
static PyObject *
branchesiter_next(branchesiterator *bi)
{
int offset = bi->bi_offset;
while (offset < Py_SIZE(bi->bi_code)) {
_Py_CODEUNIT inst = _Py_GetBaseCodeUnit(bi->bi_code, offset);
int next_offset = offset + _PyInstruction_GetLength(bi->bi_code, offset);
int event = EVENT_FOR_OPCODE[inst.op.code];
if (event == PY_MONITORING_EVENT_BRANCH_RIGHT) {
/* Skip NOT_TAKEN */
int not_taken = next_offset + 1;
bi->bi_offset = not_taken;
return int_triple(offset*2, not_taken*2, (next_offset + inst.op.arg)*2);
}
offset = next_offset;
}
return NULL;
}
static void
branchesiter_dealloc(branchesiterator *bi)
{
Py_DECREF(bi->bi_code);
PyObject_Free(bi);
}
static PyTypeObject _PyBranchesIterator = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"line_iterator", /* tp_name */
sizeof(branchesiterator), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
.tp_dealloc = (destructor)branchesiter_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_iter = PyObject_SelfIter,
.tp_iternext = (iternextfunc)branchesiter_next,
.tp_free = PyObject_Del,
};
PyObject *
_PyInstrumentation_BranchesIterator(PyCodeObject *code)
{
branchesiterator *bi = (branchesiterator *)PyType_GenericAlloc(&_PyBranchesIterator, 0);
if (bi == NULL) {
return NULL;
}
bi->bi_code = (PyCodeObject*)Py_NewRef(code);
bi->bi_offset = 0;
return (PyObject *)bi;
}

View file

@ -27,6 +27,7 @@ static void *opcode_targets[256] = {
&&TARGET_MATCH_MAPPING,
&&TARGET_MATCH_SEQUENCE,
&&TARGET_NOP,
&&TARGET_NOT_TAKEN,
&&TARGET_POP_EXCEPT,
&&TARGET_POP_TOP,
&&TARGET_PUSH_EXC_INFO,
@ -147,7 +148,6 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_RESUME,
&&TARGET_BINARY_OP_ADD_FLOAT,
&&TARGET_BINARY_OP_ADD_INT,
@ -235,7 +235,6 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_INSTRUMENTED_END_FOR,
&&TARGET_INSTRUMENTED_END_SEND,
&&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR,
@ -244,6 +243,7 @@ static void *opcode_targets[256] = {
&&TARGET_INSTRUMENTED_CALL_FUNCTION_EX,
&&TARGET_INSTRUMENTED_INSTRUCTION,
&&TARGET_INSTRUMENTED_JUMP_FORWARD,
&&TARGET_INSTRUMENTED_NOT_TAKEN,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_TRUE,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_FALSE,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_NONE,

View file

@ -2343,6 +2343,8 @@
/* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 */
/* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 */
/* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 */
/* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 */