Skip to content

Commit a1db71f

Browse files
committed
Exit ASAP if no audit hook was added
Avoid any performance overhead when no hook was added. Enhance also test_audit_tuple(): add a hook and reuse "sawSet" test.
1 parent 4a7dfe5 commit a1db71f

File tree

2 files changed

+48
-14
lines changed

2 files changed

+48
-14
lines changed

Programs/_testembed.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,18 +1321,24 @@ static int test_audit(void)
13211321

13221322
static int test_audit_tuple(void)
13231323
{
1324+
Py_ssize_t sawSet = 0;
1325+
1326+
// we need at least one hook, otherwise code checking for
1327+
// PySys_AuditTuple() is skipped.
1328+
PySys_AddAuditHook(_audit_hook, &sawSet);
13241329
_testembed_Py_InitializeFromConfig();
13251330

13261331
assert(!PyErr_Occurred());
13271332

13281333
// pass tuple
1329-
PyObject *tuple = Py_BuildValue("ii", 3, 5);
1334+
PyObject *tuple = Py_BuildValue("(i)", 444);
13301335
if (tuple == NULL) {
13311336
goto error;
13321337
}
1333-
assert(PySys_AuditTuple("_testembed.test_audit_tuple", tuple) == 0);
1338+
assert(PySys_AuditTuple("_testembed.set", tuple) == 0);
13341339
assert(!PyErr_Occurred());
13351340
Py_DECREF(tuple);
1341+
assert(sawSet == 444);
13361342

13371343
// NULL is accepted and means "no arguments"
13381344
assert(PySys_AuditTuple("_testembed.test_audit_tuple", NULL) == 0);

Python/sysmodule.c

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ _PySys_ClearAttrString(PyInterpreterState *interp,
172172

173173

174174
static int
175-
should_audit(PyInterpreterState *interp)
175+
should_audit_interp(PyInterpreterState *interp)
176176
{
177177
/* interp must not be NULL, but test it just in case for extra safety */
178178
assert(interp != NULL);
@@ -186,10 +186,8 @@ should_audit(PyInterpreterState *interp)
186186

187187

188188
static int
189-
sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
189+
should_audit_tstate(PyThreadState *ts)
190190
{
191-
assert(PyTuple_Check(eventArgs));
192-
193191
if (!ts) {
194192
/* Audit hooks cannot be called with a NULL thread state */
195193
return 0;
@@ -199,18 +197,33 @@ sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
199197
the current Python thread state. */
200198
assert(ts == _PyThreadState_GET());
201199

200+
/* Early exit when no hooks are registered */
201+
PyInterpreterState *is = ts->interp;
202+
if (!should_audit_interp(is)) {
203+
return 0;
204+
}
205+
return 1;
206+
}
207+
208+
209+
static int
210+
sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
211+
{
212+
// The caller check should_audit_tstate() or should_audit_interp() as soon
213+
// as possible to avoid any performance overhead if no hook was added.
214+
assert(should_audit_tstate(ts));
215+
216+
// The caller must check via should_audit_tstate() that tstate is not NULL
217+
assert(ts != NULL);
218+
219+
assert(PyTuple_Check(eventArgs));
220+
202221
if (event == NULL) {
203222
_PyErr_SetString(ts, PyExc_ValueError,
204223
"event argument must not be NULL");
205224
return -1;
206225
}
207226

208-
/* Early exit when no hooks are registered */
209-
PyInterpreterState *is = ts->interp;
210-
if (!should_audit(is)) {
211-
return 0;
212-
}
213-
214227
PyObject *eventName = NULL;
215228
PyObject *hooks = NULL;
216229
PyObject *hook = NULL;
@@ -225,6 +238,7 @@ sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
225238
*
226239
* We don't worry about any races on hooks getting added,
227240
* since that would not leave is in an inconsistent state. */
241+
PyInterpreterState *is = ts->interp;
228242
_Py_AuditHookEntry *e = is->runtime->audit_hooks.head;
229243
for (; e; e = e->next) {
230244
if (e->hookCFunction(event, eventArgs, e->userData) < 0) {
@@ -333,6 +347,11 @@ int
333347
_PySys_Audit(PyThreadState *tstate, const char *event,
334348
const char *format, ...)
335349
{
350+
// tstate can be NULL
351+
if (!should_audit_tstate(tstate)) {
352+
return 0;
353+
}
354+
336355
va_list vargs;
337356
va_start(vargs, format);
338357
int res = sys_audit_vargs(tstate, event, format, vargs);
@@ -344,6 +363,11 @@ int
344363
PySys_Audit(const char *event, const char *format, ...)
345364
{
346365
PyThreadState *tstate = _PyThreadState_GET();
366+
// tstate can be NULL if the function is called before Py_Initialize()
367+
if (!should_audit_tstate(tstate)) {
368+
return 0;
369+
}
370+
347371
va_list vargs;
348372
va_start(vargs, format);
349373
int res = sys_audit_vargs(tstate, event, format, vargs);
@@ -355,8 +379,12 @@ int
355379
PySys_AuditTuple(const char *event, PyObject *args)
356380
{
357381
PyThreadState *tstate = _PyThreadState_GET();
358-
int delete_args = 0;
382+
// tstate can be NULL if the function is called before Py_Initialize()
383+
if (!should_audit_tstate(tstate)) {
384+
return 0;
385+
}
359386

387+
int delete_args = 0;
360388
if (args == NULL) {
361389
delete_args = 1;
362390
args = PyTuple_New(0);
@@ -549,7 +577,7 @@ sys_audit(PyObject *self, PyObject *const *args, Py_ssize_t argc)
549577
return NULL;
550578
}
551579

552-
if (!should_audit(tstate->interp)) {
580+
if (!should_audit_interp(tstate->interp)) {
553581
Py_RETURN_NONE;
554582
}
555583

0 commit comments

Comments
 (0)