Skip to content

Commit

Permalink
Exit ASAP if no audit hook was added
Browse files Browse the repository at this point in the history
Avoid any performance overhead when no hook was added.

Enhance also test_audit_tuple(): add a hook and reuse "sawSet" test.
  • Loading branch information
vstinner committed Sep 6, 2023
1 parent 528ac80 commit cb25901
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 14 deletions.
10 changes: 8 additions & 2 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -1314,18 +1314,24 @@ static int test_audit(void)

static int test_audit_tuple(void)
{
Py_ssize_t sawSet = 0;

// we need at least one hook, otherwise code checking for
// PySys_AuditTuple() is skipped.
PySys_AddAuditHook(_audit_hook, &sawSet);
_testembed_Py_InitializeFromConfig();

assert(!PyErr_Occurred());

// pass tuple
PyObject *tuple = Py_BuildValue("ii", 3, 5);
PyObject *tuple = Py_BuildValue("(i)", 444);
if (tuple == NULL) {
goto error;
}
assert(PySys_AuditTuple("_testembed.test_audit_tuple", tuple) == 0);
assert(PySys_AuditTuple("_testembed.set", tuple) == 0);
assert(!PyErr_Occurred());
Py_DECREF(tuple);
assert(sawSet == 444);

// NULL is accepted and means "no arguments"
assert(PySys_AuditTuple("_testembed.test_audit_tuple", NULL) == 0);
Expand Down
52 changes: 40 additions & 12 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ _PySys_ClearAttrString(PyInterpreterState *interp,


static int
should_audit(PyInterpreterState *interp)
should_audit_interp(PyInterpreterState *interp)
{
/* interp must not be NULL, but test it just in case for extra safety */
assert(interp != NULL);
Expand All @@ -185,10 +185,8 @@ should_audit(PyInterpreterState *interp)


static int
sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
should_audit_tstate(PyThreadState *ts)
{
assert(PyTuple_Check(eventArgs));

if (!ts) {
/* Audit hooks cannot be called with a NULL thread state */
return 0;
Expand All @@ -198,18 +196,33 @@ sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
the current Python thread state. */
assert(ts == _PyThreadState_GET());

/* Early exit when no hooks are registered */
PyInterpreterState *is = ts->interp;
if (!should_audit_interp(is)) {
return 0;
}
return 1;
}


static int
sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
{
// The caller check should_audit_tstate() or should_audit_interp() as soon
// as possible to avoid any performance overhead if no hook was added.
assert(should_audit_tstate(ts));

// The caller must check via should_audit_tstate() that tstate is not NULL
assert(ts != NULL);

assert(PyTuple_Check(eventArgs));

if (event == NULL) {
_PyErr_SetString(ts, PyExc_ValueError,
"event argument must not be NULL");
return -1;
}

/* Early exit when no hooks are registered */
PyInterpreterState *is = ts->interp;
if (!should_audit(is)) {
return 0;
}

PyObject *eventName = NULL;
PyObject *hooks = NULL;
PyObject *hook = NULL;
Expand All @@ -224,6 +237,7 @@ sys_audit_tuple(PyThreadState *ts, const char *event, PyObject *eventArgs)
*
* We don't worry about any races on hooks getting added,
* since that would not leave is in an inconsistent state. */
PyInterpreterState *is = ts->interp;
_Py_AuditHookEntry *e = is->runtime->audit_hooks.head;
for (; e; e = e->next) {
if (e->hookCFunction(event, eventArgs, e->userData) < 0) {
Expand Down Expand Up @@ -332,6 +346,11 @@ int
_PySys_Audit(PyThreadState *tstate, const char *event,
const char *format, ...)
{
// tstate can be NULL
if (!should_audit_tstate(tstate)) {
return 0;
}

va_list vargs;
va_start(vargs, format);
int res = sys_audit_vargs(tstate, event, format, vargs);
Expand All @@ -343,6 +362,11 @@ int
PySys_Audit(const char *event, const char *format, ...)
{
PyThreadState *tstate = _PyThreadState_GET();
// tstate can be NULL if the function is called before Py_Initialize()
if (!should_audit_tstate(tstate)) {
return 0;
}

va_list vargs;
va_start(vargs, format);
int res = sys_audit_vargs(tstate, event, format, vargs);
Expand All @@ -354,8 +378,12 @@ int
PySys_AuditTuple(const char *event, PyObject *args)
{
PyThreadState *tstate = _PyThreadState_GET();
int delete_args = 0;
// tstate can be NULL if the function is called before Py_Initialize()
if (!should_audit_tstate(tstate)) {
return 0;
}

int delete_args = 0;
if (args == NULL) {
delete_args = 1;
args = PyTuple_New(0);
Expand Down Expand Up @@ -548,7 +576,7 @@ sys_audit(PyObject *self, PyObject *const *args, Py_ssize_t argc)
return NULL;
}

if (!should_audit(tstate->interp)) {
if (!should_audit_interp(tstate->interp)) {
Py_RETURN_NONE;
}

Expand Down

0 comments on commit cb25901

Please sign in to comment.