Skip to content

Commit 53c3b51

Browse files
committed
Add test_bytes_resize_tracer()
1 parent a0a3753 commit 53c3b51

1 file changed

Lines changed: 139 additions & 4 deletions

File tree

Modules/_testcapi/mem.c

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,29 @@ fm_remove_hooks(void)
177177
}
178178
}
179179

180+
static void
181+
fm_set_nomemory(int start, int stop)
182+
{
183+
/* Memory allocation fails after 'start' allocation requests, and until
184+
* 'stop' allocation requests except when 'stop' is negative or equal
185+
* to 0 (default) in which case allocation failures never stop. */
186+
FmData.start = start;
187+
FmData.stop = stop;
188+
FmData.count = 0;
189+
fm_setup_hooks();
190+
}
191+
180192
static PyObject *
181193
set_nomemory(PyObject *self, PyObject *args)
182194
{
183195
/* Memory allocation fails after 'start' allocation requests, and until
184196
* 'stop' allocation requests except when 'stop' is negative or equal
185197
* to 0 (default) in which case allocation failures never stop. */
186-
FmData.count = 0;
187-
FmData.stop = 0;
188-
if (!PyArg_ParseTuple(args, "i|i", &FmData.start, &FmData.stop)) {
198+
int start, stop = 0;
199+
if (!PyArg_ParseTuple(args, "i|i", &start, &stop)) {
189200
return NULL;
190201
}
191-
fm_setup_hooks();
202+
fm_set_nomemory(start, stop);
192203
Py_RETURN_NONE;
193204
}
194205

@@ -821,6 +832,129 @@ get_process_memory_usage(PyObject *self, PyObject *args)
821832
#endif
822833

823834

835+
struct bytes_resize_tracer {
836+
PyObject *create;
837+
PyObject *destroy;
838+
};
839+
840+
841+
static int
842+
bytes_resize_tracer(PyObject *obj, PyRefTracerEvent event, void* data)
843+
{
844+
if (event != PyRefTracer_CREATE && event != PyRefTracer_DESTROY) {
845+
return 0;
846+
}
847+
848+
struct bytes_resize_tracer *tracer = (struct bytes_resize_tracer*)data;
849+
if (!PyBytes_Check(obj)) {
850+
return 0;
851+
}
852+
853+
switch (event) {
854+
case PyRefTracer_CREATE:
855+
tracer->create = obj;
856+
break;
857+
case PyRefTracer_DESTROY:
858+
tracer->destroy = obj;
859+
break;
860+
default:
861+
break;
862+
}
863+
return 0;
864+
}
865+
866+
867+
// When _PyBytes_Resize() resizes a bytes object in-place, check that
868+
// PyRefTracer_DESTROY and PyRefTracer_CREATE events are emitted.
869+
// If no_memory is non-zero, inject MemoryError.
870+
static int
871+
check_bytes_resize_tracer(int no_memory)
872+
{
873+
PyObject *bytes = NULL;
874+
PyRefTracer old_tracer = NULL;
875+
void *old_tracer_data = NULL;
876+
int restore_tracer = 0;
877+
878+
bytes = PyBytes_FromString("hello");
879+
if (bytes == NULL) {
880+
goto error;
881+
}
882+
assert(PyUnstable_Object_IsUniquelyReferenced(bytes));
883+
884+
old_tracer = PyRefTracer_GetTracer(&old_tracer_data);
885+
restore_tracer = 1;
886+
887+
struct bytes_resize_tracer tracer = {0};
888+
if (PyRefTracer_SetTracer(bytes_resize_tracer, &tracer) != 0) {
889+
goto error;
890+
}
891+
892+
PyObject *old_bytes = bytes; // borrowed reference
893+
if (no_memory) {
894+
fm_set_nomemory(0, 0);
895+
int res = _PyBytes_Resize(&bytes, 100);
896+
assert(res < 0);
897+
assert(bytes == NULL);
898+
fm_remove_hooks();
899+
900+
assert(PyErr_ExceptionMatches(PyExc_MemoryError));
901+
PyErr_Clear();
902+
}
903+
else {
904+
if (_PyBytes_Resize(&bytes, 100) < 0) {
905+
assert(bytes == NULL);
906+
goto error;
907+
}
908+
}
909+
910+
if (tracer.destroy != old_bytes) {
911+
PyErr_SetString(PyExc_AssertionError, "PyRefTracer_DESTROY not seen");
912+
goto error;
913+
}
914+
915+
int seen_create;
916+
if (no_memory) {
917+
seen_create = (tracer.create == old_bytes);
918+
}
919+
else {
920+
seen_create = (tracer.create == bytes);
921+
}
922+
if (!seen_create) {
923+
PyErr_SetString(PyExc_AssertionError, "PyRefTracer_CREATE not seen");
924+
goto error;
925+
}
926+
927+
Py_CLEAR(bytes);
928+
if (PyRefTracer_SetTracer(old_tracer, old_tracer_data) != 0) {
929+
restore_tracer = 0;
930+
goto error;
931+
}
932+
return 0;
933+
934+
error:
935+
Py_XDECREF(bytes);
936+
if (restore_tracer) {
937+
if (PyRefTracer_SetTracer(old_tracer, old_tracer_data) != 0) {
938+
return -1;
939+
}
940+
}
941+
return -1;
942+
}
943+
944+
945+
static PyObject*
946+
test_bytes_resize_tracer(PyObject *self, PyObject *Py_UNUSED(ignored))
947+
{
948+
if (check_bytes_resize_tracer(0) < 0) {
949+
return NULL;
950+
}
951+
if (check_bytes_resize_tracer(1) < 0) {
952+
return NULL;
953+
}
954+
Py_RETURN_NONE;
955+
}
956+
957+
824958
static PyMethodDef test_methods[] = {
825959
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
826960
{"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS},
@@ -838,6 +972,7 @@ static PyMethodDef test_methods[] = {
838972
#if TARGET_OS_OSX || defined(__FreeBSD__)
839973
{"get_process_memory_usage", get_process_memory_usage, METH_VARARGS},
840974
#endif
975+
{"test_bytes_resize_tracer", test_bytes_resize_tracer, METH_NOARGS},
841976

842977
// Tracemalloc tests
843978
{"tracemalloc_track", tracemalloc_track, METH_VARARGS},

0 commit comments

Comments
 (0)