changeset: 84085:856dbffccdd6 tag: tip user: Victor Stinner date: Mon Jun 10 22:11:19 2013 +0200 files: Include/objimpl.h Include/pymem.h Modules/gcmodule.c Objects/object.c Objects/obmalloc.c description: Issue #3329: Add an API to set memory allocators diff -r 668aba845fb2 -r 856dbffccdd6 Include/objimpl.h --- a/Include/objimpl.h Mon Jun 10 09:24:01 2013 -0700 +++ b/Include/objimpl.h Mon Jun 10 22:11:19 2013 +0200 @@ -98,6 +98,19 @@ PyAPI_FUNC(void *) PyObject_Malloc(size_ PyAPI_FUNC(void *) PyObject_Realloc(void *, size_t); PyAPI_FUNC(void) PyObject_Free(void *); +#ifdef PYMEM_TRACE_MALLOC +PyAPI_FUNC(void *) _PyObject_MallocTrace(size_t, const char *, int); +PyAPI_FUNC(void *) _PyObject_ReallocTrace(void *, size_t, const char *, int); +PyAPI_FUNC(void) _PyObject_FreeTrace(void *, const char *, int); + +#define PyObject_Malloc(size) \ + _PyObject_MallocTrace(size, __FILE__, __LINE__) +#define PyObject_Realloc(ptr, size) \ + _PyObject_ReallocTrace(ptr, size, __FILE__, __LINE__) +#define PyObject_Free(ptr) \ + _PyObject_FreeTrace(ptr, __FILE__, __LINE__) +#endif + /* This function returns the number of allocated memory blocks, regardless of size */ PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void); @@ -106,41 +119,14 @@ PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedB #ifndef Py_LIMITED_API PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out); #endif /* #ifndef Py_LIMITED_API */ -#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ -PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); -PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); -PyAPI_FUNC(void) _PyObject_DebugFree(void *p); -PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p); -PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p); -PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes); -PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes); -PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p); -PyAPI_FUNC(void) _PyObject_DebugCheckAddressApi(char api, const void *p); -PyAPI_FUNC(void *) _PyMem_DebugMalloc(size_t nbytes); -PyAPI_FUNC(void *) _PyMem_DebugRealloc(void *p, size_t nbytes); -PyAPI_FUNC(void) _PyMem_DebugFree(void *p); -#define PyObject_MALLOC _PyObject_DebugMalloc -#define PyObject_Malloc _PyObject_DebugMalloc -#define PyObject_REALLOC _PyObject_DebugRealloc -#define PyObject_Realloc _PyObject_DebugRealloc -#define PyObject_FREE _PyObject_DebugFree -#define PyObject_Free _PyObject_DebugFree +#endif -#else /* WITH_PYMALLOC && ! PYMALLOC_DEBUG */ +/* Macros */ #define PyObject_MALLOC PyObject_Malloc #define PyObject_REALLOC PyObject_Realloc #define PyObject_FREE PyObject_Free -#endif - -#else /* ! WITH_PYMALLOC */ -#define PyObject_MALLOC PyMem_MALLOC -#define PyObject_REALLOC PyMem_REALLOC -#define PyObject_FREE PyMem_FREE - -#endif /* WITH_PYMALLOC */ - #define PyObject_Del PyObject_Free -#define PyObject_DEL PyObject_FREE +#define PyObject_DEL PyObject_Free /* * Generic object allocator interface @@ -310,6 +296,22 @@ PyAPI_FUNC(void) PyObject_GC_Track(void PyAPI_FUNC(void) PyObject_GC_UnTrack(void *); PyAPI_FUNC(void) PyObject_GC_Del(void *); +#ifdef PYMEM_TRACE_MALLOC +PyAPI_FUNC(PyObject *) _PyObject_GC_MallocTrace(size_t, const char *, int); +PyAPI_FUNC(PyObject *) _PyObject_GC_NewTrace(PyTypeObject *, const char *, int); +PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVarTrace(PyTypeObject *, Py_ssize_t, const char *, int); +PyAPI_FUNC(void) _PyObject_GC_DelTrace(void *, const char *, int); + +#define _PyObject_GC_Malloc(size) \ + _PyObject_GC_MallocTrace(size, __FILE__, __LINE__) +#define _PyObject_GC_New(tp) \ + _PyObject_GC_NewTrace(tp, __FILE__, __LINE__) +#define _PyObject_GC_NewVar(tp, nitems) \ + _PyObject_GC_NewVarTrace(tp, nitems, __FILE__, __LINE__) +#define PyObject_GC_Del(op) \ + _PyObject_GC_DelTrace(op, __FILE__, __LINE__) +#endif + #define PyObject_GC_New(type, typeobj) \ ( (type *) _PyObject_GC_New(typeobj) ) #define PyObject_GC_NewVar(type, typeobj, n) \ diff -r 668aba845fb2 -r 856dbffccdd6 Include/pymem.h --- a/Include/pymem.h Mon Jun 10 09:24:01 2013 -0700 +++ b/Include/pymem.h Mon Jun 10 22:11:19 2013 +0200 @@ -49,21 +49,65 @@ extern "C" { performed on failure (no exception is set, no warning is printed, etc). */ +typedef struct { + void *data; + /* NULL and -1 when unknown */ + const char *filename; + int lineno; +} _PyMem_Trace; + +#define PYMEM_MEM_API 'm' /* PyMem_Malloc() API */ +#define PYMEM_OBJECT_API 'o' /* PyObject_Malloc() API */ + +PyAPI_FUNC(int) Py_GetAllocators( + char api, + void* (**malloc_p) (size_t size, void *user_data), + void* (**realloc_p) (void *ptr, size_t size, void *user_data), + void (**free_p) (void *ptr, void *user_data), + void **user_data_p + ); + +PyAPI_FUNC(int) Py_SetAllocators( + char api, + void* (*malloc) (size_t size, void *user_data), + void* (*realloc) (void *ptr, size_t size, void *user_data), + void (*free) (void *ptr, void *user_data), + void *user_data + ); + +PyAPI_FUNC(void) Py_GetBlockAllocators( + void* (**malloc_p) (size_t size, void *user_data), + void (**free_p) (void *ptr, size_t size, void *user_data), + void **user_data_p + ); + +PyAPI_FUNC(int) Py_SetBlockAllocators( + void* (*malloc) (size_t size, void *user_data), + void (*free) (void *ptr, size_t size, void *user_data), + void *user_data + ); + PyAPI_FUNC(void *) PyMem_Malloc(size_t); PyAPI_FUNC(void *) PyMem_Realloc(void *, size_t); PyAPI_FUNC(void) PyMem_Free(void *); +#ifdef PYMEM_TRACE_MALLOC +PyAPI_FUNC(void *) _PyMem_MallocTrace(size_t, const char *, int); +PyAPI_FUNC(void *) _PyMem_ReallocTrace(void *, size_t, const char *, int); +PyAPI_FUNC(void) _PyMem_FreeTrace(void *, const char *, int); + +#define PyMem_Malloc(size) \ + _PyMem_MallocTrace(size, __FILE__, __LINE__) +#define PyMem_Realloc(ptr, size) \ + _PyMem_ReallocTrace(ptr, size, __FILE__, __LINE__) +#define PyMem_Free(ptr) \ + _PyMem_FreeTrace(ptr, __FILE__, __LINE__) +#endif + /* Starting from Python 1.6, the wrappers Py_{Malloc,Realloc,Free} are no longer supported. They used to call PyErr_NoMemory() on failure. */ /* Macros. */ -#ifdef PYMALLOC_DEBUG -/* Redirect all memory operations to Python's debugging allocator. */ -#define PyMem_MALLOC _PyMem_DebugMalloc -#define PyMem_REALLOC _PyMem_DebugRealloc -#define PyMem_FREE _PyMem_DebugFree - -#else /* ! PYMALLOC_DEBUG */ /* PyMem_MALLOC(0) means malloc(1). Some systems would return NULL for malloc(0), which would be treated as an error. Some platforms @@ -71,13 +115,9 @@ PyAPI_FUNC(void) PyMem_Free(void *); pymalloc. To solve these problems, allocate an extra byte. */ /* Returns NULL to indicate error if a negative size or size larger than Py_ssize_t can represent is supplied. Helps prevents security holes. */ -#define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ - : malloc((n) ? (n) : 1)) -#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ - : realloc((p), (n) ? (n) : 1)) -#define PyMem_FREE free - -#endif /* PYMALLOC_DEBUG */ +#define PyMem_MALLOC(n) PyMem_Malloc(n) +#define PyMem_REALLOC(p, n) PyMem_Realloc(p, n) +#define PyMem_FREE(p) PyMem_Free(p) /* * Type-oriented memory interface diff -r 668aba845fb2 -r 856dbffccdd6 Modules/gcmodule.c --- a/Modules/gcmodule.c Mon Jun 10 09:24:01 2013 -0700 +++ b/Modules/gcmodule.c Mon Jun 10 22:11:19 2013 +0200 @@ -1652,14 +1652,23 @@ void } PyObject * +#ifdef PYMEM_TRACE_MALLOC +_PyObject_GC_MallocTrace(size_t basicsize, const char* filename, int lineno) +#else _PyObject_GC_Malloc(size_t basicsize) +#endif { PyObject *op; PyGC_Head *g; if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) return PyErr_NoMemory(); +#ifdef PYMEM_TRACE_MALLOC + g = (PyGC_Head *)_PyObject_MallocTrace( + sizeof(PyGC_Head) + basicsize, filename, lineno); +#else g = (PyGC_Head *)PyObject_MALLOC( sizeof(PyGC_Head) + basicsize); +#endif if (g == NULL) return PyErr_NoMemory(); g->gc.gc_refs = GC_UNTRACKED; @@ -1678,32 +1687,57 @@ PyObject * } PyObject * +#ifdef PYMEM_TRACE_MALLOC +_PyObject_GC_NewTrace(PyTypeObject *tp, const char* filename, int lineno) +#else _PyObject_GC_New(PyTypeObject *tp) +#endif { +#ifdef PYMEM_TRACE_MALLOC + PyObject *op = _PyObject_GC_MallocTrace(_PyObject_SIZE(tp), filename, lineno); +#else PyObject *op = _PyObject_GC_Malloc(_PyObject_SIZE(tp)); +#endif if (op != NULL) op = PyObject_INIT(op, tp); return op; } PyVarObject * +#ifdef PYMEM_TRACE_MALLOC +_PyObject_GC_NewVarTrace(PyTypeObject *tp, Py_ssize_t nitems, const char* filename, int lineno) +#else _PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems) +#endif { const size_t size = _PyObject_VAR_SIZE(tp, nitems); +#ifdef PYMEM_TRACE_MALLOC + PyVarObject *op = (PyVarObject *) _PyObject_GC_MallocTrace(size, filename, lineno); +#else PyVarObject *op = (PyVarObject *) _PyObject_GC_Malloc(size); +#endif if (op != NULL) op = PyObject_INIT_VAR(op, tp, nitems); return op; } PyVarObject * +#ifdef PYMEM_TRACE_MALLOC +_PyObject_GC_ResizeTrace(PyVarObject *op, Py_ssize_t nitems, const char* filename, int lineno) +#else _PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems) +#endif { const size_t basicsize = _PyObject_VAR_SIZE(Py_TYPE(op), nitems); PyGC_Head *g = AS_GC(op); if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) return (PyVarObject *)PyErr_NoMemory(); +#ifdef PYMEM_TRACE_MALLOC + g = (PyGC_Head *)_PyObject_ReallocTrace(g, sizeof(PyGC_Head) + basicsize, + filename, lineno); +#else g = (PyGC_Head *)PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize); +#endif if (g == NULL) return (PyVarObject *)PyErr_NoMemory(); op = (PyVarObject *) FROM_GC(g); @@ -1712,7 +1746,11 @@ PyVarObject * } void +#ifdef PYMEM_TRACE_MALLOC +_PyObject_GC_DelTrace(void *op, const char *filename, int lineno) +#else PyObject_GC_Del(void *op) +#endif { PyGC_Head *g = AS_GC(op); if (IS_TRACKED(op)) @@ -1720,5 +1758,47 @@ PyObject_GC_Del(void *op) if (generations[0].count > 0) { generations[0].count--; } +#ifdef PYMEM_TRACE_MALLOC + _PyObject_FreeTrace(g, filename, lineno); +#else PyObject_FREE(g); +#endif } + +#ifdef PYMEM_TRACE_MALLOC +#undef _PyObject_GC_Malloc +#undef _PyObject_GC_New +#undef _PyObject_GC_NewVar +#undef PyObject_GC_Del + +PyObject * +_PyObject_GC_Malloc(size_t basicsize) +{ + return _PyObject_GC_MallocTrace(basicsize, NULL, -1); +} + +PyObject * +_PyObject_GC_New(PyTypeObject *tp) +{ + return _PyObject_GC_NewTrace(tp, NULL, -1); +} + +PyVarObject * +_PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems) +{ + return _PyObject_GC_NewVarTrace(tp, nitems, NULL, -1); +} + +PyVarObject * +_PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems) +{ + return _PyObject_GC_ResizeTrace(op, nitems, NULL, -1); +} + +void +PyObject_GC_Del(void *op) +{ + return _PyObject_GC_DelTrace(op, NULL, -1); +} +#endif + diff -r 668aba845fb2 -r 856dbffccdd6 Objects/object.c --- a/Objects/object.c Mon Jun 10 09:24:01 2013 -0700 +++ b/Objects/object.c Mon Jun 10 22:11:19 2013 +0200 @@ -1859,26 +1859,6 @@ PyTypeObject *_PyCapsule_hack = &PyCapsu Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size; -/* Python's malloc wrappers (see pymem.h) */ - -void * -PyMem_Malloc(size_t nbytes) -{ - return PyMem_MALLOC(nbytes); -} - -void * -PyMem_Realloc(void *p, size_t nbytes) -{ - return PyMem_REALLOC(p, nbytes); -} - -void -PyMem_Free(void *p) -{ - PyMem_FREE(p); -} - void _PyObject_DebugTypeStats(FILE *out) { diff -r 668aba845fb2 -r 856dbffccdd6 Objects/obmalloc.c --- a/Objects/obmalloc.c Mon Jun 10 09:24:01 2013 -0700 +++ b/Objects/obmalloc.c Mon Jun 10 22:11:19 2013 +0200 @@ -1,5 +1,430 @@ #include "Python.h" +/* Forward declaration */ + +#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ +static void* _PyMem_DebugMalloc(size_t nbytes, void *user_data); +static void _PyMem_DebugFree(void *p, void *user_data); +static void* _PyMem_DebugRealloc(void *p, size_t nbytes, void *user_data); + +static void _PyObject_DebugDumpAddress(const void *p); +static void _PyMem_DebugCheckAddress(const void *p); +#endif + +#ifdef WITH_PYMALLOC +static void* _PyObject_Malloc(size_t nbytes, void *user_data); +static void _PyObject_Free(void *p, void *user_data); +static void* _PyObject_Realloc(void *p, size_t nbytes, void *user_data); +#endif + +/* Python's malloc wrappers (see pymem.h) */ + +static struct { + int init; + + void* (*mem_malloc) (size_t, void*); + void* (*mem_realloc) (void*, size_t, void*); + void (*mem_free) (void*, void*); + void *mem_user_data; + + void* (*object_malloc) (size_t, void*); + void* (*object_realloc) (void*, size_t, void*); + void (*object_free) (void*, void*); + void *object_user_data; + + void* (*block_malloc) (size_t, void*); + void (*block_free) (void*, size_t, void*); + void *block_user_data; +} _PyMem = { + 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL}; + +#ifndef PYMALLOC_DEBUG +static void * +_PyMem_Malloc(size_t size, void *user_data) +{ + return malloc(size); +} + +static void * +_PyMem_Realloc(void *ptr, size_t size, void *user_data) +{ + return realloc(ptr, size); +} + +static void +_PyMem_Free(void *ptr, void *user_data) +{ + return free(ptr); +} +#endif + +#ifdef ARENAS_USE_MMAP +static void * +_PyMem_BlockMmap(size_t size, void *user_data) +{ + void *ptr; + ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) + return NULL; + assert(ptr != NULL); + return ptr; +} + +static void +_PyMem_BlockMunmap(void *ptr, size_t size, void *user_data) +{ + return munmap(ptr, size); +} +#else +static void * +_PyMem_BlockMalloc(size_t size, void *user_data) +{ + return malloc(size); +} + +static void +_PyMem_BlockFree(void *ptr, size_t size, void *user_data) +{ + free(ptr); +} +#endif + +static void +_PyMem_InitDefaultAllocators(void) +{ + _PyMem.init = 1; + +#ifdef PYMALLOC_DEBUG + _PyMem.mem_malloc = _PyMem_DebugMalloc; + _PyMem.mem_realloc = _PyMem_DebugRealloc; + _PyMem.mem_free = _PyMem_DebugFree; +#else + _PyMem.mem_malloc = _PyMem_Malloc; + _PyMem.mem_realloc = _PyMem_Realloc; + _PyMem.mem_free = _PyMem_Free; +#endif + _PyMem.mem_user_data = NULL; + +#ifndef WITH_PYMALLOC + _PyMem.object_malloc = _PyMem_Malloc; + _PyMem.object_free = _PyMem_Free; + _PyMem.object_realloc = _PyMem_Realloc; +#else + _PyMem.object_malloc = _PyObject_Malloc; + _PyMem.object_free = _PyObject_Free; + _PyMem.object_realloc = _PyObject_Realloc; +#endif + _PyMem.object_user_data = NULL; + +#ifdef ARENAS_USE_MMAP + _PyMem.block_malloc = _PyMem_BlockMmap; + _PyMem.block_free = _PyMem_BlockMunmap; +#else + _PyMem.block_malloc = _PyMem_BlockMalloc; + _PyMem.block_free = _PyMem_BlockFree; +#endif + _PyMem.block_user_data = NULL; +} + +#define _PyMem_InitAllocators() \ + do { \ + if (!_PyMem.init) \ + _PyMem_InitDefaultAllocators(); \ + } while (0) + +int +Py_GetAllocators( + char api, + void* (**malloc_p) (size_t size, void *user_data), + void* (**realloc_p) (void *ptr, size_t size, void *user_data), + void (**free_p) (void *ptr, void *user_data), + void **user_data_p) +{ + _PyMem_InitAllocators(); + + if (api == PYMEM_MEM_API) { + *malloc_p = _PyMem.mem_malloc; + *realloc_p = _PyMem.mem_realloc; + *free_p = _PyMem.mem_free; + *user_data_p = _PyMem.mem_user_data; + } + else if (api == PYMEM_OBJECT_API) { + *malloc_p = _PyMem.object_malloc; + *realloc_p = _PyMem.object_realloc; + *free_p = _PyMem.object_free; + *user_data_p = _PyMem.object_user_data; + } + else { + *malloc_p = NULL; + *realloc_p = NULL; + *free_p = NULL; + return -1; + } + return 0; +} + +int Py_SetAllocators( + char api, + void* (*user_malloc) (size_t size, void *user_data), + void* (*user_realloc) (void *ptr, size_t size, void *user_data), + void (*user_free) (void *ptr, void *user_data), + void *user_data) +{ + _PyMem_InitAllocators(); + + if (api == PYMEM_MEM_API) { + _PyMem.mem_malloc = user_malloc; + _PyMem.mem_realloc = user_realloc; + _PyMem.mem_free = user_free; + _PyMem.mem_user_data = user_data; + } + else if (api == PYMEM_OBJECT_API) { + _PyMem.object_malloc = user_malloc; + _PyMem.object_realloc = user_realloc; + _PyMem.object_free = user_free; + _PyMem.object_user_data = user_data; + } + else { + return -1; + } + return 0; +} + +void +Py_GetBlockAllocators( + void* (**malloc_p) (size_t size, void *user_data), + void (**free_p) (void *ptr, size_t size, void *user_data), + void **user_data_p) +{ + _PyMem_InitAllocators(); + + *malloc_p = _PyMem.block_malloc; + *free_p = _PyMem.block_free; + *user_data_p = _PyMem.block_user_data; +} + +int Py_SetBlockAllocators( + void* (*user_malloc) (size_t size, void *user_data), + void (*user_free) (void *ptr, size_t size, void *user_data), + void *user_data) +{ + _PyMem_InitAllocators(); + + _PyMem.block_malloc = user_malloc; + _PyMem.block_free = user_free; + _PyMem.block_user_data = user_data; + return 0; +} + +#ifdef PYMEM_TRACE_MALLOC + +void* +_PyMem_MallocTrace(size_t size, const char* filename, int lineno) +{ + _PyMem_Trace trace; + _PyMem_InitAllocators(); + + if (nbytes > (size_t)PY_SSIZE_T_MAX) + return NULL; + if (nbytes == 0) + nbytes = 1; + + trace.data = _PyMem.mem_user_data; + trace.filename = filename; + trace.lineno = lineno; + return _PyMem.mem_malloc(size, &trace); +} + +void* +_PyMem_ReallocTrace(void *ptr, size_t size, const char* filename, int lineno) +{ + _PyMem_Trace trace; + _PyMem_InitAllocators(); + + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + if (size == 0) + size = 1; + + trace.data = _PyMem.mem_user_data; + trace.filename = filename; + trace.lineno = lineno; + return _PyMem.mem_realloc(ptr, size, &trace); +} + +void +_PyMem_FreeTrace(void *ptr, const char* filename, int lineno) +{ + _PyMem_Trace trace; + _PyMem_InitAllocators(); + trace.data = _PyMem.mem_user_data; + trace.filename = filename; + trace.lineno = lineno; + return _PyMem.mem_free(ptr, &trace); +} + +void* +_PyObject_MallocTrace(size_t size, const char *filename, int lineno) +{ + _PyMem_Trace trace; + _PyMem_InitAllocators(); + if (size > PY_SSIZE_T_MAX) + return NULL; + trace.data = _PyMem.object_user_data; + trace.filename = filename; + trace.lineno = lineno; + return _PyMem.object_malloc(size, &trace); +} + +void* +_PyObject_ReallocTrace(void *ptr, size_t size, const char *filename, int lineno) +{ + _PyMem_Trace trace; + _PyMem_InitAllocators(); + if (size > PY_SSIZE_T_MAX) + return NULL; + trace.data = _PyMem.object_user_data; + trace.filename = filename; + trace.lineno = lineno; + return _PyMem.object_realloc(ptr, size, &trace); +} + +void +_PyObject_FreeTrace(void *ptr, const char *filename, int lineno) +{ + _PyMem_Trace trace; + _PyMem_InitAllocators(); + trace.data = _PyMem.object_user_data; + trace.filename = filename; + trace.lineno = lineno; + _PyMem.object_free(ptr, &trace); +} + +#undef PyMem_Malloc +#undef PyMem_Realloc +#undef PyMem_Free +#undef PyObject_Malloc +#undef PyObject_Realloc +#undef PyObject_Free + +void* +PyMem_Malloc(size_t size) +{ + return _PyMem_MallocTrace(size, NULL, -1); +} + +void* +PyMem_Realloc(void *ptr, size_t size) +{ + return _PyMem_ReallocTrace(ptr, size, NULL, -1); +} + +void +PyMem_Free(void *ptr) +{ + return _PyMem_FreeTrace(ptr, NULL, -1); +} + +void* +PyObject_Malloc(size_t size) +{ + return _PyObject_MallocTrace(size, NULL, -1); +} + +void* +PyObject_Realloc(void *ptr, size_t size) +{ + return _PyObject_ReallocTrace(ptr, size, NULL, -1); +} + +void +PyObject_Free(void *ptr) +{ + return _PyObject_FreeTrace(ptr, NULL, -1); +} + +#else + +void * +PyMem_Malloc(size_t nbytes) +{ + _PyMem_InitAllocators(); + + if (nbytes > (size_t)PY_SSIZE_T_MAX) + return NULL; + if (nbytes == 0) + nbytes = 1; + + return _PyMem.mem_malloc(nbytes, _PyMem.mem_user_data); +} + +void * +PyMem_Realloc(void *ptr, size_t size) +{ + _PyMem_InitAllocators(); + + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + if (size == 0) + size = 1; + + return _PyMem.mem_realloc(ptr, size, _PyMem.mem_user_data); +} + +void +PyMem_Free(void *ptr) +{ + _PyMem_InitAllocators(); + _PyMem.mem_free(ptr, _PyMem.mem_user_data); +} + +void * +PyObject_Malloc(size_t size) +{ + _PyMem_InitAllocators(); + + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for size < 0 is not required. + */ + if (size > PY_SSIZE_T_MAX) + return NULL; + + return _PyMem.object_malloc(size, _PyMem.object_user_data); +} + +void * +PyObject_Realloc(void *ptr, size_t size) +{ + _PyMem_InitAllocators(); + + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for size < 0 is not required. + */ + if (size > PY_SSIZE_T_MAX) + return NULL; + + return _PyMem.object_realloc(ptr, size, _PyMem.object_user_data); +} + +void +PyObject_Free(void *ptr) +{ + _PyMem_InitAllocators(); + _PyMem.object_free(ptr, _PyMem.object_user_data); +} + +#endif /* !PYMEM_TRACE_MALLOC */ + #ifdef WITH_PYMALLOC #ifdef HAVE_MMAP @@ -540,12 +965,11 @@ Py_ssize_t * `usable_arenas` to the return value. */ static struct arena_object* -new_arena(void) +new_arena(void *user_data) { struct arena_object* arenaobj; uint excess; /* number of bytes above pool alignment */ void *address; - int err; #ifdef PYMALLOC_DEBUG if (Py_GETENV("PYTHONMALLOCSTATS")) @@ -555,6 +979,9 @@ new_arena(void) uint i; uint numarenas; size_t nbytes; +#ifdef PYMEM_TRACE_MALLOC + _PyMem_Trace *trace; +#endif /* Double the number of arena objects on each allocation. * Note that it's possible for `numarenas` to overflow. @@ -567,7 +994,14 @@ new_arena(void) return NULL; /* overflow */ #endif nbytes = numarenas * sizeof(*arenas); - arenaobj = (struct arena_object *)realloc(arenas, nbytes); +#ifdef PYMEM_TRACE_MALLOC + trace = (_PyMem_Trace *)user_data; + arenaobj = (struct arena_object *)_PyMem_ReallocTrace(arenas, nbytes, + trace->filename, + trace->lineno); +#else + arenaobj = (struct arena_object *)PyMem_Realloc(arenas, nbytes); +#endif if (arenaobj == NULL) return NULL; arenas = arenaobj; @@ -598,15 +1032,9 @@ new_arena(void) arenaobj = unused_arena_objects; unused_arena_objects = arenaobj->nextarena; assert(arenaobj->address == 0); -#ifdef ARENAS_USE_MMAP - address = mmap(NULL, ARENA_SIZE, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - err = (address == MAP_FAILED); -#else - address = malloc(ARENA_SIZE); - err = (address == 0); -#endif - if (err) { + + address = _PyMem.block_malloc(ARENA_SIZE, _PyMem.block_user_data); + if (address == NULL) { /* The allocation failed: return NULL after putting the * arenaobj back. */ @@ -769,9 +1197,8 @@ int Py_ADDRESS_IN_RANGE(void *P, poolp p * Unless the optimizer reorders everything, being too smart... */ -#undef PyObject_Malloc -void * -PyObject_Malloc(size_t nbytes) +static void * +_PyObject_Malloc(size_t nbytes, void *user_data) { block *bp; poolp pool; @@ -788,17 +1215,6 @@ PyObject_Malloc(size_t nbytes) #endif /* - * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. - * Most python internals blindly use a signed Py_ssize_t to track - * things without checking for overflows or negatives. - * As size_t is unsigned, checking for nbytes < 0 is not required. - */ - if (nbytes > PY_SSIZE_T_MAX) { - _Py_AllocatedBlocks--; - return NULL; - } - - /* * This implicitly redirects malloc(0). */ if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { @@ -852,7 +1268,7 @@ PyObject_Malloc(size_t nbytes) goto redirect; } #endif - usable_arenas = new_arena(); + usable_arenas = new_arena(user_data); if (usable_arenas == NULL) { UNLOCK(); goto redirect; @@ -973,7 +1389,13 @@ redirect: if (nbytes == 0) nbytes = 1; { - void *result = malloc(nbytes); + void *result; +#ifdef PYMEM_TRACE_MALLOC + _PyMem_Trace *trace = (_PyMem_Trace *)user_data; + result = _PyMem_MallocTrace(nbytes, trace->filename, trace->lineno); +#else + result = PyMem_Malloc(nbytes); +#endif if (!result) _Py_AllocatedBlocks--; return result; @@ -982,9 +1404,8 @@ redirect: /* free */ -#undef PyObject_Free -void -PyObject_Free(void *p) +static void +_PyObject_Free(void *p, void *user_data) { poolp pool; block *lastfree; @@ -993,6 +1414,9 @@ PyObject_Free(void *p) #ifndef Py_USING_MEMORY_DEBUGGER uint arenaindex_temp; #endif +#ifdef PYMEM_TRACE_MALLOC + _PyMem_Trace *trace; +#endif if (p == NULL) /* free(NULL) has no effect */ return; @@ -1093,11 +1517,8 @@ PyObject_Free(void *p) unused_arena_objects = ao; /* Free the entire arena. */ -#ifdef ARENAS_USE_MMAP - munmap((void *)ao->address, ARENA_SIZE); -#else - free((void *)ao->address); -#endif + _PyMem.block_free((void *)ao->address, + ARENA_SIZE, _PyMem.block_user_data); ao->address = 0; /* mark unassociated */ --narenas_currently_allocated; @@ -1206,7 +1627,12 @@ PyObject_Free(void *p) redirect: #endif /* We didn't allocate this address. */ - free(p); +#ifdef PYMEM_TRACE_MALLOC + trace = (_PyMem_Trace *)user_data; + _PyMem_FreeTrace(p, trace->filename, trace->lineno); +#else + PyMem_Free(p); +#endif } /* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0, @@ -1214,9 +1640,8 @@ redirect: * return a non-NULL result. */ -#undef PyObject_Realloc -void * -PyObject_Realloc(void *p, size_t nbytes) +static void * +_PyObject_Realloc(void *p, size_t nbytes, void *user_data) { void *bp; poolp pool; @@ -1224,18 +1649,12 @@ PyObject_Realloc(void *p, size_t nbytes) #ifndef Py_USING_MEMORY_DEBUGGER uint arenaindex_temp; #endif +#ifdef PYMEM_TRACE_MALLOC + _PyMem_Trace *trace; +#endif if (p == NULL) - return PyObject_Malloc(nbytes); - - /* - * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. - * Most python internals blindly use a signed Py_ssize_t to track - * things without checking for overflows or negatives. - * As size_t is unsigned, checking for nbytes < 0 is not required. - */ - if (nbytes > PY_SSIZE_T_MAX) - return NULL; + return _PyObject_Malloc(nbytes, user_data); #ifdef WITH_VALGRIND /* Treat running_on_valgrind == -1 the same as 0 */ @@ -1263,16 +1682,21 @@ PyObject_Realloc(void *p, size_t nbytes) } size = nbytes; } - bp = PyObject_Malloc(nbytes); + bp = _PyObject_Malloc(nbytes, user_data); if (bp != NULL) { memcpy(bp, p, size); - PyObject_Free(p); + _PyObject_Free(p, user_data); } return bp; } #ifdef WITH_VALGRIND redirect: #endif +#ifdef PYMEM_TRACE_MALLOC + trace = (_PyMem_Trace *)user_data; +#endif + + /* We're not managing this block. If nbytes <= * SMALL_REQUEST_THRESHOLD, it's tempting to try to take over this * block. However, if we do, we need to copy the valid data from @@ -1283,15 +1707,25 @@ PyObject_Realloc(void *p, size_t nbytes) * a memory fault can occur if we try to copy nbytes bytes starting * at p. Instead we punt: let C continue to manage this block. */ - if (nbytes) - return realloc(p, nbytes); + if (nbytes) { +#ifdef PYMEM_TRACE_MALLOC + return _PyMem_ReallocTrace(p, nbytes, trace->filename, trace->lineno); +#else + return PyMem_Realloc(p, nbytes); +#endif + } + /* C doesn't define the result of realloc(p, 0) (it may or may not * return NULL then), but Python's docs promise that nbytes==0 never * returns NULL. We don't pass 0 to realloc(), to avoid that endcase * to begin with. Even then, we can't be sure that realloc() won't * return NULL. */ - bp = realloc(p, 1); +#ifdef PYMEM_TRACE_MALLOC + bp = _PyMem_ReallocTrace(p, 1, trace->filename, trace->lineno); +#else + bp = PyMem_Realloc(p, 1); +#endif return bp ? bp : p; } @@ -1301,24 +1735,6 @@ PyObject_Realloc(void *p, size_t nbytes) /* pymalloc not enabled: Redirect the entry points to malloc. These will * only be used by extensions that are compiled with pymalloc enabled. */ -void * -PyObject_Malloc(size_t n) -{ - return PyMem_MALLOC(n); -} - -void * -PyObject_Realloc(void *p, size_t n) -{ - return PyMem_REALLOC(p, n); -} - -void -PyObject_Free(void *p) -{ - PyMem_FREE(p); -} - Py_ssize_t _Py_GetAllocatedBlocks(void) { @@ -1344,10 +1760,6 @@ Py_ssize_t #define DEADBYTE 0xDB /* dead (newly freed) memory */ #define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */ -/* We tag each block with an API ID in order to tag API violations */ -#define _PYMALLOC_MEM_ID 'm' /* the PyMem_Malloc() API */ -#define _PYMALLOC_OBJ_ID 'o' /* The PyObject_Malloc() API */ - static size_t serialno = 0; /* incremented on each debug {m,re}alloc */ /* serialno is always incremented via calling this routine. The point is @@ -1430,57 +1842,16 @@ p[2*S: 2*S+n] p[2*S+n: 2*S+n+S] Copies of FORBIDDENBYTE. Used to catch over- writes and reads. p[2*S+n+S: 2*S+n+2*S] - A serial number, incremented by 1 on each call to _PyObject_DebugMalloc - and _PyObject_DebugRealloc. + A serial number, incremented by 1 on each call to _PyMem_DebugMalloc + and _PyMem_DebugRealloc. This is a big-endian size_t. If "bad memory" is detected later, the serial number gives an excellent way to set a breakpoint on the next run, to capture the instant at which this block was passed out. */ -/* debug replacements for the PyMem_* memory API */ -void * -_PyMem_DebugMalloc(size_t nbytes) -{ - return _PyObject_DebugMallocApi(_PYMALLOC_MEM_ID, nbytes); -} -void * -_PyMem_DebugRealloc(void *p, size_t nbytes) -{ - return _PyObject_DebugReallocApi(_PYMALLOC_MEM_ID, p, nbytes); -} -void -_PyMem_DebugFree(void *p) -{ - _PyObject_DebugFreeApi(_PYMALLOC_MEM_ID, p); -} - -/* debug replacements for the PyObject_* memory API */ -void * -_PyObject_DebugMalloc(size_t nbytes) -{ - return _PyObject_DebugMallocApi(_PYMALLOC_OBJ_ID, nbytes); -} -void * -_PyObject_DebugRealloc(void *p, size_t nbytes) -{ - return _PyObject_DebugReallocApi(_PYMALLOC_OBJ_ID, p, nbytes); -} -void -_PyObject_DebugFree(void *p) -{ - _PyObject_DebugFreeApi(_PYMALLOC_OBJ_ID, p); -} -void -_PyObject_DebugCheckAddress(const void *p) -{ - _PyObject_DebugCheckAddressApi(_PYMALLOC_OBJ_ID, p); -} - - -/* generic debug memory api, with an "id" to identify the API in use */ -void * -_PyObject_DebugMallocApi(char id, size_t nbytes) +static void * +_PyMem_DebugMalloc(size_t nbytes, void *user_data) { uchar *p; /* base address of malloc'ed block */ uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */ @@ -1492,14 +1863,13 @@ void * /* overflow: can't represent total as a size_t */ return NULL; - p = (uchar *)PyObject_Malloc(total); + p = (uchar *)malloc(total); if (p == NULL) return NULL; - /* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */ + /* at p, write size (SST bytes), pad (SST bytes) */ write_size_t(p, nbytes); - p[SST] = (uchar)id; - memset(p + SST + 1 , FORBIDDENBYTE, SST-1); + memset(p + SST, FORBIDDENBYTE, SST); if (nbytes > 0) memset(p + 2*SST, CLEANBYTE, nbytes); @@ -1517,24 +1887,24 @@ void * Then fills the original bytes with DEADBYTE. Then calls the underlying free. */ -void -_PyObject_DebugFreeApi(char api, void *p) +static void +_PyMem_DebugFree(void *p, void *user_data) { uchar *q = (uchar *)p - 2*SST; /* address returned from malloc */ size_t nbytes; if (p == NULL) return; - _PyObject_DebugCheckAddressApi(api, p); + _PyMem_DebugCheckAddress(p); nbytes = read_size_t(q); nbytes += 4*SST; if (nbytes > 0) memset(q, DEADBYTE, nbytes); - PyObject_Free(q); + free(q); } -void * -_PyObject_DebugReallocApi(char api, void *p, size_t nbytes) +static void * +_PyMem_DebugRealloc(void *p, size_t nbytes, void *user_data) { uchar *q = (uchar *)p; uchar *tail; @@ -1543,9 +1913,9 @@ void * int i; if (p == NULL) - return _PyObject_DebugMallocApi(api, nbytes); + return _PyMem_DebugMalloc(nbytes, user_data); - _PyObject_DebugCheckAddressApi(api, p); + _PyMem_DebugCheckAddress(p); bumpserialno(); original_nbytes = read_size_t(q - 2*SST); total = nbytes + 4*SST; @@ -1562,13 +1932,12 @@ void * * case we didn't get the chance to mark the old memory with DEADBYTE, * but we live with that. */ - q = (uchar *)PyObject_Realloc(q - 2*SST, total); + q = (uchar *)realloc(q - 2*SST, total); if (q == NULL) return NULL; write_size_t(q, nbytes); - assert(q[SST] == (uchar)api); - for (i = 1; i < SST; ++i) + for (i = 0; i < SST; ++i) assert(q[SST + i] == FORBIDDENBYTE); q += 2*SST; tail = q + nbytes; @@ -1587,38 +1956,26 @@ void * /* 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. - * The API id, is also checked. */ - void -_PyObject_DebugCheckAddressApi(char api, const void *p) +static void +_PyMem_DebugCheckAddress(const void *p) { const uchar *q = (const uchar *)p; - char msgbuf[64]; char *msg; size_t nbytes; const uchar *tail; int i; - char id; if (p == NULL) { msg = "didn't expect a NULL pointer"; goto error; } - /* Check the API id */ - id = (char)q[-SST]; - if (id != api) { - msg = msgbuf; - snprintf(msg, sizeof(msgbuf), "bad ID: Allocated using API '%c', verified using API '%c'", id, api); - msgbuf[sizeof(msgbuf)-1] = 0; - goto error; - } - /* Check the stuff at the start of p first: if there's underwrite * corruption, the number-of-bytes field may be nuts, and checking * the tail could lead to a segfault then. */ - for (i = SST-1; i >= 1; --i) { + for (i = SST; i >= 1; --i) { if (*(q-i) != FORBIDDENBYTE) { msg = "bad leading pad byte"; goto error; @@ -1642,7 +1999,7 @@ error: } /* Display info to stderr about the memory block at p. */ -void +static void _PyObject_DebugDumpAddress(const void *p) { const uchar *q = (const uchar *)p; @@ -1650,24 +2007,20 @@ void size_t nbytes, serial; int i; int ok; - char id; - fprintf(stderr, "Debug memory block at address p=%p:", p); + fprintf(stderr, "Debug memory block at address p=%p:\n", p); if (p == NULL) { - fprintf(stderr, "\n"); return; } - id = (char)q[-SST]; - fprintf(stderr, " API '%c'\n", id); nbytes = read_size_t(q - 2*SST); fprintf(stderr, " %" PY_FORMAT_SIZE_T "u bytes originally " "requested\n", nbytes); /* In case this is nuts, check the leading pad bytes first. */ - fprintf(stderr, " The %d pad bytes at p-%d are ", SST-1, SST-1); + fprintf(stderr, " The %d pad bytes at p-%d are ", SST, SST); ok = 1; - for (i = 1; i <= SST-1; ++i) { + for (i = 1; i <= SST; ++i) { if (*(q-i) != FORBIDDENBYTE) { ok = 0; break; @@ -1678,7 +2031,7 @@ void else { fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n", FORBIDDENBYTE); - for (i = SST-1; i >= 1; --i) { + for (i = SST; i >= 1; --i) { const uchar byte = *(q-i); fprintf(stderr, " at p-%d: 0x%02x", i, byte); if (byte != FORBIDDENBYTE)