Skip to content

Commit 0b393a4

Browse files
committed
CHANGELOG entry; fixed edge-case memory bug
line_profiler/_line_profiler.pyx::alloc_callback() Added missing handling for when `malloc()` fails CHANGELOG.rst Added entry
1 parent 67fe213 commit 0b393a4

File tree

2 files changed

+36
-25
lines changed

2 files changed

+36
-25
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Changes
1010
* ENH: Added CLI argument ``-m`` to ``kernprof`` for running a library module as a script; also made it possible for profiling targets to be supplied across multiple ``-p`` flags
1111
* FIX: Fixed explicit profiling of class methods; added handling for profiling static, bound, and partial methods, ``functools.partial`` objects, (cached) properties, and async generator functions
1212
* FIX: Fixed namespace bug when running ``kernprof -m`` on certain modules (e.g. ``calendar`` on Python 3.12+).
13+
* FIX: ``LineProfiler`` now caches the existing ``sys`` trace callback in ``.enable()`` and restores it in ``.disable()``, instead of always discarding it on the way out; also added experimental support for calling (instead of suspending) said callback during profiling #333
1314

1415
4.2.0
1516
~~~~~

line_profiler/_line_profiler.pyx

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,27 @@ cdef extern from "Python.h":
102102

103103
cdef extern from *:
104104
r"""
105+
#if PY_VERSION_HEX >= 0x030D00a1 // 3.13.0a0
106+
#define add_module_ref PyImport_AddModuleRef
107+
#else
108+
inline PyObject *add_module_ref(const char *name) {
109+
PyObject *mod = NULL, *name_str = NULL;
110+
name_str = PyUnicode_FromString(name);
111+
if (name_str == NULL) goto cleanup;
112+
mod = PyImport_AddModuleObject(name_str);
113+
Py_XINCREF(mod);
114+
cleanup:
115+
Py_XDECREF(name_str);
116+
return mod;
117+
}
118+
#endif
119+
#define THIS_MODULE "line_profiler._line_profiler"
120+
#define DISABLE_CALLBACK "disable_line_events"
121+
#define RAISE_IN_CALL(func_name, xc, const_msg) \
122+
PyErr_SetString(xc, \
123+
"in `" THIS_MODULE "." func_name "()`: " \
124+
const_msg)
125+
105126
typedef struct TraceCallback {
106127
/* Notes:
107128
* - These fields are synonymous with the corresponding
@@ -120,7 +141,15 @@ cdef extern from *:
120141
121142
TraceCallback *alloc_callback() {
122143
/* Heap-allocate a new `TraceCallback`. */
123-
return (TraceCallback*)malloc(sizeof(TraceCallback));
144+
TraceCallback *callback = (TraceCallback*)malloc(sizeof(TraceCallback));
145+
if (callback == NULL) RAISE_IN_CALL(
146+
// If we're here we have bigger fish to fry... but be nice
147+
// and raise an error explicitly anyway
148+
"alloc_callback",
149+
PyExc_MemoryError,
150+
"failed to allocate memory for storing the existing "
151+
"`sys` trace callback");
152+
return callback;
124153
}
125154
126155
void free_callback(TraceCallback *callback) {
@@ -171,27 +200,6 @@ cdef extern from *:
171200
|| callback->c_traceobj == NULL);
172201
}
173202
174-
#if PY_VERSION_HEX >= 0x030D00a1 // 3.13.0a0
175-
#define add_module_ref PyImport_AddModuleRef
176-
#else
177-
inline PyObject *add_module_ref(const char *name) {
178-
PyObject *mod = NULL, *name_str = NULL;
179-
name_str = PyUnicode_FromString(name);
180-
if (name_str == NULL) goto cleanup;
181-
mod = PyImport_AddModuleObject(name_str);
182-
Py_XINCREF(mod);
183-
cleanup:
184-
Py_XDECREF(name_str);
185-
return mod;
186-
}
187-
#endif
188-
#define RAISE_IN_CALL(xc, const_msg) \
189-
PyErr_SetString(xc,\
190-
"in `line_profiler._line_profiler.call_callback()`: " \
191-
const_msg)
192-
#define THIS_MODULE "line_profiler._line_profiler"
193-
#define DISABLE_CALLBACK "disable_line_events"
194-
195203
int call_callback(TraceCallback *callback, PyFrameObject *py_frame,
196204
int what, PyObject *arg) {
197205
/* Call the cached trace callback `callback` where appropriate,
@@ -256,14 +264,16 @@ cdef extern from *:
256264
// somewhere?
257265
mod = add_module_ref(THIS_MODULE);
258266
if (mod == NULL) {
259-
RAISE_IN_CALL(PyExc_ImportError,
267+
RAISE_IN_CALL("call_callback",
268+
PyExc_ImportError,
260269
"cannot import `" THIS_MODULE "`");
261270
result = -1;
262271
goto cleanup;
263272
}
264273
dle = PyObject_GetAttrString(mod, DISABLE_CALLBACK);
265274
if (dle == NULL) {
266-
RAISE_IN_CALL(PyExc_AttributeError,
275+
RAISE_IN_CALL("call_callback",
276+
PyExc_AttributeError,
267277
"`line_profiler._line_profiler` has no "
268278
"attribute `" DISABLE_CALLBACK "`");
269279
result = -1;
@@ -292,7 +302,7 @@ cdef extern from *:
292302
Py_tracefunc c_tracefunc
293303
PyObject *c_traceobj
294304

295-
cdef TraceCallback *alloc_callback()
305+
cdef TraceCallback *alloc_callback() except *
296306
cdef void free_callback(TraceCallback *callback)
297307
cdef void fetch_callback(TraceCallback *callback)
298308
cdef void restore_callback(TraceCallback *callback)

0 commit comments

Comments
 (0)