Commit aceb0e33 authored by Victor Stinner's avatar Victor Stinner

Issue #26588: add debug traces

Try to debug random failure on buildbots.
parent d07dc186
...@@ -88,6 +88,9 @@ def traceback_filename(filename): ...@@ -88,6 +88,9 @@ def traceback_filename(filename):
class TestTracemallocEnabled(unittest.TestCase): class TestTracemallocEnabled(unittest.TestCase):
def setUp(self): def setUp(self):
if _testcapi:
_testcapi.tracemalloc_set_debug(True)
if tracemalloc.is_tracing(): if tracemalloc.is_tracing():
self.skipTest("tracemalloc must be stopped before the test") self.skipTest("tracemalloc must be stopped before the test")
...@@ -95,6 +98,8 @@ class TestTracemallocEnabled(unittest.TestCase): ...@@ -95,6 +98,8 @@ class TestTracemallocEnabled(unittest.TestCase):
def tearDown(self): def tearDown(self):
tracemalloc.stop() tracemalloc.stop()
if _testcapi:
_testcapi.tracemalloc_set_debug(False)
def test_get_tracemalloc_memory(self): def test_get_tracemalloc_memory(self):
data = [allocate_bytes(123) for count in range(1000)] data = [allocate_bytes(123) for count in range(1000)]
...@@ -877,6 +882,9 @@ class TestCAPI(unittest.TestCase): ...@@ -877,6 +882,9 @@ class TestCAPI(unittest.TestCase):
maxDiff = 80 * 20 maxDiff = 80 * 20
def setUp(self): def setUp(self):
if _testcapi:
_testcapi.tracemalloc_set_debug(True)
if tracemalloc.is_tracing(): if tracemalloc.is_tracing():
self.skipTest("tracemalloc must be stopped before the test") self.skipTest("tracemalloc must be stopped before the test")
...@@ -890,6 +898,8 @@ class TestCAPI(unittest.TestCase): ...@@ -890,6 +898,8 @@ class TestCAPI(unittest.TestCase):
def tearDown(self): def tearDown(self):
tracemalloc.stop() tracemalloc.stop()
if _testcapi:
_testcapi.tracemalloc_set_debug(False)
def get_traceback(self): def get_traceback(self):
frames = _testcapi.tracemalloc_get_traceback(self.domain, self.ptr) frames = _testcapi.tracemalloc_get_traceback(self.domain, self.ptr)
......
...@@ -3747,6 +3747,19 @@ tracemalloc_get_traceback(PyObject *self, PyObject *args) ...@@ -3747,6 +3747,19 @@ tracemalloc_get_traceback(PyObject *self, PyObject *args)
return _PyTraceMalloc_GetTraceback(domain, (Py_uintptr_t)ptr); return _PyTraceMalloc_GetTraceback(domain, (Py_uintptr_t)ptr);
} }
PyObject*
tracemalloc_set_debug(PyObject *self, PyObject *args)
{
int debug;
extern int tracemalloc_debug;
if (!PyArg_ParseTuple(args, "i", &debug))
return NULL;
tracemalloc_debug = debug;
Py_RETURN_NONE;
}
static PyMethodDef TestMethods[] = { static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS}, {"raise_exception", raise_exception, METH_VARARGS},
...@@ -3936,6 +3949,7 @@ static PyMethodDef TestMethods[] = { ...@@ -3936,6 +3949,7 @@ static PyMethodDef TestMethods[] = {
{"tracemalloc_track", tracemalloc_track, METH_VARARGS}, {"tracemalloc_track", tracemalloc_track, METH_VARARGS},
{"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS}, {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS},
{"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS}, {"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS},
{"tracemalloc_set_debug", tracemalloc_set_debug, METH_VARARGS},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -45,6 +45,8 @@ static struct { ...@@ -45,6 +45,8 @@ static struct {
int use_domain; int use_domain;
} tracemalloc_config = {TRACEMALLOC_NOT_INITIALIZED, 0, 1, 1}; } tracemalloc_config = {TRACEMALLOC_NOT_INITIALIZED, 0, 1, 1};
int tracemalloc_debug = 0;
#if defined(TRACE_RAW_MALLOC) && defined(WITH_THREAD) #if defined(TRACE_RAW_MALLOC) && defined(WITH_THREAD)
/* This lock is needed because tracemalloc_free() is called without /* This lock is needed because tracemalloc_free() is called without
the GIL held from PyMem_RawFree(). It cannot acquire the lock because it the GIL held from PyMem_RawFree(). It cannot acquire the lock because it
...@@ -891,18 +893,24 @@ tracemalloc_clear_traces(void) ...@@ -891,18 +893,24 @@ tracemalloc_clear_traces(void)
_Py_hashtable_clear(tracemalloc_filenames); _Py_hashtable_clear(tracemalloc_filenames);
} }
#define DEBUG(MSG) \
if (tracemalloc_debug) { fprintf(stderr, "[pid %li, tid %li] " MSG "\n", (long)getpid(), PyThread_get_thread_ident()); fflush(stderr); }
static int static int
tracemalloc_init(void) tracemalloc_init(void)
{ {
DEBUG("tracemalloc_init()");
if (tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) { if (tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) {
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"the tracemalloc module has been unloaded"); "the tracemalloc module has been unloaded");
return -1; return -1;
} }
if (tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED) if (tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED) {
DEBUG("tracemalloc_init(): exit (already initialized)");
return 0; return 0;
}
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw); PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
...@@ -969,9 +977,11 @@ tracemalloc_init(void) ...@@ -969,9 +977,11 @@ tracemalloc_init(void)
/* Disable tracing allocations until hooks are installed. Set /* Disable tracing allocations until hooks are installed. Set
also the reentrant flag to detect bugs: fail with an assertion error also the reentrant flag to detect bugs: fail with an assertion error
if set_reentrant(1) is called while tracing is disabled. */ if set_reentrant(1) is called while tracing is disabled. */
DEBUG("tracemalloc_init(): set_reentrant(1)");
set_reentrant(1); set_reentrant(1);
tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED; tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
DEBUG("tracemalloc_init(): done");
return 0; return 0;
} }
...@@ -979,8 +989,11 @@ tracemalloc_init(void) ...@@ -979,8 +989,11 @@ tracemalloc_init(void)
static void static void
tracemalloc_deinit(void) tracemalloc_deinit(void)
{ {
if (tracemalloc_config.initialized != TRACEMALLOC_INITIALIZED) DEBUG("tracemalloc_deinit()");
if (tracemalloc_config.initialized != TRACEMALLOC_INITIALIZED) {
DEBUG("tracemalloc_deinit(): exit (not initialized)");
return; return;
}
tracemalloc_config.initialized = TRACEMALLOC_FINALIZED; tracemalloc_config.initialized = TRACEMALLOC_FINALIZED;
tracemalloc_stop(); tracemalloc_stop();
...@@ -997,11 +1010,13 @@ tracemalloc_deinit(void) ...@@ -997,11 +1010,13 @@ tracemalloc_deinit(void)
} }
#endif #endif
DEBUG("tracemalloc_deinit(): delete reentrant key");
#ifdef REENTRANT_THREADLOCAL #ifdef REENTRANT_THREADLOCAL
PyThread_delete_key(tracemalloc_reentrant_key); PyThread_delete_key(tracemalloc_reentrant_key);
#endif #endif
Py_XDECREF(unknown_filename); Py_XDECREF(unknown_filename);
DEBUG("tracemalloc_deinit(): done");
} }
...@@ -1011,11 +1026,15 @@ tracemalloc_start(int max_nframe) ...@@ -1011,11 +1026,15 @@ tracemalloc_start(int max_nframe)
PyMemAllocatorEx alloc; PyMemAllocatorEx alloc;
size_t size; size_t size;
if (tracemalloc_init() < 0) DEBUG("tracemalloc_start()");
if (tracemalloc_init() < 0) {
DEBUG("tracemalloc_start(): ERROR! init failed!");
return -1; return -1;
}
if (tracemalloc_config.tracing) { if (tracemalloc_config.tracing) {
/* hook already installed: do nothing */ /* hook already installed: do nothing */
DEBUG("tracemalloc_start(): exit (already tracing)");
return 0; return 0;
} }
...@@ -1057,8 +1076,11 @@ tracemalloc_start(int max_nframe) ...@@ -1057,8 +1076,11 @@ tracemalloc_start(int max_nframe)
/* everything is ready: start tracing Python memory allocations */ /* everything is ready: start tracing Python memory allocations */
tracemalloc_config.tracing = 1; tracemalloc_config.tracing = 1;
DEBUG("tracemalloc_start(): set_reentrant(0)");
set_reentrant(0); set_reentrant(0);
DEBUG("tracemalloc_start(): done");
return 0; return 0;
} }
...@@ -1066,14 +1088,18 @@ tracemalloc_start(int max_nframe) ...@@ -1066,14 +1088,18 @@ tracemalloc_start(int max_nframe)
static void static void
tracemalloc_stop(void) tracemalloc_stop(void)
{ {
if (!tracemalloc_config.tracing) DEBUG("tracemalloc_stop()");
if (!tracemalloc_config.tracing) {
DEBUG("tracemalloc_stop(): exit (not tracing)");
return; return;
}
/* stop tracing Python memory allocations */ /* stop tracing Python memory allocations */
tracemalloc_config.tracing = 0; tracemalloc_config.tracing = 0;
/* set the reentrant flag to detect bugs: fail with an assertion error if /* set the reentrant flag to detect bugs: fail with an assertion error if
set_reentrant(1) is called while tracing is disabled. */ set_reentrant(1) is called while tracing is disabled. */
DEBUG("tracemalloc_stop(): set_reentrant(1)");
set_reentrant(1); set_reentrant(1);
/* unregister the hook on memory allocators */ /* unregister the hook on memory allocators */
...@@ -1088,6 +1114,7 @@ tracemalloc_stop(void) ...@@ -1088,6 +1114,7 @@ tracemalloc_stop(void)
/* release memory */ /* release memory */
raw_free(tracemalloc_traceback); raw_free(tracemalloc_traceback);
tracemalloc_traceback = NULL; tracemalloc_traceback = NULL;
DEBUG("tracemalloc_stop(): done");
} }
PyDoc_STRVAR(tracemalloc_is_tracing_doc, PyDoc_STRVAR(tracemalloc_is_tracing_doc,
...@@ -1455,8 +1482,11 @@ py_tracemalloc_start(PyObject *self, PyObject *args) ...@@ -1455,8 +1482,11 @@ py_tracemalloc_start(PyObject *self, PyObject *args)
} }
nframe_int = Py_SAFE_DOWNCAST(nframe, Py_ssize_t, int); nframe_int = Py_SAFE_DOWNCAST(nframe, Py_ssize_t, int);
if (tracemalloc_start(nframe_int) < 0) if (tracemalloc_start(nframe_int) < 0) {
DEBUG("start(): ERROR!");
return NULL; return NULL;
}
DEBUG("start(): done");
Py_RETURN_NONE; Py_RETURN_NONE;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment