diff options
Diffstat (limited to 'Objects/obmalloc.c')
| -rw-r--r-- | Objects/obmalloc.c | 283 | 
1 files changed, 207 insertions, 76 deletions
| diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 7cc889f817..3f95133a74 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -1,17 +1,38 @@  #include "Python.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 */ +/* + * 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 uptr +#define uptr    Py_uintptr_t +  /* 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? */ @@ -145,9 +166,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 +183,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 +205,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 +287,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 +338,6 @@ PyMem_SetupDebugHooks(void)          PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc);          PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);      } -#endif  }  void @@ -264,7 +368,6 @@ PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)      case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break;      /* ignore unknown domain */      } -  }  void @@ -642,22 +745,6 @@ 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; @@ -949,11 +1036,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; @@ -1709,7 +1800,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. @@ -1767,31 +1858,6 @@ write_size_t(void *p, size_t n)      }  } -#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,7 +1885,7 @@ 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 */ @@ -1856,18 +1922,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,7 +1942,7 @@ _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 */ @@ -1893,7 +1959,7 @@ _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; @@ -1903,7 +1969,7 @@ _PyMem_DebugRealloc(void *ctx, void *p, size_t 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(); @@ -1946,6 +2012,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. @@ -2104,9 +2208,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 +2265,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. @@ -2233,7 +2362,9 @@ _PyObject_DebugMallocStats(FILE *out)              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 +2404,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); @@ -2303,6 +2433,7 @@ _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. | 
