Skip to content

Commit 8b2e17d

Browse files
authored
Merge pull request #257 from Cskorpion/support-py311
Support py311
2 parents b1d242b + 043878e commit 8b2e17d

11 files changed

Lines changed: 439 additions & 18 deletions

setup.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ def run(self):
7878
raise NotImplementedError("platform '%s' is not supported!" % sys.platform)
7979
extra_compile_args.append('-I src/')
8080
extra_compile_args.append('-I src/libbacktrace')
81+
if sys.version_info[:2] == (3,11):
82+
extra_source_files += ['src/populate_frames.c']
8183
ext_modules = [Extension('_vmprof',
8284
sources=[
8385
'src/_vmprof.c',
@@ -121,7 +123,7 @@ def run(self):
121123
'pytz',
122124
'colorama',
123125
] + extra_install_requires,
124-
python_requires='>=3.6, <3.11',
126+
python_requires='>=3.6, <3.12',
125127
tests_require=['pytest','cffi','hypothesis'],
126128
entry_points = {
127129
'console_scripts': [

src/_vmprof.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
#include "_vmprof.h"
1212
#include "vmprof_common.h"
1313

14+
15+
#if PY_VERSION_HEX >= 0x030b00f0 /* >= 3.11 */
16+
#include "internal/pycore_frame.h"
17+
#include "populate_frames.h"
18+
#endif
19+
1420
static destructor Original_code_dealloc = 0;
1521
static PyObject* (*_default_eval_loop)(PyFrameObject *, int) = 0;
1622

@@ -112,7 +118,7 @@ static int _look_for_code_object(PyObject *o, void * param)
112118
/* as a special case, recursively look for and add code
113119
objects found in the co_consts. The problem is that code
114120
objects are not created as GC-aware in CPython, so we need
115-
to hack like this to hope to find most of them.
121+
to hack like this to hope to find most of them.
116122
*/
117123
i = PyTuple_Size(co->co_consts);
118124
while (i > 0) {
@@ -267,6 +273,15 @@ write_all_code_objects(PyObject *module, PyObject * seen_code_ids)
267273
}
268274

269275

276+
#if PY_VERSION_HEX < 0x030900B1 /* < 3.9 */
277+
static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate)
278+
{
279+
Py_XINCREF(tstate->frame);
280+
return tstate->frame;
281+
}
282+
#endif
283+
284+
270285

271286
static PyObject *
272287
sample_stack_now(PyObject *module, PyObject * args)
@@ -298,7 +313,17 @@ sample_stack_now(PyObject *module, PyObject * args)
298313
vmprof_ignore_signals(0);
299314
return NULL;
300315
}
301-
entry_count = vmp_walk_and_record_stack(tstate->frame, m, SINGLE_BUF_SIZE/sizeof(void*)-1, (int)skip, 0);
316+
317+
#if PY_VERSION_HEX >= 0x030B0000 /* < 3.11, no pypy 3.11 at the moment*/
318+
_PyInterpreterFrame * frame = unsafe_PyThreadState_GetInterpreterFrame(tstate);
319+
#else
320+
PyFrameObject* frame = PyThreadState_GetFrame(tstate);
321+
#endif
322+
323+
entry_count = vmp_walk_and_record_stack(frame, m, SINGLE_BUF_SIZE/sizeof(void*)-1, (int)skip, 0);
324+
325+
Py_XDECREF(frame);
326+
302327

303328
for (i = 0; i < entry_count; i++) {
304329
routine_ip = m[i];

src/populate_frames.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/* This code was taken from https://github.com/GoogleCloudPlatform/cloud-profiler-python/blob/main/googlecloudprofiler/src/populate_frames.cc */
2+
3+
#include "populate_frames.h"
4+
5+
#include <Python.h>
6+
7+
8+
// 0x030B0000 is 3.11.
9+
#define PY_311 0x030B0000
10+
#if PY_VERSION_HEX >= PY_311
11+
12+
/**
13+
* The PyFrameObject structure members have been removed from the public C API
14+
* in 3.11:
15+
https://docs.python.org/3/whatsnew/3.11.html#pyframeobject-3-11-hiding.
16+
*
17+
* Instead, getters are provided which participate in reference counting; since
18+
* this code runs as part of the SIGPROF handler, it cannot modify Python
19+
* objects (including their refcounts) and the getters can't be used. Instead,
20+
* we expose the internal _PyInterpreterFrame and use that directly.
21+
*
22+
*/
23+
24+
#define Py_BUILD_CORE
25+
#include "internal/pycore_frame.h"
26+
#undef Py_BUILD_CORE
27+
28+
// Modified from
29+
// https://github.com/python/cpython/blob/v3.11.4/Python/pystate.c#L1278-L1285
30+
_PyInterpreterFrame *unsafe_PyThreadState_GetInterpreterFrame(
31+
PyThreadState *tstate) {
32+
assert(tstate != NULL);
33+
_PyInterpreterFrame *f = tstate->cframe->current_frame;
34+
while (f && _PyFrame_IsIncomplete(f)) {
35+
f = f->previous;
36+
}
37+
if (f == NULL) {
38+
return NULL;
39+
}
40+
return f;
41+
}
42+
43+
// Modified from
44+
// https://github.com/python/cpython/blob/v3.11.4/Objects/frameobject.c#L1310-L1315
45+
// with refcounting removed
46+
PyCodeObject *unsafe_PyInterpreterFrame_GetCode(
47+
_PyInterpreterFrame *frame) {
48+
assert(frame != NULL);
49+
assert(!_PyFrame_IsIncomplete(frame));
50+
PyCodeObject *code = frame->f_code;
51+
assert(code != NULL);
52+
return code;
53+
}
54+
55+
// Modified from
56+
// https://github.com/python/cpython/blob/v3.11.4/Objects/frameobject.c#L1326-L1329
57+
// with refcounting removed
58+
_PyInterpreterFrame *unsafe_PyInterpreterFrame_GetBack(
59+
_PyInterpreterFrame *frame) {
60+
assert(frame != NULL);
61+
assert(!_PyFrame_IsIncomplete(frame));
62+
_PyInterpreterFrame *prev = frame->previous;
63+
while (prev && _PyFrame_IsIncomplete(prev)) {
64+
prev = prev->previous;
65+
}
66+
return prev;
67+
}
68+
69+
// Copied from
70+
// https://github.com/python/cpython/blob/v3.11.4/Python/frame.c#L165-L170 as
71+
// this function is not available in libpython
72+
int _PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame) {
73+
int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
74+
return PyCode_Addr2Line(frame->f_code, addr);
75+
}
76+
#endif // PY_VERSION_HEX >= PY_311

src/populate_frames.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* This code was taken from https://github.com/GoogleCloudPlatform/cloud-profiler-python/blob/main/googlecloudprofiler/src/populate_frames.h */
2+
3+
#ifndef pp_frames
4+
#define pp_frames
5+
6+
#include <Python.h>
7+
8+
#include <frameobject.h>
9+
10+
#define Py_BUILD_CORE
11+
#include "internal/pycore_frame.h"
12+
#undef Py_BUILD_CORE
13+
14+
_PyInterpreterFrame *unsafe_PyThreadState_GetInterpreterFrame(PyThreadState *tstate);
15+
16+
PyCodeObject *unsafe_PyInterpreterFrame_GetCode(_PyInterpreterFrame *frame);
17+
18+
_PyInterpreterFrame *unsafe_PyInterpreterFrame_GetBack(_PyInterpreterFrame *frame);
19+
20+
int _PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame);
21+
22+
#endif

0 commit comments

Comments
 (0)