#include "Python.h"
#include "opcode_ids.h"
#include "pycore_bitutils.h"
#include "pycore_call.h"
#include "pycore_ceval.h"
#include "pycore_code.h"
#include "pycore_critical_section.h"
#include "pycore_frame.h"
#include "pycore_interp.h"
#include "pycore_long.h"
#include "pycore_modsupport.h"
#include "pycore_namespace.h"
#include "pycore_object.h"
#include "pycore_opcode_metadata.h"
#include "pycore_pyatomic_ft_wrappers.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h"
#if defined(Py_DEBUG) && defined(Py_GIL_DISABLED)
#define ASSERT_WORLD_STOPPED_OR_LOCKED …
#define ASSERT_WORLD_STOPPED …
#else
#define ASSERT_WORLD_STOPPED_OR_LOCKED(obj) …
#define ASSERT_WORLD_STOPPED() …
#endif
#ifdef Py_GIL_DISABLED
#define LOCK_CODE …
#define UNLOCK_CODE …
#define MODIFY_BYTECODE …
#else
#define LOCK_CODE(code) …
#define UNLOCK_CODE() …
#define MODIFY_BYTECODE(code, func, ...) …
#endif
PyObject _PyInstrumentation_DISABLE = …;
PyObject _PyInstrumentation_MISSING = …;
static const int8_t EVENT_FOR_OPCODE[256] = …;
static const uint8_t DE_INSTRUMENT[256] = …;
static const uint8_t INSTRUMENTED_OPCODES[256] = …;
static inline bool
opcode_has_event(int opcode)
{ … }
static inline bool
is_instrumented(int opcode)
{ … }
#ifndef NDEBUG
static inline bool
monitors_equals(_Py_LocalMonitors a, _Py_LocalMonitors b)
{
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
if (a.tools[i] != b.tools[i]) {
return false;
}
}
return true;
}
#endif
static inline _Py_LocalMonitors
monitors_sub(_Py_LocalMonitors a, _Py_LocalMonitors b)
{ … }
#ifndef NDEBUG
static inline _Py_LocalMonitors
monitors_and(_Py_LocalMonitors a, _Py_LocalMonitors b)
{
_Py_LocalMonitors res;
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
res.tools[i] = a.tools[i] & b.tools[i];
}
return res;
}
#endif
static inline _Py_LocalMonitors
local_union(_Py_GlobalMonitors a, _Py_LocalMonitors b)
{ … }
static inline bool
monitors_are_empty(_Py_LocalMonitors m)
{ … }
static inline bool
multiple_tools(_Py_LocalMonitors *m)
{ … }
static inline _PyMonitoringEventSet
get_local_events(_Py_LocalMonitors *m, int tool_id)
{ … }
static inline _PyMonitoringEventSet
get_events(_Py_GlobalMonitors *m, int tool_id)
{ … }
#define NO_LINE …
#define COMPUTED_LINE_LINENO_CHANGE …
#define COMPUTED_LINE …
#define OFFSET_SHIFT …
static int8_t
compute_line_delta(PyCodeObject *code, int offset, int line)
{ … }
static int
compute_line(PyCodeObject *code, int offset, int8_t line_delta)
{ … }
int
_PyInstruction_GetLength(PyCodeObject *code, int offset)
{ … }
#ifdef INSTRUMENT_DEBUG
static void
dump_instrumentation_data_tools(PyCodeObject *code, uint8_t *tools, int i, FILE*out)
{
if (tools == NULL) {
fprintf(out, "tools = NULL");
}
else {
fprintf(out, "tools = %d", tools[i]);
}
}
static void
dump_instrumentation_data_lines(PyCodeObject *code, _PyCoLineInstrumentationData *lines, int i, FILE*out)
{
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);
}
}
static void
dump_instrumentation_data_line_tools(PyCodeObject *code, uint8_t *line_tools, int i, FILE*out)
{
if (line_tools == NULL) {
fprintf(out, ", line_tools = NULL");
}
else {
fprintf(out, ", line_tools = %d", line_tools[i]);
}
}
static void
dump_instrumentation_data_per_instruction(PyCodeObject *code, _PyCoMonitoringData *data, int i, FILE*out)
{
if (data->per_instruction_opcodes == NULL) {
fprintf(out, ", per-inst opcode = NULL");
}
else {
fprintf(out, ", per-inst opcode = %s", _PyOpcode_OpName[data->per_instruction_opcodes[i]]);
}
if (data->per_instruction_tools == NULL) {
fprintf(out, ", per-inst tools = NULL");
}
else {
fprintf(out, ", per-inst tools = %d", data->per_instruction_tools[i]);
}
}
static void
dump_global_monitors(const char *prefix, _Py_GlobalMonitors monitors, FILE*out)
{
fprintf(out, "%s monitors:\n", prefix);
for (int event = 0; event < _PY_MONITORING_UNGROUPED_EVENTS; event++) {
fprintf(out, " Event %d: Tools %x\n", event, monitors.tools[event]);
}
}
static void
dump_local_monitors(const char *prefix, _Py_LocalMonitors monitors, FILE*out)
{
fprintf(out, "%s monitors:\n", prefix);
for (int event = 0; event < _PY_MONITORING_LOCAL_EVENTS; event++) {
fprintf(out, " Event %d: Tools %x\n", event, monitors.tools[event]);
}
}
static void
dump_instrumentation_data(PyCodeObject *code, int star, FILE*out)
{
_PyCoMonitoringData *data = code->_co_monitoring;
fprintf(out, "\n");
PyObject_Print(code->co_name, out, Py_PRINT_RAW);
fprintf(out, "\n");
if (data == NULL) {
fprintf(out, "NULL\n");
return;
}
dump_global_monitors("Global", _PyInterpreterState_GET()->monitors, out);
dump_local_monitors("Code", data->local_monitors, out);
dump_local_monitors("Active", data->active_monitors, out);
int code_len = (int)Py_SIZE(code);
bool starred = false;
for (int i = 0; i < code_len; i += _PyInstruction_GetLength(code, i)) {
_Py_CODEUNIT *instr = &_PyCode_CODE(code)[i];
int opcode = instr->op.code;
if (i == star) {
fprintf(out, "** ");
starred = true;
}
fprintf(out, "Offset: %d, line: %d %s: ", i, PyCode_Addr2Line(code, i*2), _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);
dump_instrumentation_data_per_instruction(code, data, i, out);
fprintf(out, "\n");
;
}
if (!starred && star >= 0) {
fprintf(out, "Error offset not at valid instruction offset: %d\n", star);
fprintf(out, " ");
dump_instrumentation_data_tools(code, data->tools, star, out);
dump_instrumentation_data_lines(code, data->lines, star, out);
dump_instrumentation_data_line_tools(code, data->line_tools, star, out);
dump_instrumentation_data_per_instruction(code, data, star, out);
fprintf(out, "\n");
}
}
#define CHECK …
static bool
valid_opcode(int opcode)
{
if (opcode == INSTRUMENTED_LINE) {
return true;
}
if (IS_VALID_OPCODE(opcode) &&
opcode != CACHE &&
opcode != RESERVED &&
opcode < 255)
{
return true;
}
return false;
}
static void
sanity_check_instrumentation(PyCodeObject *code)
{
ASSERT_WORLD_STOPPED_OR_LOCKED(code);
_PyCoMonitoringData *data = code->_co_monitoring;
if (data == NULL) {
return;
}
_Py_GlobalMonitors global_monitors = _PyInterpreterState_GET()->monitors;
_Py_LocalMonitors active_monitors;
if (code->_co_monitoring) {
_Py_LocalMonitors local_monitors = code->_co_monitoring->local_monitors;
active_monitors = local_union(global_monitors, local_monitors);
}
else {
_Py_LocalMonitors empty = (_Py_LocalMonitors) { 0 };
active_monitors = local_union(global_monitors, empty);
}
assert(monitors_equals(
code->_co_monitoring->active_monitors,
active_monitors));
int code_len = (int)Py_SIZE(code);
for (int i = 0; i < code_len;) {
_Py_CODEUNIT *instr = &_PyCode_CODE(code)[i];
int opcode = instr->op.code;
int base_opcode = _Py_GetBaseCodeUnit(code, offset).op.code;
CHECK(valid_opcode(opcode));
CHECK(valid_opcode(base_opcode));
if (opcode == INSTRUMENTED_INSTRUCTION) {
opcode = data->per_instruction_opcodes[i];
if (!is_instrumented(opcode)) {
CHECK(_PyOpcode_Deopt[opcode] == opcode);
}
}
if (opcode == INSTRUMENTED_LINE) {
CHECK(data->lines);
CHECK(valid_opcode(data->lines[i].original_opcode));
opcode = data->lines[i].original_opcode;
CHECK(opcode != END_FOR);
CHECK(opcode != RESUME);
CHECK(opcode != RESUME_CHECK);
CHECK(opcode != INSTRUMENTED_RESUME);
if (!is_instrumented(opcode)) {
CHECK(_PyOpcode_Deopt[opcode] == opcode);
}
CHECK(opcode != INSTRUMENTED_LINE);
}
else if (data->lines) {
CHECK(data->lines[i].original_opcode != INSTRUMENTED_INSTRUCTION);
}
if (opcode == INSTRUMENTED_INSTRUCTION) {
CHECK(data->per_instruction_opcodes[i] != 0);
opcode = data->per_instruction_opcodes[i];
}
if (is_instrumented(opcode)) {
CHECK(DE_INSTRUMENT[opcode] == base_opcode);
int event = EVENT_FOR_OPCODE[DE_INSTRUMENT[opcode]];
if (event < 0) {
event = instr->op.arg ? 1: 0;
}
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));
CHECK(line1 == line2);
}
CHECK(valid_opcode(opcode));
if (data->tools) {
uint8_t local_tools = data->tools[i];
if (opcode_has_event(base_opcode)) {
int event = EVENT_FOR_OPCODE[base_opcode];
if (event == -1) {
event = _PyCode_CODE(code)[i].op.arg;
}
CHECK((active_monitors.tools[event] & local_tools) == local_tools);
}
else {
CHECK(local_tools == 0xff);
}
}
i += _PyInstruction_GetLength(code, i);
assert(i <= code_len);
}
}
#else
#define CHECK(test) …
#endif
_Py_CODEUNIT
_Py_GetBaseCodeUnit(PyCodeObject *code, int i)
{ … }
static void
de_instrument(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring, int i,
int event)
{ … }
static void
de_instrument_line(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring,
int i)
{ … }
static void
de_instrument_per_instruction(_Py_CODEUNIT *bytecode,
_PyCoMonitoringData *monitoring, int i)
{ … }
static void
instrument(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring, int i)
{ … }
static void
instrument_line(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring, int i)
{ … }
static void
instrument_per_instruction(_Py_CODEUNIT *bytecode,
_PyCoMonitoringData *monitoring, int i)
{ … }
static void
remove_tools(PyCodeObject * code, int offset, int event, int tools)
{ … }
#ifndef NDEBUG
static bool
tools_is_subset_for_event(PyCodeObject * code, int event, int tools)
{
int global_tools = _PyInterpreterState_GET()->monitors.tools[event];
int local_tools = code->_co_monitoring->local_monitors.tools[event];
return tools == ((global_tools | local_tools) & tools);
}
#endif
static void
remove_line_tools(PyCodeObject * code, int offset, int tools)
{ … }
static void
add_tools(PyCodeObject * code, int offset, int event, int tools)
{ … }
static void
add_line_tools(PyCodeObject * code, int offset, int tools)
{ … }
static void
add_per_instruction_tools(PyCodeObject * code, int offset, int tools)
{ … }
static void
remove_per_instruction_tools(PyCodeObject * code, int offset, int tools)
{ … }
static int
call_one_instrument(
PyInterpreterState *interp, PyThreadState *tstate, PyObject **args,
size_t nargsf, int8_t tool, int event)
{ … }
static const int8_t MOST_SIGNIFICANT_BITS[16] = …;
static inline int most_significant_bit(uint8_t bits) { … }
static uint32_t
global_version(PyInterpreterState *interp)
{ … }
static void
set_version_raw(uintptr_t *ptr, uint32_t version)
{ … }
static void
set_global_version(PyThreadState *tstate, uint32_t version)
{ … }
static bool
is_version_up_to_date(PyCodeObject *code, PyInterpreterState *interp)
{ … }
#ifndef NDEBUG
static bool
instrumentation_cross_checks(PyInterpreterState *interp, PyCodeObject *code)
{
ASSERT_WORLD_STOPPED_OR_LOCKED(code);
_Py_LocalMonitors expected = local_union(
interp->monitors,
code->_co_monitoring->local_monitors);
return monitors_equals(code->_co_monitoring->active_monitors, expected);
}
static int
debug_check_sanity(PyInterpreterState *interp, PyCodeObject *code)
{
int res;
LOCK_CODE(code);
res = is_version_up_to_date(code, interp) &&
instrumentation_cross_checks(interp, code);
UNLOCK_CODE();
return res;
}
#endif
static inline uint8_t
get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i, int event)
{ … }
static const char *const event_names [] = …;
static int
call_instrumentation_vector(
PyThreadState *tstate, int event,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, Py_ssize_t nargs, PyObject *args[])
{ … }
int
_Py_call_instrumentation(
PyThreadState *tstate, int event,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr)
{ … }
int
_Py_call_instrumentation_arg(
PyThreadState *tstate, int event,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg)
{ … }
int
_Py_call_instrumentation_2args(
PyThreadState *tstate, int event,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1)
{ … }
_Py_CODEUNIT *
_Py_call_instrumentation_jump(
PyThreadState *tstate, int event,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target)
{ … }
static void
call_instrumentation_vector_protected(
PyThreadState *tstate, int event,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, Py_ssize_t nargs, PyObject *args[])
{ … }
void
_Py_call_instrumentation_exc2(
PyThreadState *tstate, int event,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1)
{ … }
int
_Py_Instrumentation_GetLine(PyCodeObject *code, int index)
{ … }
int
_Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *prev)
{ … }
int
_Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr)
{ … }
PyObject *
_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj)
{ … }
static void
initialize_tools(PyCodeObject *code)
{ … }
#define NO_LINE …
static void
initialize_lines(PyCodeObject *code)
{ … }
static void
initialize_line_tools(PyCodeObject *code, _Py_LocalMonitors *all_events)
{ … }
static int
allocate_instrumentation_data(PyCodeObject *code)
{ … }
static int
update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
{ … }
static int
force_instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
{ … }
static int
instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
{ … }
int
_Py_Instrument(PyCodeObject *code, PyInterpreterState *interp)
{ … }
#define C_RETURN_EVENTS …
#define C_CALL_EVENTS …
static int
instrument_all_executing_code_objects(PyInterpreterState *interp) { … }
static void
set_events(_Py_GlobalMonitors *m, int tool_id, _PyMonitoringEventSet events)
{ … }
static void
set_local_events(_Py_LocalMonitors *m, int tool_id, _PyMonitoringEventSet events)
{ … }
static int
check_tool(PyInterpreterState *interp, int tool_id)
{ … }
#define MONITORING_VERSION_INCREMENT …
int
_PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events)
{ … }
int
_PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEventSet events)
{ … }
int
_PyMonitoring_GetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEventSet *events)
{ … }
int _PyMonitoring_ClearToolId(int tool_id)
{ … }
#include "clinic/instrumentation.c.h"
static int
check_valid_tool(int tool_id)
{ … }
static PyObject *
monitoring_use_tool_id_impl(PyObject *module, int tool_id, PyObject *name)
{ … }
static PyObject *
monitoring_clear_tool_id_impl(PyObject *module, int tool_id)
{ … }
static PyObject *
monitoring_free_tool_id_impl(PyObject *module, int tool_id)
{ … }
static PyObject *
monitoring_get_tool_impl(PyObject *module, int tool_id)
{ … }
static PyObject *
monitoring_register_callback_impl(PyObject *module, int tool_id, int event,
PyObject *func)
{ … }
static int
monitoring_get_events_impl(PyObject *module, int tool_id)
{ … }
static PyObject *
monitoring_set_events_impl(PyObject *module, int tool_id, int event_set)
{ … }
static int
monitoring_get_local_events_impl(PyObject *module, int tool_id,
PyObject *code)
{ … }
static PyObject *
monitoring_set_local_events_impl(PyObject *module, int tool_id,
PyObject *code, int event_set)
{ … }
static PyObject *
monitoring_restart_events_impl(PyObject *module)
{ … }
static int
add_power2_constant(PyObject *obj, const char *name, int i)
{ … }
static PyObject *
monitoring__all_events_impl(PyObject *module)
{ … }
static PyMethodDef methods[] = …;
static struct PyModuleDef monitoring_module = …;
PyObject *_Py_CreateMonitoringObject(void)
{ … }
static int
capi_call_instrumentation(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject **args, Py_ssize_t nargs, int event)
{ … }
int
PyMonitoring_EnterScope(PyMonitoringState *state_array, uint64_t *version,
const uint8_t *event_types, Py_ssize_t length)
{ … }
int
PyMonitoring_ExitScope(void)
{ … }
int
_PyMonitoring_FirePyStartEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FirePyResumeEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FirePyReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject* retval)
{ … }
int
_PyMonitoring_FirePyYieldEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject* retval)
{ … }
int
_PyMonitoring_FireCallEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject* callable, PyObject *arg0)
{ … }
int
_PyMonitoring_FireLineEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
int lineno)
{ … }
int
_PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset)
{ … }
int
_PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset)
{ … }
int
_PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *retval)
{ … }
static inline int
exception_event_setup(PyObject **exc, int event) { … }
static inline int
exception_event_teardown(int err, PyObject *exc) { … }
int
_PyMonitoring_FirePyThrowEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FireRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FireCRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FireReraiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FireExceptionHandledEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FirePyUnwindEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
{ … }
int
_PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *value)
{ … }