Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 411b169

Browse filesBrowse files
authored
GH-103082: Implementation of PEP 669: Low Impact Monitoring for CPython (GH-103083)
* The majority of the monitoring code is in instrumentation.c * The new instrumentation bytecodes are in bytecodes.c * legacy_tracing.c adapts the new API to the old sys.setrace and sys.setprofile APIs
1 parent dce2d38 commit 411b169
Copy full SHA for 411b169

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner

44 files changed

+6021
-1617
lines changed

‎Include/cpython/code.h

Copy file name to clipboardExpand all lines: Include/cpython/code.h
+43-2Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,22 @@
33
#ifndef Py_LIMITED_API
44
#ifndef Py_CODE_H
55
#define Py_CODE_H
6+
67
#ifdef __cplusplus
78
extern "C" {
89
#endif
910

11+
12+
/* Count of all "real" monitoring events (not derived from other events) */
13+
#define PY_MONITORING_UNGROUPED_EVENTS 14
14+
/* Count of all monitoring events */
15+
#define PY_MONITORING_EVENTS 16
16+
17+
/* Table of which tools are active for each monitored event. */
18+
typedef struct _Py_Monitors {
19+
uint8_t tools[PY_MONITORING_UNGROUPED_EVENTS];
20+
} _Py_Monitors;
21+
1022
/* Each instruction in a code object is a fixed-width value,
1123
* currently 2 bytes: 1-byte opcode + 1-byte oparg. The EXTENDED_ARG
1224
* opcode allows for larger values but the current limit is 3 uses
@@ -56,6 +68,35 @@ typedef struct {
5668
PyObject *_co_freevars;
5769
} _PyCoCached;
5870

71+
/* Ancilliary data structure used for instrumentation.
72+
Line instrumentation creates an array of
73+
these. One entry per code unit.*/
74+
typedef struct {
75+
uint8_t original_opcode;
76+
int8_t line_delta;
77+
} _PyCoLineInstrumentationData;
78+
79+
/* Main data structure used for instrumentation.
80+
* This is allocated when needed for instrumentation
81+
*/
82+
typedef struct {
83+
/* Monitoring specific to this code object */
84+
_Py_Monitors local_monitors;
85+
/* Monitoring that is active on this code object */
86+
_Py_Monitors active_monitors;
87+
/* The tools that are to be notified for events for the matching code unit */
88+
uint8_t *tools;
89+
/* Information to support line events */
90+
_PyCoLineInstrumentationData *lines;
91+
/* The tools that are to be notified for line events for the matching code unit */
92+
uint8_t *line_tools;
93+
/* Information to support instruction events */
94+
/* The underlying instructions, which can themselves be instrumented */
95+
uint8_t *per_instruction_opcodes;
96+
/* The tools that are to be notified for instruction events for the matching code unit */
97+
uint8_t *per_instruction_tools;
98+
} _PyCoMonitoringData;
99+
59100
// To avoid repeating ourselves in deepfreeze.py, all PyCodeObject members are
60101
// defined in this macro:
61102
#define _PyCode_DEF(SIZE) { \
@@ -87,7 +128,6 @@ typedef struct {
87128
PyObject *co_exceptiontable; /* Byte string encoding exception handling \
88129
table */ \
89130
int co_flags; /* CO_..., see below */ \
90-
short _co_linearray_entry_size; /* Size of each entry in _co_linearray */ \
91131
\
92132
/* The rest are not so impactful on performance. */ \
93133
int co_argcount; /* #arguments, except *args */ \
@@ -114,8 +154,9 @@ typedef struct {
114154
PyObject *co_linetable; /* bytes object that holds location info */ \
115155
PyObject *co_weakreflist; /* to support weakrefs to code objects */ \
116156
_PyCoCached *_co_cached; /* cached co_* attributes */ \
157+
uint64_t _co_instrumentation_version; /* current instrumentation version */ \
158+
_PyCoMonitoringData *_co_monitoring; /* Monitoring data */ \
117159
int _co_firsttraceable; /* index of first traceable instruction */ \
118-
char *_co_linearray; /* array of line offsets */ \
119160
/* Scratch space for extra data relating to the code object. \
120161
Type is a void* to keep the format private in codeobject.c to force \
121162
people to go through the proper APIs. */ \

‎Include/cpython/pystate.h

Copy file name to clipboardExpand all lines: Include/cpython/pystate.h
+1-10Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,6 @@ typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *, int, PyObject *);
5858
#define PyTrace_C_RETURN 6
5959
#define PyTrace_OPCODE 7
6060

61-
62-
typedef struct {
63-
PyCodeObject *code; // The code object for the bounds. May be NULL.
64-
PyCodeAddressRange bounds; // Only valid if code != NULL.
65-
} PyTraceInfo;
66-
6761
// Internal structure: you should not use it directly, but use public functions
6862
// like PyThreadState_EnterTracing() and PyThreadState_LeaveTracing().
6963
typedef struct _PyCFrame {
@@ -77,7 +71,6 @@ typedef struct _PyCFrame {
7771
* discipline and make sure that instances of this struct cannot
7872
* accessed outside of their lifetime.
7973
*/
80-
uint8_t use_tracing; // 0 or 255 (or'ed into opcode, hence 8-bit type)
8174
/* Pointer to the currently executing frame (it can be NULL) */
8275
struct _PyInterpreterFrame *current_frame;
8376
struct _PyCFrame *previous;
@@ -157,7 +150,7 @@ struct _ts {
157150
This is to prevent the actual trace/profile code from being recorded in
158151
the trace/profile. */
159152
int tracing;
160-
int tracing_what; /* The event currently being traced, if any. */
153+
int what_event; /* The event currently being monitored, if any. */
161154

162155
/* Pointer to current _PyCFrame in the C stack frame of the currently,
163156
* or most recently, executing _PyEval_EvalFrameDefault. */
@@ -228,8 +221,6 @@ struct _ts {
228221
/* Unique thread state id. */
229222
uint64_t id;
230223

231-
PyTraceInfo trace_info;
232-
233224
_PyStackChunk *datastack_chunk;
234225
PyObject **datastack_top;
235226
PyObject **datastack_limit;

‎Include/internal/pycore_code.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_code.h
+4-26Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -441,32 +441,6 @@ adaptive_counter_backoff(uint16_t counter) {
441441

442442
/* Line array cache for tracing */
443443

444-
extern int _PyCode_CreateLineArray(PyCodeObject *co);
445-
446-
static inline int
447-
_PyCode_InitLineArray(PyCodeObject *co)
448-
{
449-
if (co->_co_linearray) {
450-
return 0;
451-
}
452-
return _PyCode_CreateLineArray(co);
453-
}
454-
455-
static inline int
456-
_PyCode_LineNumberFromArray(PyCodeObject *co, int index)
457-
{
458-
assert(co->_co_linearray != NULL);
459-
assert(index >= 0);
460-
assert(index < Py_SIZE(co));
461-
if (co->_co_linearray_entry_size == 2) {
462-
return ((int16_t *)co->_co_linearray)[index];
463-
}
464-
else {
465-
assert(co->_co_linearray_entry_size == 4);
466-
return ((int32_t *)co->_co_linearray)[index];
467-
}
468-
}
469-
470444
typedef struct _PyShimCodeDef {
471445
const uint8_t *code;
472446
int codelen;
@@ -500,6 +474,10 @@ extern uint32_t _Py_next_func_version;
500474

501475
#define COMPARISON_NOT_EQUALS (COMPARISON_UNORDERED | COMPARISON_LESS_THAN | COMPARISON_GREATER_THAN)
502476

477+
extern int _Py_Instrument(PyCodeObject *co, PyInterpreterState *interp);
478+
479+
extern int _Py_GetBaseOpcode(PyCodeObject *code, int offset);
480+
503481

504482
#ifdef __cplusplus
505483
}

‎Include/internal/pycore_frame.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_frame.h
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct _frame {
1919
struct _PyInterpreterFrame *f_frame; /* points to the frame data */
2020
PyObject *f_trace; /* Trace function */
2121
int f_lineno; /* Current line number. Only valid if non-zero */
22+
int f_last_traced_line; /* The last line traced for this frame */
2223
char f_trace_lines; /* Emit per-line trace events? */
2324
char f_trace_opcodes; /* Emit per-opcode trace events? */
2425
char f_fast_as_locals; /* Have the fast locals of this frame been converted to a dict? */
@@ -137,10 +138,16 @@ _PyFrame_GetLocalsArray(_PyInterpreterFrame *frame)
137138
return frame->localsplus;
138139
}
139140

141+
/* Fetches the stack pointer, and sets stacktop to -1.
142+
Having stacktop <= 0 ensures that invalid
143+
values are not visible to the cycle GC.
144+
We choose -1 rather than 0 to assist debugging. */
140145
static inline PyObject**
141146
_PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
142147
{
143-
return frame->localsplus+frame->stacktop;
148+
PyObject **sp = frame->localsplus + frame->stacktop;
149+
frame->stacktop = -1;
150+
return sp;
144151
}
145152

146153
static inline void

‎Include/internal/pycore_instruments.h

Copy file name to clipboard
+107Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
2+
#ifndef Py_INTERNAL_INSTRUMENT_H
3+
#define Py_INTERNAL_INSTRUMENT_H
4+
5+
6+
#include "pycore_bitutils.h" // _Py_popcount32
7+
#include "pycore_frame.h"
8+
9+
#include "cpython/code.h"
10+
11+
#ifdef __cplusplus
12+
extern "C" {
13+
#endif
14+
15+
#define PY_MONITORING_TOOL_IDS 8
16+
17+
/* Local events.
18+
* These require bytecode instrumentation */
19+
20+
#define PY_MONITORING_EVENT_PY_START 0
21+
#define PY_MONITORING_EVENT_PY_RESUME 1
22+
#define PY_MONITORING_EVENT_PY_RETURN 2
23+
#define PY_MONITORING_EVENT_PY_YIELD 3
24+
#define PY_MONITORING_EVENT_CALL 4
25+
#define PY_MONITORING_EVENT_LINE 5
26+
#define PY_MONITORING_EVENT_INSTRUCTION 6
27+
#define PY_MONITORING_EVENT_JUMP 7
28+
#define PY_MONITORING_EVENT_BRANCH 8
29+
#define PY_MONITORING_EVENT_STOP_ITERATION 9
30+
31+
#define PY_MONITORING_INSTRUMENTED_EVENTS 10
32+
33+
/* Other events, mainly exceptions */
34+
35+
#define PY_MONITORING_EVENT_RAISE 10
36+
#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 11
37+
#define PY_MONITORING_EVENT_PY_UNWIND 12
38+
#define PY_MONITORING_EVENT_PY_THROW 13
39+
40+
41+
/* Ancilliary events */
42+
43+
#define PY_MONITORING_EVENT_C_RETURN 14
44+
#define PY_MONITORING_EVENT_C_RAISE 15
45+
46+
47+
typedef uint32_t _PyMonitoringEventSet;
48+
49+
/* Tool IDs */
50+
51+
/* These are defined in PEP 669 for convenience to avoid clashes */
52+
#define PY_MONITORING_DEBUGGER_ID 0
53+
#define PY_MONITORING_COVERAGE_ID 1
54+
#define PY_MONITORING_PROFILER_ID 2
55+
#define PY_MONITORING_OPTIMIZER_ID 5
56+
57+
/* Internal IDs used to suuport sys.setprofile() and sys.settrace() */
58+
#define PY_MONITORING_SYS_PROFILE_ID 6
59+
#define PY_MONITORING_SYS_TRACE_ID 7
60+
61+
62+
PyObject *_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj);
63+
64+
int _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events);
65+
66+
extern int
67+
_Py_call_instrumentation(PyThreadState *tstate, int event,
68+
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
69+
70+
extern int
71+
_Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
72+
_Py_CODEUNIT *instr);
73+
74+
extern int
75+
_Py_call_instrumentation_instruction(
76+
PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr);
77+
78+
int
79+
_Py_call_instrumentation_jump(
80+
PyThreadState *tstate, int event,
81+
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target);
82+
83+
extern int
84+
_Py_call_instrumentation_arg(PyThreadState *tstate, int event,
85+
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg);
86+
87+
extern int
88+
_Py_call_instrumentation_2args(PyThreadState *tstate, int event,
89+
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1);
90+
91+
extern void
92+
_Py_call_instrumentation_exc0(PyThreadState *tstate, int event,
93+
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
94+
95+
extern void
96+
_Py_call_instrumentation_exc2(PyThreadState *tstate, int event,
97+
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1);
98+
99+
extern int
100+
_Py_Instrumentation_GetLine(PyCodeObject *code, int index);
101+
102+
extern PyObject _PyInstrumentation_MISSING;
103+
104+
#ifdef __cplusplus
105+
}
106+
#endif
107+
#endif /* !Py_INTERNAL_INSTRUMENT_H */

‎Include/internal/pycore_interp.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_interp.h
+13-1Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern "C" {
2424
#include "pycore_genobject.h" // struct _Py_async_gen_state
2525
#include "pycore_gc.h" // struct _gc_runtime_state
2626
#include "pycore_import.h" // struct _import_state
27+
#include "pycore_instruments.h" // PY_MONITORING_EVENTS
2728
#include "pycore_list.h" // struct _Py_list_state
2829
#include "pycore_global_objects.h" // struct _Py_interp_static_objects
2930
#include "pycore_object_state.h" // struct _py_object_state
@@ -37,7 +38,6 @@ struct _Py_long_state {
3738
int max_str_digits;
3839
};
3940

40-
4141
/* interpreter state */
4242

4343
/* PyInterpreterState holds the global state for one of the runtime's
@@ -49,6 +49,9 @@ struct _is {
4949

5050
PyInterpreterState *next;
5151

52+
uint64_t monitoring_version;
53+
uint64_t last_restart_version;
54+
5255
struct pythreads {
5356
uint64_t next_unique_id;
5457
/* The linked list of threads, newest first. */
@@ -148,6 +151,15 @@ struct _is {
148151
struct callable_cache callable_cache;
149152
PyCodeObject *interpreter_trampoline;
150153

154+
_Py_Monitors monitors;
155+
bool f_opcode_trace_set;
156+
bool sys_profile_initialized;
157+
bool sys_trace_initialized;
158+
Py_ssize_t sys_profiling_threads; /* Count of threads with c_profilefunc set */
159+
Py_ssize_t sys_tracing_threads; /* Count of threads with c_tracefunc set */
160+
PyObject *monitoring_callables[PY_MONITORING_TOOL_IDS][PY_MONITORING_EVENTS];
161+
PyObject *monitoring_tool_names[PY_MONITORING_TOOL_IDS];
162+
151163
struct _Py_interp_cached_objects cached_objects;
152164
struct _Py_interp_static_objects static_objects;
153165

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.