mirror of https://github.com/python/cpython.git
Closes #19786: tracemalloc, remove the arbitrary limit of 100 frames
The limit is now 178,956,969 on 64 bit (it is greater on 32 bit because structures are smaller). Use int instead of Py_ssize_t to store the number of frames to have smaller traceback_t objects.
This commit is contained in:
parent
3c0481d426
commit
f28ce60441
|
@ -747,14 +747,14 @@ def test_env_var(self):
|
||||||
self.assertEqual(stdout, b'10')
|
self.assertEqual(stdout, b'10')
|
||||||
|
|
||||||
def test_env_var_invalid(self):
|
def test_env_var_invalid(self):
|
||||||
for nframe in (-1, 0, 5000):
|
for nframe in (-1, 0, 2**30):
|
||||||
with self.subTest(nframe=nframe):
|
with self.subTest(nframe=nframe):
|
||||||
with support.SuppressCrashReport():
|
with support.SuppressCrashReport():
|
||||||
ok, stdout, stderr = assert_python_failure(
|
ok, stdout, stderr = assert_python_failure(
|
||||||
'-c', 'pass',
|
'-c', 'pass',
|
||||||
PYTHONTRACEMALLOC=str(nframe))
|
PYTHONTRACEMALLOC=str(nframe))
|
||||||
self.assertIn(b'PYTHONTRACEMALLOC must be an integer '
|
self.assertIn(b'PYTHONTRACEMALLOC: invalid '
|
||||||
b'in range [1; 100]',
|
b'number of frames',
|
||||||
stderr)
|
stderr)
|
||||||
|
|
||||||
def test_sys_xoptions(self):
|
def test_sys_xoptions(self):
|
||||||
|
@ -770,13 +770,13 @@ def test_sys_xoptions(self):
|
||||||
self.assertEqual(stdout, str(nframe).encode('ascii'))
|
self.assertEqual(stdout, str(nframe).encode('ascii'))
|
||||||
|
|
||||||
def test_sys_xoptions_invalid(self):
|
def test_sys_xoptions_invalid(self):
|
||||||
for nframe in (-1, 0, 5000):
|
for nframe in (-1, 0, 2**30):
|
||||||
with self.subTest(nframe=nframe):
|
with self.subTest(nframe=nframe):
|
||||||
with support.SuppressCrashReport():
|
with support.SuppressCrashReport():
|
||||||
args = ('-X', 'tracemalloc=%s' % nframe, '-c', 'pass')
|
args = ('-X', 'tracemalloc=%s' % nframe, '-c', 'pass')
|
||||||
ok, stdout, stderr = assert_python_failure(*args)
|
ok, stdout, stderr = assert_python_failure(*args)
|
||||||
self.assertIn(b'-X tracemalloc=NFRAME: number of frame must '
|
self.assertIn(b'-X tracemalloc=NFRAME: invalid '
|
||||||
b'be an integer in range [1; 100]',
|
b'number of frames',
|
||||||
stderr)
|
stderr)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,6 @@ static struct {
|
||||||
PyMemAllocator obj;
|
PyMemAllocator obj;
|
||||||
} allocators;
|
} allocators;
|
||||||
|
|
||||||
/* Arbitrary limit of the number of frames in a traceback. The value was chosen
|
|
||||||
to not allocate too much memory on the stack (see TRACEBACK_STACK_SIZE
|
|
||||||
below). */
|
|
||||||
#define MAX_NFRAME 100
|
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
/* Module initialized?
|
/* Module initialized?
|
||||||
Variable protected by the GIL */
|
Variable protected by the GIL */
|
||||||
|
@ -88,7 +83,9 @@ typedef struct {
|
||||||
|
|
||||||
#define TRACEBACK_SIZE(NFRAME) \
|
#define TRACEBACK_SIZE(NFRAME) \
|
||||||
(sizeof(traceback_t) + sizeof(frame_t) * (NFRAME - 1))
|
(sizeof(traceback_t) + sizeof(frame_t) * (NFRAME - 1))
|
||||||
#define TRACEBACK_STACK_SIZE TRACEBACK_SIZE(MAX_NFRAME)
|
|
||||||
|
#define MAX_NFRAME \
|
||||||
|
((INT_MAX - sizeof(traceback_t)) / sizeof(frame_t) + 1)
|
||||||
|
|
||||||
static PyObject *unknown_filename = NULL;
|
static PyObject *unknown_filename = NULL;
|
||||||
static traceback_t tracemalloc_empty_traceback;
|
static traceback_t tracemalloc_empty_traceback;
|
||||||
|
@ -115,6 +112,10 @@ static size_t tracemalloc_peak_traced_memory = 0;
|
||||||
Protected by the GIL */
|
Protected by the GIL */
|
||||||
static _Py_hashtable_t *tracemalloc_filenames = NULL;
|
static _Py_hashtable_t *tracemalloc_filenames = NULL;
|
||||||
|
|
||||||
|
/* Buffer to store a new traceback in traceback_new().
|
||||||
|
Protected by the GIL. */
|
||||||
|
static traceback_t *tracemalloc_traceback = NULL;
|
||||||
|
|
||||||
/* Hash table used as a set to intern tracebacks:
|
/* Hash table used as a set to intern tracebacks:
|
||||||
traceback_t* => traceback_t*
|
traceback_t* => traceback_t*
|
||||||
Protected by the GIL */
|
Protected by the GIL */
|
||||||
|
@ -394,8 +395,7 @@ traceback_get_frames(traceback_t *traceback)
|
||||||
static traceback_t *
|
static traceback_t *
|
||||||
traceback_new(void)
|
traceback_new(void)
|
||||||
{
|
{
|
||||||
char stack_buffer[TRACEBACK_STACK_SIZE];
|
traceback_t *traceback;
|
||||||
traceback_t *traceback = (traceback_t *)stack_buffer;
|
|
||||||
_Py_hashtable_entry_t *entry;
|
_Py_hashtable_entry_t *entry;
|
||||||
|
|
||||||
#ifdef WITH_THREAD
|
#ifdef WITH_THREAD
|
||||||
|
@ -403,6 +403,7 @@ traceback_new(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* get frames */
|
/* get frames */
|
||||||
|
traceback = tracemalloc_traceback;
|
||||||
traceback->nframe = 0;
|
traceback->nframe = 0;
|
||||||
traceback_get_frames(traceback);
|
traceback_get_frames(traceback);
|
||||||
if (traceback->nframe == 0)
|
if (traceback->nframe == 0)
|
||||||
|
@ -788,9 +789,10 @@ tracemalloc_deinit(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
tracemalloc_start(void)
|
tracemalloc_start(int max_nframe)
|
||||||
{
|
{
|
||||||
PyMemAllocator alloc;
|
PyMemAllocator alloc;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
if (tracemalloc_init() < 0)
|
if (tracemalloc_init() < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -803,6 +805,18 @@ tracemalloc_start(void)
|
||||||
if (tracemalloc_atexit_register() < 0)
|
if (tracemalloc_atexit_register() < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
assert(1 <= max_nframe && max_nframe <= MAX_NFRAME);
|
||||||
|
tracemalloc_config.max_nframe = max_nframe;
|
||||||
|
|
||||||
|
/* allocate a buffer to store a new traceback */
|
||||||
|
size = TRACEBACK_SIZE(max_nframe);
|
||||||
|
assert(tracemalloc_traceback == NULL);
|
||||||
|
tracemalloc_traceback = raw_malloc(size);
|
||||||
|
if (tracemalloc_traceback == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TRACE_RAW_MALLOC
|
#ifdef TRACE_RAW_MALLOC
|
||||||
alloc.malloc = tracemalloc_raw_malloc;
|
alloc.malloc = tracemalloc_raw_malloc;
|
||||||
alloc.realloc = tracemalloc_raw_realloc;
|
alloc.realloc = tracemalloc_raw_realloc;
|
||||||
|
@ -854,9 +868,10 @@ tracemalloc_stop(void)
|
||||||
|
|
||||||
/* release memory */
|
/* release memory */
|
||||||
tracemalloc_clear_traces();
|
tracemalloc_clear_traces();
|
||||||
|
raw_free(tracemalloc_traceback);
|
||||||
|
tracemalloc_traceback = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
lineno_as_obj(int lineno)
|
lineno_as_obj(int lineno)
|
||||||
{
|
{
|
||||||
|
@ -1194,6 +1209,7 @@ static PyObject*
|
||||||
py_tracemalloc_start(PyObject *self, PyObject *args)
|
py_tracemalloc_start(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
Py_ssize_t nframe = 1;
|
Py_ssize_t nframe = 1;
|
||||||
|
int nframe_int;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|n:start", &nframe))
|
if (!PyArg_ParseTuple(args, "|n:start", &nframe))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1201,12 +1217,12 @@ py_tracemalloc_start(PyObject *self, PyObject *args)
|
||||||
if (nframe < 1 || nframe > MAX_NFRAME) {
|
if (nframe < 1 || nframe > MAX_NFRAME) {
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"the number of frames must be in range [1; %i]",
|
"the number of frames must be in range [1; %i]",
|
||||||
MAX_NFRAME);
|
(int)MAX_NFRAME);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
tracemalloc_config.max_nframe = Py_SAFE_DOWNCAST(nframe, Py_ssize_t, int);
|
nframe_int = Py_SAFE_DOWNCAST(nframe, Py_ssize_t, int);
|
||||||
|
|
||||||
if (tracemalloc_start() < 0)
|
if (tracemalloc_start(nframe_int) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -1378,16 +1394,15 @@ _PyTraceMalloc_Init(void)
|
||||||
|
|
||||||
if ((p = Py_GETENV("PYTHONTRACEMALLOC")) && *p != '\0') {
|
if ((p = Py_GETENV("PYTHONTRACEMALLOC")) && *p != '\0') {
|
||||||
char *endptr = p;
|
char *endptr = p;
|
||||||
unsigned long value;
|
long value;
|
||||||
|
|
||||||
value = strtoul(p, &endptr, 10);
|
value = strtol(p, &endptr, 10);
|
||||||
if (*endptr != '\0'
|
if (*endptr != '\0'
|
||||||
|| value < 1
|
|| value < 1
|
||||||
|| value > MAX_NFRAME
|
|| value > MAX_NFRAME
|
||||||
|| (errno == ERANGE && value == ULONG_MAX))
|
|| (errno == ERANGE && value == ULONG_MAX))
|
||||||
{
|
{
|
||||||
Py_FatalError("PYTHONTRACEMALLOC must be an integer "
|
Py_FatalError("PYTHONTRACEMALLOC: invalid number of frames");
|
||||||
"in range [1; " STR(MAX_NFRAME) "]");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1417,12 +1432,10 @@ _PyTraceMalloc_Init(void)
|
||||||
nframe = parse_sys_xoptions(value);
|
nframe = parse_sys_xoptions(value);
|
||||||
Py_DECREF(value);
|
Py_DECREF(value);
|
||||||
if (nframe < 0) {
|
if (nframe < 0) {
|
||||||
Py_FatalError("-X tracemalloc=NFRAME: number of frame must be "
|
Py_FatalError("-X tracemalloc=NFRAME: invalid number of frames");
|
||||||
"an integer in range [1; " STR(MAX_NFRAME) "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracemalloc_config.max_nframe = nframe;
|
return tracemalloc_start(nframe);
|
||||||
return tracemalloc_start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue