summaryrefslogtreecommitdiff
path: root/Objects/obmalloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'Objects/obmalloc.c')
-rw-r--r--Objects/obmalloc.c432
1 files changed, 249 insertions, 183 deletions
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
index 7cc889f817..a1142f3b09 100644
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -1,31 +1,42 @@
#include "Python.h"
+#include <stdbool.h>
+
+
+/* Defined in tracemalloc.c */
+extern void _PyMem_DumpTraceback(int fd, const void *ptr);
+
+
/* Python's malloc wrappers (see pymem.h) */
-#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */
+#undef uint
+#define uint unsigned int /* assuming >= 16 bits */
+
/* Forward declaration */
+static void* _PyMem_DebugRawMalloc(void *ctx, size_t size);
+static void* _PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize);
+static void* _PyMem_DebugRawRealloc(void *ctx, void *ptr, size_t size);
+static void _PyMem_DebugRawFree(void *ctx, void *p);
+
static void* _PyMem_DebugMalloc(void *ctx, size_t size);
static void* _PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize);
-static void _PyMem_DebugFree(void *ctx, void *p);
static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size);
+static void _PyMem_DebugFree(void *ctx, void *p);
static void _PyObject_DebugDumpAddress(const void *p);
static void _PyMem_DebugCheckAddress(char api_id, const void *p);
-#endif
#if defined(__has_feature) /* Clang */
#if __has_feature(address_sanitizer) /* is ASAN enabled? */
#define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
- __attribute__((no_address_safety_analysis)) \
- __attribute__ ((noinline))
+ __attribute__((no_address_safety_analysis))
#else
#define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
#endif
#else
#if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
#define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
- __attribute__((no_address_safety_analysis)) \
- __attribute__ ((noinline))
+ __attribute__((no_address_safety_analysis))
#else
#define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
#endif
@@ -145,9 +156,8 @@ _PyObject_ArenaFree(void *ctx, void *ptr, size_t size)
#else
# define PYOBJ_FUNCS PYRAW_FUNCS
#endif
-#define PYMEM_FUNCS PYRAW_FUNCS
+#define PYMEM_FUNCS PYOBJ_FUNCS
-#ifdef PYMALLOC_DEBUG
typedef struct {
/* We tag each block with an API ID in order to tag API violations */
char api_id;
@@ -163,19 +173,21 @@ static struct {
{'o', {NULL, PYOBJ_FUNCS}}
};
-#define PYDBG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree
-#endif
+#define PYRAWDBG_FUNCS \
+ _PyMem_DebugRawMalloc, _PyMem_DebugRawCalloc, _PyMem_DebugRawRealloc, _PyMem_DebugRawFree
+#define PYDBG_FUNCS \
+ _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree
static PyMemAllocatorEx _PyMem_Raw = {
-#ifdef PYMALLOC_DEBUG
- &_PyMem_Debug.raw, PYDBG_FUNCS
+#ifdef Py_DEBUG
+ &_PyMem_Debug.raw, PYRAWDBG_FUNCS
#else
NULL, PYRAW_FUNCS
#endif
};
static PyMemAllocatorEx _PyMem = {
-#ifdef PYMALLOC_DEBUG
+#ifdef Py_DEBUG
&_PyMem_Debug.mem, PYDBG_FUNCS
#else
NULL, PYMEM_FUNCS
@@ -183,16 +195,76 @@ static PyMemAllocatorEx _PyMem = {
};
static PyMemAllocatorEx _PyObject = {
-#ifdef PYMALLOC_DEBUG
+#ifdef Py_DEBUG
&_PyMem_Debug.obj, PYDBG_FUNCS
#else
NULL, PYOBJ_FUNCS
#endif
};
+int
+_PyMem_SetupAllocators(const char *opt)
+{
+ if (opt == NULL || *opt == '\0') {
+ /* PYTHONMALLOC is empty or is not set or ignored (-E/-I command line
+ options): use default allocators */
+#ifdef Py_DEBUG
+# ifdef WITH_PYMALLOC
+ opt = "pymalloc_debug";
+# else
+ opt = "malloc_debug";
+# endif
+#else
+ /* !Py_DEBUG */
+# ifdef WITH_PYMALLOC
+ opt = "pymalloc";
+# else
+ opt = "malloc";
+# endif
+#endif
+ }
+
+ if (strcmp(opt, "debug") == 0) {
+ PyMem_SetupDebugHooks();
+ }
+ else if (strcmp(opt, "malloc") == 0 || strcmp(opt, "malloc_debug") == 0)
+ {
+ PyMemAllocatorEx alloc = {NULL, PYRAW_FUNCS};
+
+ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
+
+ if (strcmp(opt, "malloc_debug") == 0)
+ PyMem_SetupDebugHooks();
+ }
+#ifdef WITH_PYMALLOC
+ else if (strcmp(opt, "pymalloc") == 0
+ || strcmp(opt, "pymalloc_debug") == 0)
+ {
+ PyMemAllocatorEx raw_alloc = {NULL, PYRAW_FUNCS};
+ PyMemAllocatorEx mem_alloc = {NULL, PYMEM_FUNCS};
+ PyMemAllocatorEx obj_alloc = {NULL, PYOBJ_FUNCS};
+
+ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &mem_alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &obj_alloc);
+
+ if (strcmp(opt, "pymalloc_debug") == 0)
+ PyMem_SetupDebugHooks();
+ }
+#endif
+ else {
+ /* unknown allocator */
+ return -1;
+ }
+ return 0;
+}
+
#undef PYRAW_FUNCS
#undef PYMEM_FUNCS
#undef PYOBJ_FUNCS
+#undef PYRAWDBG_FUNCS
#undef PYDBG_FUNCS
static PyObjectArenaAllocator _PyObject_Arena = {NULL,
@@ -205,23 +277,46 @@ static PyObjectArenaAllocator _PyObject_Arena = {NULL,
#endif
};
+#ifdef WITH_PYMALLOC
+static int
+_PyMem_DebugEnabled(void)
+{
+ return (_PyObject.malloc == _PyMem_DebugMalloc);
+}
+
+int
+_PyMem_PymallocEnabled(void)
+{
+ if (_PyMem_DebugEnabled()) {
+ return (_PyMem_Debug.obj.alloc.malloc == _PyObject_Malloc);
+ }
+ else {
+ return (_PyObject.malloc == _PyObject_Malloc);
+ }
+}
+#endif
+
void
PyMem_SetupDebugHooks(void)
{
-#ifdef PYMALLOC_DEBUG
PyMemAllocatorEx alloc;
- alloc.malloc = _PyMem_DebugMalloc;
- alloc.calloc = _PyMem_DebugCalloc;
- alloc.realloc = _PyMem_DebugRealloc;
- alloc.free = _PyMem_DebugFree;
+ alloc.malloc = _PyMem_DebugRawMalloc;
+ alloc.calloc = _PyMem_DebugRawCalloc;
+ alloc.realloc = _PyMem_DebugRawRealloc;
+ alloc.free = _PyMem_DebugRawFree;
- if (_PyMem_Raw.malloc != _PyMem_DebugMalloc) {
+ if (_PyMem_Raw.malloc != _PyMem_DebugRawMalloc) {
alloc.ctx = &_PyMem_Debug.raw;
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &_PyMem_Debug.raw.alloc);
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
}
+ alloc.malloc = _PyMem_DebugMalloc;
+ alloc.calloc = _PyMem_DebugCalloc;
+ alloc.realloc = _PyMem_DebugRealloc;
+ alloc.free = _PyMem_DebugFree;
+
if (_PyMem.malloc != _PyMem_DebugMalloc) {
alloc.ctx = &_PyMem_Debug.mem;
PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &_PyMem_Debug.mem.alloc);
@@ -233,7 +328,6 @@ PyMem_SetupDebugHooks(void)
PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc);
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
}
-#endif
}
void
@@ -264,7 +358,6 @@ PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break;
/* ignore unknown domain */
}
-
}
void
@@ -642,24 +735,8 @@ static int running_on_valgrind = -1;
#define SIMPLELOCK_LOCK(lock) /* acquire released lock */
#define SIMPLELOCK_UNLOCK(lock) /* release acquired lock */
-/*
- * Basic types
- * I don't care if these are defined in <sys/types.h> or elsewhere. Axiom.
- */
-#undef uchar
-#define uchar unsigned char /* assuming == 8 bits */
-
-#undef uint
-#define uint unsigned int /* assuming >= 16 bits */
-
-#undef ulong
-#define ulong unsigned long /* assuming >= 32 bits */
-
-#undef uptr
-#define uptr Py_uintptr_t
-
/* When you say memory, my mind reasons in terms of (pointers to) blocks */
-typedef uchar block;
+typedef uint8_t block;
/* Pool for small blocks. */
struct pool_header {
@@ -683,7 +760,7 @@ struct arena_object {
* here to mark an arena_object that doesn't correspond to an
* allocated arena.
*/
- uptr address;
+ uintptr_t address;
/* Pool-aligned pointer to the next pool to be carved off. */
block* pool_address;
@@ -834,7 +911,7 @@ on that C doesn't insert any padding anywhere in a pool_header at or before
the prevpool member.
**************************************************************************** */
-#define PTA(x) ((poolp )((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *)))
+#define PTA(x) ((poolp )((uint8_t *)&(usedpools[2*(x)]) - 2*sizeof(block *)))
#define PT(x) PTA(x), PTA(x)
static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = {
@@ -949,11 +1026,15 @@ new_arena(void)
struct arena_object* arenaobj;
uint excess; /* number of bytes above pool alignment */
void *address;
+ static int debug_stats = -1;
-#ifdef PYMALLOC_DEBUG
- if (Py_GETENV("PYTHONMALLOCSTATS"))
+ if (debug_stats == -1) {
+ char *opt = Py_GETENV("PYTHONMALLOCSTATS");
+ debug_stats = (opt != NULL && *opt != '\0');
+ }
+ if (debug_stats)
_PyObject_DebugMallocStats(stderr);
-#endif
+
if (unused_arena_objects == NULL) {
uint i;
uint numarenas;
@@ -966,7 +1047,7 @@ new_arena(void)
if (numarenas <= maxarenas)
return NULL; /* overflow */
#if SIZEOF_SIZE_T <= SIZEOF_INT
- if (numarenas > PY_SIZE_MAX / sizeof(*arenas))
+ if (numarenas > SIZE_MAX / sizeof(*arenas))
return NULL; /* overflow */
#endif
nbytes = numarenas * sizeof(*arenas);
@@ -1010,7 +1091,7 @@ new_arena(void)
unused_arena_objects = arenaobj;
return NULL;
}
- arenaobj->address = (uptr)address;
+ arenaobj->address = (uintptr_t)address;
++narenas_currently_allocated;
++ntimes_arena_allocated;
@@ -1033,13 +1114,13 @@ new_arena(void)
}
/*
-Py_ADDRESS_IN_RANGE(P, POOL)
+address_in_range(P, POOL)
Return true if and only if P is an address that was allocated by pymalloc.
POOL must be the pool address associated with P, i.e., POOL = POOL_ADDR(P)
(the caller is asked to compute this because the macro expands POOL more than
once, and for efficiency it's best for the caller to assign POOL_ADDR(P) to a
-variable and pass the latter to the macro; because Py_ADDRESS_IN_RANGE is
+variable and pass the latter to the macro; because address_in_range is
called on every alloc/realloc/free, micro-efficiency is important here).
Tricky: Let B be the arena base address associated with the pool, B =
@@ -1064,7 +1145,7 @@ arenas[(POOL)->arenaindex]. Suppose obmalloc controls P. Then (barring wild
stores, etc), POOL is the correct address of P's pool, AO.address is the
correct base address of the pool's arena, and P must be within ARENA_SIZE of
AO.address. In addition, AO.address is not 0 (no arena can start at address 0
-(NULL)). Therefore Py_ADDRESS_IN_RANGE correctly reports that obmalloc
+(NULL)). Therefore address_in_range correctly reports that obmalloc
controls P.
Now suppose obmalloc does not control P (e.g., P was obtained via a direct
@@ -1105,51 +1186,21 @@ that this test determines whether an arbitrary address is controlled by
obmalloc in a small constant time, independent of the number of arenas
obmalloc controls. Since this test is needed at every entry point, it's
extremely desirable that it be this fast.
-
-Since Py_ADDRESS_IN_RANGE may be reading from memory which was not allocated
-by Python, it is important that (POOL)->arenaindex is read only once, as
-another thread may be concurrently modifying the value without holding the
-GIL. To accomplish this, the arenaindex_temp variable is used to store
-(POOL)->arenaindex for the duration of the Py_ADDRESS_IN_RANGE macro's
-execution. The caller of the macro is responsible for declaring this
-variable.
*/
-#define Py_ADDRESS_IN_RANGE(P, POOL) \
- ((arenaindex_temp = (POOL)->arenaindex) < maxarenas && \
- (uptr)(P) - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE && \
- arenas[arenaindex_temp].address != 0)
-
-
-/* This is only useful when running memory debuggers such as
- * Purify or Valgrind. Uncomment to use.
- *
-#define Py_USING_MEMORY_DEBUGGER
- */
-
-#ifdef Py_USING_MEMORY_DEBUGGER
-
-/* Py_ADDRESS_IN_RANGE may access uninitialized memory by design
- * This leads to thousands of spurious warnings when using
- * Purify or Valgrind. By making a function, we can easily
- * suppress the uninitialized memory reads in this one function.
- * So we won't ignore real errors elsewhere.
- *
- * Disable the macro and use a function.
- */
-
-#undef Py_ADDRESS_IN_RANGE
-
-#if defined(__GNUC__) && ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) || \
- (__GNUC__ >= 4))
-#define Py_NO_INLINE __attribute__((__noinline__))
-#else
-#define Py_NO_INLINE
-#endif
-/* Don't make static, to try to ensure this isn't inlined. */
-int Py_ADDRESS_IN_RANGE(void *P, poolp pool) Py_NO_INLINE;
-#undef Py_NO_INLINE
-#endif
+static bool ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
+address_in_range(void *p, poolp pool)
+{
+ // Since address_in_range may be reading from memory which was not allocated
+ // by Python, it is important that pool->arenaindex is read only once, as
+ // another thread may be concurrently modifying the value without holding
+ // the GIL. The following dance forces the compiler to read pool->arenaindex
+ // only once.
+ uint arenaindex = *((volatile uint *)&pool->arenaindex);
+ return arenaindex < maxarenas &&
+ (uintptr_t)p - arenas[arenaindex].address < ARENA_SIZE &&
+ arenas[arenaindex].address != 0;
+}
/*==========================================================================*/
@@ -1394,7 +1445,6 @@ _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize)
/* free */
-ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
static void
_PyObject_Free(void *ctx, void *p)
{
@@ -1402,9 +1452,6 @@ _PyObject_Free(void *ctx, void *p)
block *lastfree;
poolp next, prev;
uint size;
-#ifndef Py_USING_MEMORY_DEBUGGER
- uint arenaindex_temp;
-#endif
if (p == NULL) /* free(NULL) has no effect */
return;
@@ -1417,7 +1464,7 @@ _PyObject_Free(void *ctx, void *p)
#endif
pool = POOL_ADDR(p);
- if (Py_ADDRESS_IN_RANGE(p, pool)) {
+ if (address_in_range(p, pool)) {
/* We allocated this address. */
LOCK();
/* Link p to the start of the pool's freeblock list. Since
@@ -1623,16 +1670,12 @@ redirect:
* return a non-NULL result.
*/
-ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
static void *
_PyObject_Realloc(void *ctx, void *p, size_t nbytes)
{
void *bp;
poolp pool;
size_t size;
-#ifndef Py_USING_MEMORY_DEBUGGER
- uint arenaindex_temp;
-#endif
if (p == NULL)
return _PyObject_Alloc(0, ctx, 1, nbytes);
@@ -1644,7 +1687,7 @@ _PyObject_Realloc(void *ctx, void *p, size_t nbytes)
#endif
pool = POOL_ADDR(p);
- if (Py_ADDRESS_IN_RANGE(p, pool)) {
+ if (address_in_range(p, pool)) {
/* We're in charge of this block */
size = INDEX2SIZE(pool->szidx);
if (nbytes <= size) {
@@ -1709,7 +1752,7 @@ _Py_GetAllocatedBlocks(void)
#endif /* WITH_PYMALLOC */
-#ifdef PYMALLOC_DEBUG
+
/*==========================================================================*/
/* A x-platform debugging allocator. This doesn't manage memory directly,
* it wraps a real allocator, adding extra debugging info to the memory blocks.
@@ -1743,7 +1786,7 @@ bumpserialno(void)
static size_t
read_size_t(const void *p)
{
- const uchar *q = (const uchar *)p;
+ const uint8_t *q = (const uint8_t *)p;
size_t result = *q++;
int i;
@@ -1758,40 +1801,15 @@ read_size_t(const void *p)
static void
write_size_t(void *p, size_t n)
{
- uchar *q = (uchar *)p + SST - 1;
+ uint8_t *q = (uint8_t *)p + SST - 1;
int i;
for (i = SST; --i >= 0; --q) {
- *q = (uchar)(n & 0xff);
+ *q = (uint8_t)(n & 0xff);
n >>= 8;
}
}
-#ifdef Py_DEBUG
-/* Is target in the list? The list is traversed via the nextpool pointers.
- * The list may be NULL-terminated, or circular. Return 1 if target is in
- * list, else 0.
- */
-static int
-pool_is_in_list(const poolp target, poolp list)
-{
- poolp origlist = list;
- assert(target != NULL);
- if (list == NULL)
- return 0;
- do {
- if (target == list)
- return 1;
- list = list->nextpool;
- } while (list != NULL && list != origlist);
- return 0;
-}
-
-#else
-#define pool_is_in_list(X, Y) 1
-
-#endif /* Py_DEBUG */
-
/* Let S = sizeof(size_t). The debug malloc asks for 4*S extra bytes and
fills them with useful stuff, here calling the underlying malloc's result p:
@@ -1819,11 +1837,11 @@ p[2*S+n+S: 2*S+n+2*S]
*/
static void *
-_PyMem_DebugAlloc(int use_calloc, void *ctx, size_t nbytes)
+_PyMem_DebugRawAlloc(int use_calloc, void *ctx, size_t nbytes)
{
debug_alloc_api_t *api = (debug_alloc_api_t *)ctx;
- uchar *p; /* base address of malloc'ed block */
- uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */
+ uint8_t *p; /* base address of malloc'ed block */
+ uint8_t *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */
size_t total; /* nbytes + 4*SST */
bumpserialno();
@@ -1833,15 +1851,15 @@ _PyMem_DebugAlloc(int use_calloc, void *ctx, size_t nbytes)
return NULL;
if (use_calloc)
- p = (uchar *)api->alloc.calloc(api->alloc.ctx, 1, total);
+ p = (uint8_t *)api->alloc.calloc(api->alloc.ctx, 1, total);
else
- p = (uchar *)api->alloc.malloc(api->alloc.ctx, total);
+ p = (uint8_t *)api->alloc.malloc(api->alloc.ctx, total);
if (p == NULL)
return NULL;
/* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */
write_size_t(p, nbytes);
- p[SST] = (uchar)api->api_id;
+ p[SST] = (uint8_t)api->api_id;
memset(p + SST + 1, FORBIDDENBYTE, SST-1);
if (nbytes > 0 && !use_calloc)
@@ -1856,18 +1874,18 @@ _PyMem_DebugAlloc(int use_calloc, void *ctx, size_t nbytes)
}
static void *
-_PyMem_DebugMalloc(void *ctx, size_t nbytes)
+_PyMem_DebugRawMalloc(void *ctx, size_t nbytes)
{
- return _PyMem_DebugAlloc(0, ctx, nbytes);
+ return _PyMem_DebugRawAlloc(0, ctx, nbytes);
}
static void *
-_PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize)
+_PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize)
{
size_t nbytes;
assert(elsize == 0 || nelem <= PY_SSIZE_T_MAX / elsize);
nbytes = nelem * elsize;
- return _PyMem_DebugAlloc(1, ctx, nbytes);
+ return _PyMem_DebugRawAlloc(1, ctx, nbytes);
}
/* The debug free first checks the 2*SST bytes on each end for sanity (in
@@ -1876,10 +1894,10 @@ _PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize)
Then calls the underlying free.
*/
static void
-_PyMem_DebugFree(void *ctx, void *p)
+_PyMem_DebugRawFree(void *ctx, void *p)
{
debug_alloc_api_t *api = (debug_alloc_api_t *)ctx;
- uchar *q = (uchar *)p - 2*SST; /* address returned from malloc */
+ uint8_t *q = (uint8_t *)p - 2*SST; /* address returned from malloc */
size_t nbytes;
if (p == NULL)
@@ -1893,17 +1911,17 @@ _PyMem_DebugFree(void *ctx, void *p)
}
static void *
-_PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes)
+_PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes)
{
debug_alloc_api_t *api = (debug_alloc_api_t *)ctx;
- uchar *q = (uchar *)p, *oldq;
- uchar *tail;
+ uint8_t *q = (uint8_t *)p, *oldq;
+ uint8_t *tail;
size_t total; /* nbytes + 4*SST */
size_t original_nbytes;
int i;
if (p == NULL)
- return _PyMem_DebugAlloc(0, ctx, nbytes);
+ return _PyMem_DebugRawAlloc(0, ctx, nbytes);
_PyMem_DebugCheckAddress(api->api_id, p);
bumpserialno();
@@ -1918,7 +1936,7 @@ _PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes)
* but we live with that.
*/
oldq = q;
- q = (uchar *)api->alloc.realloc(api->alloc.ctx, q - 2*SST, total);
+ q = (uint8_t *)api->alloc.realloc(api->alloc.ctx, q - 2*SST, total);
if (q == NULL)
return NULL;
@@ -1928,7 +1946,7 @@ _PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes)
}
write_size_t(q, nbytes);
- assert(q[SST] == (uchar)api->api_id);
+ assert(q[SST] == (uint8_t)api->api_id);
for (i = 1; i < SST; ++i)
assert(q[SST + i] == FORBIDDENBYTE);
q += 2*SST;
@@ -1946,6 +1964,44 @@ _PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes)
return q;
}
+static void
+_PyMem_DebugCheckGIL(void)
+{
+#ifdef WITH_THREAD
+ if (!PyGILState_Check())
+ Py_FatalError("Python memory allocator called "
+ "without holding the GIL");
+#endif
+}
+
+static void *
+_PyMem_DebugMalloc(void *ctx, size_t nbytes)
+{
+ _PyMem_DebugCheckGIL();
+ return _PyMem_DebugRawMalloc(ctx, nbytes);
+}
+
+static void *
+_PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize)
+{
+ _PyMem_DebugCheckGIL();
+ return _PyMem_DebugRawCalloc(ctx, nelem, elsize);
+}
+
+static void
+_PyMem_DebugFree(void *ctx, void *ptr)
+{
+ _PyMem_DebugCheckGIL();
+ _PyMem_DebugRawFree(ctx, ptr);
+}
+
+static void *
+_PyMem_DebugRealloc(void *ctx, void *ptr, size_t nbytes)
+{
+ _PyMem_DebugCheckGIL();
+ return _PyMem_DebugRawRealloc(ctx, ptr, nbytes);
+}
+
/* Check the forbidden bytes on both ends of the memory allocated for p.
* If anything is wrong, print info to stderr via _PyObject_DebugDumpAddress,
* and call Py_FatalError to kill the program.
@@ -1954,11 +2010,11 @@ _PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes)
static void
_PyMem_DebugCheckAddress(char api, const void *p)
{
- const uchar *q = (const uchar *)p;
+ const uint8_t *q = (const uint8_t *)p;
char msgbuf[64];
char *msg;
size_t nbytes;
- const uchar *tail;
+ const uint8_t *tail;
int i;
char id;
@@ -2007,8 +2063,8 @@ error:
static void
_PyObject_DebugDumpAddress(const void *p)
{
- const uchar *q = (const uchar *)p;
- const uchar *tail;
+ const uint8_t *q = (const uint8_t *)p;
+ const uint8_t *tail;
size_t nbytes, serial;
int i;
int ok;
@@ -2041,7 +2097,7 @@ _PyObject_DebugDumpAddress(const void *p)
fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n",
FORBIDDENBYTE);
for (i = SST-1; i >= 1; --i) {
- const uchar byte = *(q-i);
+ const uint8_t byte = *(q-i);
fprintf(stderr, " at p-%d: 0x%02x", i, byte);
if (byte != FORBIDDENBYTE)
fputs(" *** OUCH", stderr);
@@ -2069,7 +2125,7 @@ _PyObject_DebugDumpAddress(const void *p)
fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n",
FORBIDDENBYTE);
for (i = 0; i < SST; ++i) {
- const uchar byte = tail[i];
+ const uint8_t byte = tail[i];
fprintf(stderr, " at tail+%d: 0x%02x",
i, byte);
if (byte != FORBIDDENBYTE)
@@ -2104,9 +2160,12 @@ _PyObject_DebugDumpAddress(const void *p)
}
fputc('\n', stderr);
}
+ fputc('\n', stderr);
+
+ fflush(stderr);
+ _PyMem_DumpTraceback(fileno(stderr), p);
}
-#endif /* PYMALLOC_DEBUG */
static size_t
printone(FILE *out, const char* msg, size_t value)
@@ -2158,8 +2217,30 @@ _PyDebugAllocatorStats(FILE *out,
(void)printone(out, buf2, num_blocks * sizeof_block);
}
+
#ifdef WITH_PYMALLOC
+#ifdef Py_DEBUG
+/* Is target in the list? The list is traversed via the nextpool pointers.
+ * The list may be NULL-terminated, or circular. Return 1 if target is in
+ * list, else 0.
+ */
+static int
+pool_is_in_list(const poolp target, poolp list)
+{
+ poolp origlist = list;
+ assert(target != NULL);
+ if (list == NULL)
+ return 0;
+ do {
+ if (target == list)
+ return 1;
+ list = list->nextpool;
+ } while (list != NULL && list != origlist);
+ return 0;
+}
+#endif
+
/* Print summary info to "out" about the state of pymalloc's structures.
* In Py_DEBUG mode, also perform some expensive internal consistency
* checks.
@@ -2206,34 +2287,35 @@ _PyObject_DebugMallocStats(FILE *out)
*/
for (i = 0; i < maxarenas; ++i) {
uint j;
- uptr base = arenas[i].address;
+ uintptr_t base = arenas[i].address;
/* Skip arenas which are not allocated. */
- if (arenas[i].address == (uptr)NULL)
+ if (arenas[i].address == (uintptr_t)NULL)
continue;
narenas += 1;
numfreepools += arenas[i].nfreepools;
/* round up to pool alignment */
- if (base & (uptr)POOL_SIZE_MASK) {
+ if (base & (uintptr_t)POOL_SIZE_MASK) {
arena_alignment += POOL_SIZE;
- base &= ~(uptr)POOL_SIZE_MASK;
+ base &= ~(uintptr_t)POOL_SIZE_MASK;
base += POOL_SIZE;
}
/* visit every pool in the arena */
- assert(base <= (uptr) arenas[i].pool_address);
- for (j = 0;
- base < (uptr) arenas[i].pool_address;
- ++j, base += POOL_SIZE) {
+ assert(base <= (uintptr_t) arenas[i].pool_address);
+ for (j = 0; base < (uintptr_t) arenas[i].pool_address;
+ ++j, base += POOL_SIZE) {
poolp p = (poolp)base;
const uint sz = p->szidx;
uint freeblocks;
if (p->ref.count == 0) {
/* currently unused */
+#ifdef Py_DEBUG
assert(pool_is_in_list(p, arenas[i].freepools));
+#endif
continue;
}
++numpools[sz];
@@ -2273,9 +2355,8 @@ _PyObject_DebugMallocStats(FILE *out)
quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size);
}
fputc('\n', out);
-#ifdef PYMALLOC_DEBUG
- (void)printone(out, "# times object malloc called", serialno);
-#endif
+ if (_PyMem_DebugEnabled())
+ (void)printone(out, "# times object malloc called", serialno);
(void)printone(out, "# arenas allocated total", ntimes_arena_allocated);
(void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas);
(void)printone(out, "# arenas highwater mark", narenas_highwater);
@@ -2302,18 +2383,3 @@ _PyObject_DebugMallocStats(FILE *out)
}
#endif /* #ifdef WITH_PYMALLOC */
-
-#ifdef Py_USING_MEMORY_DEBUGGER
-/* Make this function last so gcc won't inline it since the definition is
- * after the reference.
- */
-int
-Py_ADDRESS_IN_RANGE(void *P, poolp pool)
-{
- uint arenaindex_temp = pool->arenaindex;
-
- return arenaindex_temp < maxarenas &&
- (uptr)P - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE &&
- arenas[arenaindex_temp].address != 0;
-}
-#endif