LCOV - code coverage report
Current view: | top level - Modules - _tracemalloc.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 69 | 659 | 10.5 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 10 | 67 | 14.9 %
|  |  | Branches: | 18 | 252 | 7.1 %

               Branch data     Line data    Source code
           1                 :            : #include "Python.h"
           2                 :            : #include "pycore_fileutils.h"     // _Py_write_noraise()
           3                 :            : #include "pycore_gc.h"            // PyGC_Head
           4                 :            : #include "pycore_hashtable.h"     // _Py_hashtable_t
           5                 :            : #include "pycore_object.h"        // _PyType_PreHeaderSize
           6                 :            : #include "pycore_pymem.h"         // _Py_tracemalloc_config
           7                 :            : #include "pycore_runtime.h"       // _Py_ID()
           8                 :            : #include "pycore_traceback.h"
           9                 :            : #include <pycore_frame.h>
          10                 :            :
          11                 :            : #include <stdlib.h>               // malloc()
          12                 :            :
          13                 :            : #include "clinic/_tracemalloc.c.h"
          14                 :            :
          15                 :            : /*[clinic input]
          16                 :            : module _tracemalloc
          17                 :            : [clinic start generated code]*/
          18                 :            : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=708a98302fc46e5f]*/
          19                 :            :
          20                 :            : #define tracemalloc_config _PyRuntime.tracemalloc.config
          21                 :            :
          22                 :            : _Py_DECLARE_STR(anon_unknown, "<unknown>");
          23                 :            :
          24                 :            : /* Forward declaration */
          25                 :            : static void tracemalloc_stop(void);
          26                 :            : static void* raw_malloc(size_t size);
          27                 :            : static void raw_free(void *ptr);
          28                 :            :
          29                 :            : #ifdef Py_DEBUG
          30                 :            : #  define TRACE_DEBUG
          31                 :            : #endif
          32                 :            :
          33                 :            : #define TO_PTR(key) ((const void *)(uintptr_t)(key))
          34                 :            : #define FROM_PTR(key) ((uintptr_t)(key))
          35                 :            :
          36                 :            : #define allocators _PyRuntime.tracemalloc.allocators
          37                 :            :
          38                 :            :
          39                 :            : #if defined(TRACE_RAW_MALLOC)
          40                 :            : /* This lock is needed because tracemalloc_free() is called without
          41                 :            :    the GIL held from PyMem_RawFree(). It cannot acquire the lock because it
          42                 :            :    would introduce a deadlock in _PyThreadState_DeleteCurrent(). */
          43                 :            : #  define tables_lock _PyRuntime.tracemalloc.tables_lock
          44                 :            : #  define TABLES_LOCK() PyThread_acquire_lock(tables_lock, 1)
          45                 :            : #  define TABLES_UNLOCK() PyThread_release_lock(tables_lock)
          46                 :            : #else
          47                 :            :    /* variables are protected by the GIL */
          48                 :            : #  define TABLES_LOCK()
          49                 :            : #  define TABLES_UNLOCK()
          50                 :            : #endif
          51                 :            :
          52                 :            :
          53                 :            : #define DEFAULT_DOMAIN 0
          54                 :            :
          55                 :            : typedef struct tracemalloc_frame frame_t;
          56                 :            : typedef struct tracemalloc_traceback traceback_t;
          57                 :            :
          58                 :            : #define TRACEBACK_SIZE(NFRAME) \
          59                 :            :         (sizeof(traceback_t) + sizeof(frame_t) * (NFRAME - 1))
          60                 :            :
          61                 :            : /* The maximum number of frames is either:
          62                 :            :  - The maximum number of frames we can store in `traceback_t.nframe`
          63                 :            :  - The maximum memory size_t we can allocate */
          64                 :            : static const unsigned long MAX_NFRAME = Py_MIN(UINT16_MAX, ((SIZE_MAX - sizeof(traceback_t)) / sizeof(frame_t) + 1));
          65                 :            :
          66                 :            :
          67                 :            : #define tracemalloc_empty_traceback _PyRuntime.tracemalloc.empty_traceback
          68                 :            :
          69                 :            :
          70                 :            : /* Trace of a memory block */
          71                 :            : typedef struct {
          72                 :            :     /* Size of the memory block in bytes */
          73                 :            :     size_t size;
          74                 :            :
          75                 :            :     /* Traceback where the memory block was allocated */
          76                 :            :     traceback_t *traceback;
          77                 :            : } trace_t;
          78                 :            :
          79                 :            :
          80                 :            : #define tracemalloc_traced_memory _PyRuntime.tracemalloc.traced_memory
          81                 :            : #define tracemalloc_peak_traced_memory _PyRuntime.tracemalloc.peak_traced_memory
          82                 :            : #define tracemalloc_filenames _PyRuntime.tracemalloc.filenames
          83                 :            : #define tracemalloc_traceback _PyRuntime.tracemalloc.traceback
          84                 :            : #define tracemalloc_tracebacks _PyRuntime.tracemalloc.tracebacks
          85                 :            : #define tracemalloc_traces _PyRuntime.tracemalloc.traces
          86                 :            : #define tracemalloc_domains _PyRuntime.tracemalloc.domains
          87                 :            :
          88                 :            :
          89                 :            : #ifdef TRACE_DEBUG
          90                 :            : static void
          91                 :            : tracemalloc_error(const char *format, ...)
          92                 :            : {
          93                 :            :     va_list ap;
          94                 :            :     fprintf(stderr, "tracemalloc: ");
          95                 :            :     va_start(ap, format);
          96                 :            :     vfprintf(stderr, format, ap);
          97                 :            :     va_end(ap);
          98                 :            :     fprintf(stderr, "\n");
          99                 :            :     fflush(stderr);
         100                 :            : }
         101                 :            : #endif
         102                 :            :
         103                 :            :
         104                 :            : #if defined(TRACE_RAW_MALLOC)
         105                 :            : #define REENTRANT_THREADLOCAL
         106                 :            :
         107                 :            : #define tracemalloc_reentrant_key _PyRuntime.tracemalloc.reentrant_key
         108                 :            :
         109                 :            : /* Any non-NULL pointer can be used */
         110                 :            : #define REENTRANT Py_True
         111                 :            :
         112                 :            : static int
         113                 :          0 : get_reentrant(void)
         114                 :            : {
         115                 :            :     void *ptr;
         116                 :            :
         117                 :            :     assert(PyThread_tss_is_created(&tracemalloc_reentrant_key));
         118                 :          0 :     ptr = PyThread_tss_get(&tracemalloc_reentrant_key);
         119         [ #  # ]:          0 :     if (ptr != NULL) {
         120                 :            :         assert(ptr == REENTRANT);
         121                 :          0 :         return 1;
         122                 :            :     }
         123                 :            :     else
         124                 :          0 :         return 0;
         125                 :            : }
         126                 :            :
         127                 :            : static void
         128                 :          0 : set_reentrant(int reentrant)
         129                 :            : {
         130                 :            :     assert(reentrant == 0 || reentrant == 1);
         131                 :            :     assert(PyThread_tss_is_created(&tracemalloc_reentrant_key));
         132                 :            :
         133         [ #  # ]:          0 :     if (reentrant) {
         134                 :            :         assert(!get_reentrant());
         135                 :          0 :         PyThread_tss_set(&tracemalloc_reentrant_key, REENTRANT);
         136                 :            :     }
         137                 :            :     else {
         138                 :            :         assert(get_reentrant());
         139                 :          0 :         PyThread_tss_set(&tracemalloc_reentrant_key, NULL);
         140                 :            :     }
         141                 :          0 : }
         142                 :            :
         143                 :            : #else
         144                 :            :
         145                 :            : /* TRACE_RAW_MALLOC not defined: variable protected by the GIL */
         146                 :            : static int tracemalloc_reentrant = 0;
         147                 :            :
         148                 :            : static int
         149                 :            : get_reentrant(void)
         150                 :            : {
         151                 :            :     return tracemalloc_reentrant;
         152                 :            : }
         153                 :            :
         154                 :            : static void
         155                 :            : set_reentrant(int reentrant)
         156                 :            : {
         157                 :            :     assert(reentrant != tracemalloc_reentrant);
         158                 :            :     tracemalloc_reentrant = reentrant;
         159                 :            : }
         160                 :            : #endif
         161                 :            :
         162                 :            :
         163                 :            : static Py_uhash_t
         164                 :          0 : hashtable_hash_pyobject(const void *key)
         165                 :            : {
         166                 :          0 :     PyObject *obj = (PyObject *)key;
         167                 :          0 :     return PyObject_Hash(obj);
         168                 :            : }
         169                 :            :
         170                 :            :
         171                 :            : static int
         172                 :          0 : hashtable_compare_unicode(const void *key1, const void *key2)
         173                 :            : {
         174                 :          0 :     PyObject *obj1 = (PyObject *)key1;
         175                 :          0 :     PyObject *obj2 = (PyObject *)key2;
         176   [ #  #  #  # ]:          0 :     if (obj1 != NULL && obj2 != NULL) {
         177                 :          0 :         return (PyUnicode_Compare(obj1, obj2) == 0);
         178                 :            :     }
         179                 :            :     else {
         180                 :          0 :         return obj1 == obj2;
         181                 :            :     }
         182                 :            : }
         183                 :            :
         184                 :            :
         185                 :            : static Py_uhash_t
         186                 :          0 : hashtable_hash_uint(const void *key_raw)
         187                 :            : {
         188                 :          0 :     unsigned int key = (unsigned int)FROM_PTR(key_raw);
         189                 :          0 :     return (Py_uhash_t)key;
         190                 :            : }
         191                 :            :
         192                 :            :
         193                 :            : static _Py_hashtable_t *
         194                 :          4 : hashtable_new(_Py_hashtable_hash_func hash_func,
         195                 :            :               _Py_hashtable_compare_func compare_func,
         196                 :            :               _Py_hashtable_destroy_func key_destroy_func,
         197                 :            :               _Py_hashtable_destroy_func value_destroy_func)
         198                 :            : {
         199                 :          4 :     _Py_hashtable_allocator_t hashtable_alloc = {malloc, free};
         200                 :          4 :     return _Py_hashtable_new_full(hash_func, compare_func,
         201                 :            :                                   key_destroy_func, value_destroy_func,
         202                 :            :                                   &hashtable_alloc);
         203                 :            : }
         204                 :            :
         205                 :            :
         206                 :            : static void*
         207                 :          0 : raw_malloc(size_t size)
         208                 :            : {
         209                 :          0 :     return allocators.raw.malloc(allocators.raw.ctx, size);
         210                 :            : }
         211                 :            :
         212                 :            : static void
         213                 :          0 : raw_free(void *ptr)
         214                 :            : {
         215                 :          0 :     allocators.raw.free(allocators.raw.ctx, ptr);
         216                 :          0 : }
         217                 :            :
         218                 :            :
         219                 :            : static Py_uhash_t
         220                 :          0 : hashtable_hash_traceback(const void *key)
         221                 :            : {
         222                 :          0 :     const traceback_t *traceback = (const traceback_t *)key;
         223                 :          0 :     return traceback->hash;
         224                 :            : }
         225                 :            :
         226                 :            :
         227                 :            : static int
         228                 :          0 : hashtable_compare_traceback(const void *key1, const void *key2)
         229                 :            : {
         230                 :          0 :     const traceback_t *traceback1 = (const traceback_t *)key1;
         231                 :          0 :     const traceback_t *traceback2 = (const traceback_t *)key2;
         232                 :            :
         233         [ #  # ]:          0 :     if (traceback1->nframe != traceback2->nframe) {
         234                 :          0 :         return 0;
         235                 :            :     }
         236         [ #  # ]:          0 :     if (traceback1->total_nframe != traceback2->total_nframe) {
         237                 :          0 :         return 0;
         238                 :            :     }
         239                 :            :
         240         [ #  # ]:          0 :     for (int i=0; i < traceback1->nframe; i++) {
         241                 :          0 :         const frame_t *frame1 = &traceback1->frames[i];
         242                 :          0 :         const frame_t *frame2 = &traceback2->frames[i];
         243                 :            :
         244         [ #  # ]:          0 :         if (frame1->lineno != frame2->lineno) {
         245                 :          0 :             return 0;
         246                 :            :         }
         247         [ #  # ]:          0 :         if (frame1->filename != frame2->filename) {
         248                 :            :             assert(PyUnicode_Compare(frame1->filename, frame2->filename) != 0);
         249                 :          0 :             return 0;
         250                 :            :         }
         251                 :            :     }
         252                 :          0 :     return 1;
         253                 :            : }
         254                 :            :
         255                 :            :
         256                 :            : static void
         257                 :          0 : tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame)
         258                 :            : {
         259                 :          0 :     frame->filename = &_Py_STR(anon_unknown);
         260                 :          0 :     int lineno = _PyInterpreterFrame_GetLine(pyframe);
         261         [ #  # ]:          0 :     if (lineno < 0) {
         262                 :          0 :         lineno = 0;
         263                 :            :     }
         264                 :          0 :     frame->lineno = (unsigned int)lineno;
         265                 :            :
         266                 :          0 :     PyObject *filename = pyframe->f_code->co_filename;
         267                 :            :
         268         [ #  # ]:          0 :     if (filename == NULL) {
         269                 :            : #ifdef TRACE_DEBUG
         270                 :            :         tracemalloc_error("failed to get the filename of the code object");
         271                 :            : #endif
         272                 :          0 :         return;
         273                 :            :     }
         274                 :            :
         275         [ #  # ]:          0 :     if (!PyUnicode_Check(filename)) {
         276                 :            : #ifdef TRACE_DEBUG
         277                 :            :         tracemalloc_error("filename is not a unicode string");
         278                 :            : #endif
         279                 :          0 :         return;
         280                 :            :     }
         281         [ #  # ]:          0 :     if (!PyUnicode_IS_READY(filename)) {
         282                 :            :         /* Don't make a Unicode string ready to avoid reentrant calls
         283                 :            :            to tracemalloc_malloc() or tracemalloc_realloc() */
         284                 :            : #ifdef TRACE_DEBUG
         285                 :            :         tracemalloc_error("filename is not a ready unicode string");
         286                 :            : #endif
         287                 :          0 :         return;
         288                 :            :     }
         289                 :            :
         290                 :            :     /* intern the filename */
         291                 :            :     _Py_hashtable_entry_t *entry;
         292                 :          0 :     entry = _Py_hashtable_get_entry(tracemalloc_filenames, filename);
         293         [ #  # ]:          0 :     if (entry != NULL) {
         294                 :          0 :         filename = (PyObject *)entry->key;
         295                 :            :     }
         296                 :            :     else {
         297                 :            :         /* tracemalloc_filenames is responsible to keep a reference
         298                 :            :            to the filename */
         299         [ #  # ]:          0 :         if (_Py_hashtable_set(tracemalloc_filenames, Py_NewRef(filename),
         300                 :            :                               NULL) < 0) {
         301                 :          0 :             Py_DECREF(filename);
         302                 :            : #ifdef TRACE_DEBUG
         303                 :            :             tracemalloc_error("failed to intern the filename");
         304                 :            : #endif
         305                 :          0 :             return;
         306                 :            :         }
         307                 :            :     }
         308                 :            :
         309                 :            :     /* the tracemalloc_filenames table keeps a reference to the filename */
         310                 :          0 :     frame->filename = filename;
         311                 :            : }
         312                 :            :
         313                 :            :
         314                 :            : static Py_uhash_t
         315                 :          1 : traceback_hash(traceback_t *traceback)
         316                 :            : {
         317                 :            :     /* code based on tuplehash() of Objects/tupleobject.c */
         318                 :            :     Py_uhash_t x, y;  /* Unsigned for defined overflow behavior. */
         319                 :          1 :     int len = traceback->nframe;
         320                 :          1 :     Py_uhash_t mult = _PyHASH_MULTIPLIER;
         321                 :            :     frame_t *frame;
         322                 :            :
         323                 :          1 :     x = 0x345678UL;
         324                 :          1 :     frame = traceback->frames;
         325         [ +  + ]:          2 :     while (--len >= 0) {
         326                 :          1 :         y = (Py_uhash_t)PyObject_Hash(frame->filename);
         327                 :          1 :         y ^= (Py_uhash_t)frame->lineno;
         328                 :          1 :         frame++;
         329                 :            :
         330                 :          1 :         x = (x ^ y) * mult;
         331                 :            :         /* the cast might truncate len; that doesn't change hash stability */
         332                 :          1 :         mult += (Py_uhash_t)(82520UL + len + len);
         333                 :            :     }
         334                 :          1 :     x ^= traceback->total_nframe;
         335                 :          1 :     x += 97531UL;
         336                 :          1 :     return x;
         337                 :            : }
         338                 :            :
         339                 :            :
         340                 :            : static void
         341                 :          0 : traceback_get_frames(traceback_t *traceback)
         342                 :            : {
         343                 :          0 :     PyThreadState *tstate = PyGILState_GetThisThreadState();
         344         [ #  # ]:          0 :     if (tstate == NULL) {
         345                 :            : #ifdef TRACE_DEBUG
         346                 :            :         tracemalloc_error("failed to get the current thread state");
         347                 :            : #endif
         348                 :          0 :         return;
         349                 :            :     }
         350                 :            :
         351                 :          0 :     _PyInterpreterFrame *pyframe = _PyThreadState_GetFrame(tstate);
         352         [ #  # ]:          0 :     while (pyframe) {
         353         [ #  # ]:          0 :         if (traceback->nframe < tracemalloc_config.max_nframe) {
         354                 :          0 :             tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]);
         355                 :            :             assert(traceback->frames[traceback->nframe].filename != NULL);
         356                 :          0 :             traceback->nframe++;
         357                 :            :         }
         358         [ #  # ]:          0 :         if (traceback->total_nframe < UINT16_MAX) {
         359                 :          0 :             traceback->total_nframe++;
         360                 :            :         }
         361                 :          0 :         pyframe = _PyFrame_GetFirstComplete(pyframe->previous);
         362                 :            :     }
         363                 :            : }
         364                 :            :
         365                 :            :
         366                 :            : static traceback_t *
         367                 :          0 : traceback_new(void)
         368                 :            : {
         369                 :            :     traceback_t *traceback;
         370                 :            :     _Py_hashtable_entry_t *entry;
         371                 :            :
         372                 :            :     assert(PyGILState_Check());
         373                 :            :
         374                 :            :     /* get frames */
         375                 :          0 :     traceback = tracemalloc_traceback;
         376                 :          0 :     traceback->nframe = 0;
         377                 :          0 :     traceback->total_nframe = 0;
         378                 :          0 :     traceback_get_frames(traceback);
         379         [ #  # ]:          0 :     if (traceback->nframe == 0)
         380                 :          0 :         return &tracemalloc_empty_traceback;
         381                 :          0 :     traceback->hash = traceback_hash(traceback);
         382                 :            :
         383                 :            :     /* intern the traceback */
         384                 :          0 :     entry = _Py_hashtable_get_entry(tracemalloc_tracebacks, traceback);
         385         [ #  # ]:          0 :     if (entry != NULL) {
         386                 :          0 :         traceback = (traceback_t *)entry->key;
         387                 :            :     }
         388                 :            :     else {
         389                 :            :         traceback_t *copy;
         390                 :            :         size_t traceback_size;
         391                 :            :
         392                 :          0 :         traceback_size = TRACEBACK_SIZE(traceback->nframe);
         393                 :            :
         394                 :          0 :         copy = raw_malloc(traceback_size);
         395         [ #  # ]:          0 :         if (copy == NULL) {
         396                 :            : #ifdef TRACE_DEBUG
         397                 :            :             tracemalloc_error("failed to intern the traceback: malloc failed");
         398                 :            : #endif
         399                 :          0 :             return NULL;
         400                 :            :         }
         401                 :          0 :         memcpy(copy, traceback, traceback_size);
         402                 :            :
         403         [ #  # ]:          0 :         if (_Py_hashtable_set(tracemalloc_tracebacks, copy, NULL) < 0) {
         404                 :          0 :             raw_free(copy);
         405                 :            : #ifdef TRACE_DEBUG
         406                 :            :             tracemalloc_error("failed to intern the traceback: putdata failed");
         407                 :            : #endif
         408                 :          0 :             return NULL;
         409                 :            :         }
         410                 :          0 :         traceback = copy;
         411                 :            :     }
         412                 :          0 :     return traceback;
         413                 :            : }
         414                 :            :
         415                 :            :
         416                 :            : static _Py_hashtable_t*
         417                 :          1 : tracemalloc_create_traces_table(void)
         418                 :            : {
         419                 :          1 :     return hashtable_new(_Py_hashtable_hash_ptr,
         420                 :            :                          _Py_hashtable_compare_direct,
         421                 :            :                          NULL, raw_free);
         422                 :            : }
         423                 :            :
         424                 :            :
         425                 :            : static _Py_hashtable_t*
         426                 :          1 : tracemalloc_create_domains_table(void)
         427                 :            : {
         428                 :          1 :     return hashtable_new(hashtable_hash_uint,
         429                 :            :                          _Py_hashtable_compare_direct,
         430                 :            :                          NULL,
         431                 :            :                          (_Py_hashtable_destroy_func)_Py_hashtable_destroy);
         432                 :            : }
         433                 :            :
         434                 :            :
         435                 :            : static _Py_hashtable_t*
         436                 :          0 : tracemalloc_get_traces_table(unsigned int domain)
         437                 :            : {
         438         [ #  # ]:          0 :     if (domain == DEFAULT_DOMAIN) {
         439                 :          0 :         return tracemalloc_traces;
         440                 :            :     }
         441                 :            :     else {
         442                 :          0 :         return _Py_hashtable_get(tracemalloc_domains, TO_PTR(domain));
         443                 :            :     }
         444                 :            : }
         445                 :            :
         446                 :            :
         447                 :            : static void
         448                 :          0 : tracemalloc_remove_trace(unsigned int domain, uintptr_t ptr)
         449                 :            : {
         450                 :            :     assert(tracemalloc_config.tracing);
         451                 :            :
         452                 :          0 :     _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
         453         [ #  # ]:          0 :     if (!traces) {
         454                 :          0 :         return;
         455                 :            :     }
         456                 :            :
         457                 :          0 :     trace_t *trace = _Py_hashtable_steal(traces, TO_PTR(ptr));
         458         [ #  # ]:          0 :     if (!trace) {
         459                 :          0 :         return;
         460                 :            :     }
         461                 :            :     assert(tracemalloc_traced_memory >= trace->size);
         462                 :          0 :     tracemalloc_traced_memory -= trace->size;
         463                 :          0 :     raw_free(trace);
         464                 :            : }
         465                 :            :
         466                 :            : #define REMOVE_TRACE(ptr) \
         467                 :            :             tracemalloc_remove_trace(DEFAULT_DOMAIN, (uintptr_t)(ptr))
         468                 :            :
         469                 :            :
         470                 :            : static int
         471                 :          0 : tracemalloc_add_trace(unsigned int domain, uintptr_t ptr,
         472                 :            :                       size_t size)
         473                 :            : {
         474                 :            :     assert(tracemalloc_config.tracing);
         475                 :            :
         476                 :          0 :     traceback_t *traceback = traceback_new();
         477         [ #  # ]:          0 :     if (traceback == NULL) {
         478                 :          0 :         return -1;
         479                 :            :     }
         480                 :            :
         481                 :          0 :     _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
         482         [ #  # ]:          0 :     if (traces == NULL) {
         483                 :          0 :         traces = tracemalloc_create_traces_table();
         484         [ #  # ]:          0 :         if (traces == NULL) {
         485                 :          0 :             return -1;
         486                 :            :         }
         487                 :            :
         488         [ #  # ]:          0 :         if (_Py_hashtable_set(tracemalloc_domains, TO_PTR(domain), traces) < 0) {
         489                 :          0 :             _Py_hashtable_destroy(traces);
         490                 :          0 :             return -1;
         491                 :            :         }
         492                 :            :     }
         493                 :            :
         494                 :          0 :     trace_t *trace = _Py_hashtable_get(traces, TO_PTR(ptr));
         495         [ #  # ]:          0 :     if (trace != NULL) {
         496                 :            :         /* the memory block is already tracked */
         497                 :            :         assert(tracemalloc_traced_memory >= trace->size);
         498                 :          0 :         tracemalloc_traced_memory -= trace->size;
         499                 :            :
         500                 :          0 :         trace->size = size;
         501                 :          0 :         trace->traceback = traceback;
         502                 :            :     }
         503                 :            :     else {
         504                 :          0 :         trace = raw_malloc(sizeof(trace_t));
         505         [ #  # ]:          0 :         if (trace == NULL) {
         506                 :          0 :             return -1;
         507                 :            :         }
         508                 :          0 :         trace->size = size;
         509                 :          0 :         trace->traceback = traceback;
         510                 :            :
         511                 :          0 :         int res = _Py_hashtable_set(traces, TO_PTR(ptr), trace);
         512         [ #  # ]:          0 :         if (res != 0) {
         513                 :          0 :             raw_free(trace);
         514                 :          0 :             return res;
         515                 :            :         }
         516                 :            :     }
         517                 :            :
         518                 :            :     assert(tracemalloc_traced_memory <= SIZE_MAX - size);
         519                 :          0 :     tracemalloc_traced_memory += size;
         520         [ #  # ]:          0 :     if (tracemalloc_traced_memory > tracemalloc_peak_traced_memory) {
         521                 :          0 :         tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
         522                 :            :     }
         523                 :          0 :     return 0;
         524                 :            : }
         525                 :            :
         526                 :            : #define ADD_TRACE(ptr, size) \
         527                 :            :             tracemalloc_add_trace(DEFAULT_DOMAIN, (uintptr_t)(ptr), size)
         528                 :            :
         529                 :            :
         530                 :            : static void*
         531                 :          0 : tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
         532                 :            : {
         533                 :          0 :     PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
         534                 :            :     void *ptr;
         535                 :            :
         536                 :            :     assert(elsize == 0 || nelem <= SIZE_MAX / elsize);
         537                 :            :
         538         [ #  # ]:          0 :     if (use_calloc)
         539                 :          0 :         ptr = alloc->calloc(alloc->ctx, nelem, elsize);
         540                 :            :     else
         541                 :          0 :         ptr = alloc->malloc(alloc->ctx, nelem * elsize);
         542         [ #  # ]:          0 :     if (ptr == NULL)
         543                 :          0 :         return NULL;
         544                 :            :
         545                 :          0 :     TABLES_LOCK();
         546         [ #  # ]:          0 :     if (ADD_TRACE(ptr, nelem * elsize) < 0) {
         547                 :            :         /* Failed to allocate a trace for the new memory block */
         548                 :          0 :         TABLES_UNLOCK();
         549                 :          0 :         alloc->free(alloc->ctx, ptr);
         550                 :          0 :         return NULL;
         551                 :            :     }
         552                 :          0 :     TABLES_UNLOCK();
         553                 :          0 :     return ptr;
         554                 :            : }
         555                 :            :
         556                 :            :
         557                 :            : static void*
         558                 :          0 : tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
         559                 :            : {
         560                 :          0 :     PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
         561                 :            :     void *ptr2;
         562                 :            :
         563                 :          0 :     ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
         564         [ #  # ]:          0 :     if (ptr2 == NULL)
         565                 :          0 :         return NULL;
         566                 :            :
         567         [ #  # ]:          0 :     if (ptr != NULL) {
         568                 :            :         /* an existing memory block has been resized */
         569                 :            :
         570                 :          0 :         TABLES_LOCK();
         571                 :            :
         572                 :            :         /* tracemalloc_add_trace() updates the trace if there is already
         573                 :            :            a trace at address ptr2 */
         574         [ #  # ]:          0 :         if (ptr2 != ptr) {
         575                 :          0 :             REMOVE_TRACE(ptr);
         576                 :            :         }
         577                 :            :
         578         [ #  # ]:          0 :         if (ADD_TRACE(ptr2, new_size) < 0) {
         579                 :            :             /* Memory allocation failed. The error cannot be reported to
         580                 :            :                the caller, because realloc() may already have shrunk the
         581                 :            :                memory block and so removed bytes.
         582                 :            :
         583                 :            :                This case is very unlikely: a hash entry has just been
         584                 :            :                released, so the hash table should have at least one free entry.
         585                 :            :
         586                 :            :                The GIL and the table lock ensures that only one thread is
         587                 :            :                allocating memory. */
         588                 :          0 :             Py_FatalError("tracemalloc_realloc() failed to allocate a trace");
         589                 :            :         }
         590                 :          0 :         TABLES_UNLOCK();
         591                 :            :     }
         592                 :            :     else {
         593                 :            :         /* new allocation */
         594                 :            :
         595                 :          0 :         TABLES_LOCK();
         596         [ #  # ]:          0 :         if (ADD_TRACE(ptr2, new_size) < 0) {
         597                 :            :             /* Failed to allocate a trace for the new memory block */
         598                 :          0 :             TABLES_UNLOCK();
         599                 :          0 :             alloc->free(alloc->ctx, ptr2);
         600                 :          0 :             return NULL;
         601                 :            :         }
         602                 :          0 :         TABLES_UNLOCK();
         603                 :            :     }
         604                 :          0 :     return ptr2;
         605                 :            : }
         606                 :            :
         607                 :            :
         608                 :            : static void
         609                 :          0 : tracemalloc_free(void *ctx, void *ptr)
         610                 :            : {
         611                 :          0 :     PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
         612                 :            :
         613         [ #  # ]:          0 :     if (ptr == NULL)
         614                 :          0 :         return;
         615                 :            :
         616                 :            :      /* GIL cannot be locked in PyMem_RawFree() because it would introduce
         617                 :            :         a deadlock in _PyThreadState_DeleteCurrent(). */
         618                 :            :
         619                 :          0 :     alloc->free(alloc->ctx, ptr);
         620                 :            :
         621                 :          0 :     TABLES_LOCK();
         622                 :          0 :     REMOVE_TRACE(ptr);
         623                 :          0 :     TABLES_UNLOCK();
         624                 :            : }
         625                 :            :
         626                 :            :
         627                 :            : static void*
         628                 :          0 : tracemalloc_alloc_gil(int use_calloc, void *ctx, size_t nelem, size_t elsize)
         629                 :            : {
         630                 :            :     void *ptr;
         631                 :            :
         632         [ #  # ]:          0 :     if (get_reentrant()) {
         633                 :          0 :         PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
         634         [ #  # ]:          0 :         if (use_calloc)
         635                 :          0 :             return alloc->calloc(alloc->ctx, nelem, elsize);
         636                 :            :         else
         637                 :          0 :             return alloc->malloc(alloc->ctx, nelem * elsize);
         638                 :            :     }
         639                 :            :
         640                 :            :     /* Ignore reentrant call. PyObjet_Malloc() calls PyMem_Malloc() for
         641                 :            :        allocations larger than 512 bytes, don't trace the same memory
         642                 :            :        allocation twice. */
         643                 :          0 :     set_reentrant(1);
         644                 :            :
         645                 :          0 :     ptr = tracemalloc_alloc(use_calloc, ctx, nelem, elsize);
         646                 :            :
         647                 :          0 :     set_reentrant(0);
         648                 :          0 :     return ptr;
         649                 :            : }
         650                 :            :
         651                 :            :
         652                 :            : static void*
         653                 :          0 : tracemalloc_malloc_gil(void *ctx, size_t size)
         654                 :            : {
         655                 :          0 :     return tracemalloc_alloc_gil(0, ctx, 1, size);
         656                 :            : }
         657                 :            :
         658                 :            :
         659                 :            : static void*
         660                 :          0 : tracemalloc_calloc_gil(void *ctx, size_t nelem, size_t elsize)
         661                 :            : {
         662                 :          0 :     return tracemalloc_alloc_gil(1, ctx, nelem, elsize);
         663                 :            : }
         664                 :            :
         665                 :            :
         666                 :            : static void*
         667                 :          0 : tracemalloc_realloc_gil(void *ctx, void *ptr, size_t new_size)
         668                 :            : {
         669                 :            :     void *ptr2;
         670                 :            :
         671         [ #  # ]:          0 :     if (get_reentrant()) {
         672                 :            :         /* Reentrant call to PyMem_Realloc() and PyMem_RawRealloc().
         673                 :            :            Example: PyMem_RawRealloc() is called internally by pymalloc
         674                 :            :            (_PyObject_Malloc() and  _PyObject_Realloc()) to allocate a new
         675                 :            :            arena (new_arena()). */
         676                 :          0 :         PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
         677                 :            :
         678                 :          0 :         ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
         679   [ #  #  #  # ]:          0 :         if (ptr2 != NULL && ptr != NULL) {
         680                 :          0 :             TABLES_LOCK();
         681                 :          0 :             REMOVE_TRACE(ptr);
         682                 :          0 :             TABLES_UNLOCK();
         683                 :            :         }
         684                 :          0 :         return ptr2;
         685                 :            :     }
         686                 :            :
         687                 :            :     /* Ignore reentrant call. PyObjet_Realloc() calls PyMem_Realloc() for
         688                 :            :        allocations larger than 512 bytes. Don't trace the same memory
         689                 :            :        allocation twice. */
         690                 :          0 :     set_reentrant(1);
         691                 :            :
         692                 :          0 :     ptr2 = tracemalloc_realloc(ctx, ptr, new_size);
         693                 :            :
         694                 :          0 :     set_reentrant(0);
         695                 :          0 :     return ptr2;
         696                 :            : }
         697                 :            :
         698                 :            :
         699                 :            : #ifdef TRACE_RAW_MALLOC
         700                 :            : static void*
         701                 :          0 : tracemalloc_raw_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
         702                 :            : {
         703                 :            :     PyGILState_STATE gil_state;
         704                 :            :     void *ptr;
         705                 :            :
         706         [ #  # ]:          0 :     if (get_reentrant()) {
         707                 :          0 :         PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
         708         [ #  # ]:          0 :         if (use_calloc)
         709                 :          0 :             return alloc->calloc(alloc->ctx, nelem, elsize);
         710                 :            :         else
         711                 :          0 :             return alloc->malloc(alloc->ctx, nelem * elsize);
         712                 :            :     }
         713                 :            :
         714                 :            :     /* Ignore reentrant call. PyGILState_Ensure() may call PyMem_RawMalloc()
         715                 :            :        indirectly which would call PyGILState_Ensure() if reentrant are not
         716                 :            :        disabled. */
         717                 :          0 :     set_reentrant(1);
         718                 :            :
         719                 :          0 :     gil_state = PyGILState_Ensure();
         720                 :          0 :     ptr = tracemalloc_alloc(use_calloc, ctx, nelem, elsize);
         721                 :          0 :     PyGILState_Release(gil_state);
         722                 :            :
         723                 :          0 :     set_reentrant(0);
         724                 :          0 :     return ptr;
         725                 :            : }
         726                 :            :
         727                 :            :
         728                 :            : static void*
         729                 :          0 : tracemalloc_raw_malloc(void *ctx, size_t size)
         730                 :            : {
         731                 :          0 :     return tracemalloc_raw_alloc(0, ctx, 1, size);
         732                 :            : }
         733                 :            :
         734                 :            :
         735                 :            : static void*
         736                 :          0 : tracemalloc_raw_calloc(void *ctx, size_t nelem, size_t elsize)
         737                 :            : {
         738                 :          0 :     return tracemalloc_raw_alloc(1, ctx, nelem, elsize);
         739                 :            : }
         740                 :            :
         741                 :            :
         742                 :            : static void*
         743                 :          0 : tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size)
         744                 :            : {
         745                 :            :     PyGILState_STATE gil_state;
         746                 :            :     void *ptr2;
         747                 :            :
         748         [ #  # ]:          0 :     if (get_reentrant()) {
         749                 :            :         /* Reentrant call to PyMem_RawRealloc(). */
         750                 :          0 :         PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
         751                 :            :
         752                 :          0 :         ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
         753                 :            :
         754   [ #  #  #  # ]:          0 :         if (ptr2 != NULL && ptr != NULL) {
         755                 :          0 :             TABLES_LOCK();
         756                 :          0 :             REMOVE_TRACE(ptr);
         757                 :          0 :             TABLES_UNLOCK();
         758                 :            :         }
         759                 :          0 :         return ptr2;
         760                 :            :     }
         761                 :            :
         762                 :            :     /* Ignore reentrant call. PyGILState_Ensure() may call PyMem_RawMalloc()
         763                 :            :        indirectly which would call PyGILState_Ensure() if reentrant calls are
         764                 :            :        not disabled. */
         765                 :          0 :     set_reentrant(1);
         766                 :            :
         767                 :          0 :     gil_state = PyGILState_Ensure();
         768                 :          0 :     ptr2 = tracemalloc_realloc(ctx, ptr, new_size);
         769                 :          0 :     PyGILState_Release(gil_state);
         770                 :            :
         771                 :          0 :     set_reentrant(0);
         772                 :          0 :     return ptr2;
         773                 :            : }
         774                 :            : #endif   /* TRACE_RAW_MALLOC */
         775                 :            :
         776                 :            :
         777                 :            : static void
         778                 :          0 : tracemalloc_clear_filename(void *value)
         779                 :            : {
         780                 :          0 :     PyObject *filename = (PyObject *)value;
         781                 :          0 :     Py_DECREF(filename);
         782                 :          0 : }
         783                 :            :
         784                 :            :
         785                 :            : /* reentrant flag must be set to call this function and GIL must be held */
         786                 :            : static void
         787                 :          0 : tracemalloc_clear_traces(void)
         788                 :            : {
         789                 :            :     /* The GIL protects variables against concurrent access */
         790                 :            :     assert(PyGILState_Check());
         791                 :            :
         792                 :          0 :     TABLES_LOCK();
         793                 :          0 :     _Py_hashtable_clear(tracemalloc_traces);
         794                 :          0 :     _Py_hashtable_clear(tracemalloc_domains);
         795                 :          0 :     tracemalloc_traced_memory = 0;
         796                 :          0 :     tracemalloc_peak_traced_memory = 0;
         797                 :          0 :     TABLES_UNLOCK();
         798                 :            :
         799                 :          0 :     _Py_hashtable_clear(tracemalloc_tracebacks);
         800                 :            :
         801                 :          0 :     _Py_hashtable_clear(tracemalloc_filenames);
         802                 :          0 : }
         803                 :            :
         804                 :            :
         805                 :            : static int
         806                 :          1 : tracemalloc_init(void)
         807                 :            : {
         808         [ -  + ]:          1 :     if (tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) {
         809                 :          0 :         PyErr_SetString(PyExc_RuntimeError,
         810                 :            :                         "the tracemalloc module has been unloaded");
         811                 :          0 :         return -1;
         812                 :            :     }
         813                 :            :
         814         [ -  + ]:          1 :     if (tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED)
         815                 :          0 :         return 0;
         816                 :            :
         817                 :          1 :     PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
         818                 :            :
         819                 :            : #ifdef REENTRANT_THREADLOCAL
         820         [ -  + ]:          1 :     if (PyThread_tss_create(&tracemalloc_reentrant_key) != 0) {
         821                 :            : #ifdef MS_WINDOWS
         822                 :            :         PyErr_SetFromWindowsErr(0);
         823                 :            : #else
         824                 :          0 :         PyErr_SetFromErrno(PyExc_OSError);
         825                 :            : #endif
         826                 :          0 :         return -1;
         827                 :            :     }
         828                 :            : #endif
         829                 :            :
         830                 :            : #if defined(TRACE_RAW_MALLOC)
         831         [ +  - ]:          1 :     if (tables_lock == NULL) {
         832                 :          1 :         tables_lock = PyThread_allocate_lock();
         833         [ -  + ]:          1 :         if (tables_lock == NULL) {
         834                 :          0 :             PyErr_SetString(PyExc_RuntimeError, "cannot allocate lock");
         835                 :          0 :             return -1;
         836                 :            :         }
         837                 :            :     }
         838                 :            : #endif
         839                 :            :
         840                 :          1 :     tracemalloc_filenames = hashtable_new(hashtable_hash_pyobject,
         841                 :            :                                           hashtable_compare_unicode,
         842                 :            :                                           tracemalloc_clear_filename, NULL);
         843                 :            :
         844                 :          1 :     tracemalloc_tracebacks = hashtable_new(hashtable_hash_traceback,
         845                 :            :                                            hashtable_compare_traceback,
         846                 :            :                                            NULL, raw_free);
         847                 :            :
         848                 :          1 :     tracemalloc_traces = tracemalloc_create_traces_table();
         849                 :          1 :     tracemalloc_domains = tracemalloc_create_domains_table();
         850                 :            :
         851   [ +  -  +  - ]:          1 :     if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL
         852   [ +  -  -  + ]:          1 :        || tracemalloc_traces == NULL || tracemalloc_domains == NULL) {
         853                 :          0 :         PyErr_NoMemory();
         854                 :          0 :         return -1;
         855                 :            :     }
         856                 :            :
         857                 :          1 :     tracemalloc_empty_traceback.nframe = 1;
         858                 :          1 :     tracemalloc_empty_traceback.total_nframe = 1;
         859                 :            :     /* borrowed reference */
         860                 :          1 :     tracemalloc_empty_traceback.frames[0].filename = &_Py_STR(anon_unknown);
         861                 :          1 :     tracemalloc_empty_traceback.frames[0].lineno = 0;
         862                 :          1 :     tracemalloc_empty_traceback.hash = traceback_hash(&tracemalloc_empty_traceback);
         863                 :            :
         864                 :          1 :     tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
         865                 :          1 :     return 0;
         866                 :            : }
         867                 :            :
         868                 :            :
         869                 :            : static void
         870                 :         25 : tracemalloc_deinit(void)
         871                 :            : {
         872         [ +  + ]:         25 :     if (tracemalloc_config.initialized != TRACEMALLOC_INITIALIZED)
         873                 :         24 :         return;
         874                 :          1 :     tracemalloc_config.initialized = TRACEMALLOC_FINALIZED;
         875                 :            :
         876                 :          1 :     tracemalloc_stop();
         877                 :            :
         878                 :            :     /* destroy hash tables */
         879                 :          1 :     _Py_hashtable_destroy(tracemalloc_domains);
         880                 :          1 :     _Py_hashtable_destroy(tracemalloc_traces);
         881                 :          1 :     _Py_hashtable_destroy(tracemalloc_tracebacks);
         882                 :          1 :     _Py_hashtable_destroy(tracemalloc_filenames);
         883                 :            :
         884                 :            : #if defined(TRACE_RAW_MALLOC)
         885         [ +  - ]:          1 :     if (tables_lock != NULL) {
         886                 :          1 :         PyThread_free_lock(tables_lock);
         887                 :          1 :         tables_lock = NULL;
         888                 :            :     }
         889                 :            : #endif
         890                 :            :
         891                 :            : #ifdef REENTRANT_THREADLOCAL
         892                 :          1 :     PyThread_tss_delete(&tracemalloc_reentrant_key);
         893                 :            : #endif
         894                 :            : }
         895                 :            :
         896                 :            :
         897                 :            : static int
         898                 :          0 : tracemalloc_start(int max_nframe)
         899                 :            : {
         900                 :            :     PyMemAllocatorEx alloc;
         901                 :            :     size_t size;
         902                 :            :
         903   [ #  #  #  # ]:          0 :     if (max_nframe < 1 || (unsigned long) max_nframe > MAX_NFRAME) {
         904                 :          0 :         PyErr_Format(PyExc_ValueError,
         905                 :            :                      "the number of frames must be in range [1; %lu]",
         906                 :            :                      MAX_NFRAME);
         907                 :          0 :         return -1;
         908                 :            :     }
         909                 :            :
         910         [ #  # ]:          0 :     if (tracemalloc_init() < 0) {
         911                 :          0 :         return -1;
         912                 :            :     }
         913                 :            :
         914         [ #  # ]:          0 :     if (tracemalloc_config.tracing) {
         915                 :            :         /* hook already installed: do nothing */
         916                 :          0 :         return 0;
         917                 :            :     }
         918                 :            :
         919                 :          0 :     tracemalloc_config.max_nframe = max_nframe;
         920                 :            :
         921                 :            :     /* allocate a buffer to store a new traceback */
         922                 :          0 :     size = TRACEBACK_SIZE(max_nframe);
         923                 :            :     assert(tracemalloc_traceback == NULL);
         924                 :          0 :     tracemalloc_traceback = raw_malloc(size);
         925         [ #  # ]:          0 :     if (tracemalloc_traceback == NULL) {
         926                 :          0 :         PyErr_NoMemory();
         927                 :          0 :         return -1;
         928                 :            :     }
         929                 :            :
         930                 :            : #ifdef TRACE_RAW_MALLOC
         931                 :          0 :     alloc.malloc = tracemalloc_raw_malloc;
         932                 :          0 :     alloc.calloc = tracemalloc_raw_calloc;
         933                 :          0 :     alloc.realloc = tracemalloc_raw_realloc;
         934                 :          0 :     alloc.free = tracemalloc_free;
         935                 :            :
         936                 :          0 :     alloc.ctx = &allocators.raw;
         937                 :          0 :     PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
         938                 :          0 :     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
         939                 :            : #endif
         940                 :            :
         941                 :          0 :     alloc.malloc = tracemalloc_malloc_gil;
         942                 :          0 :     alloc.calloc = tracemalloc_calloc_gil;
         943                 :          0 :     alloc.realloc = tracemalloc_realloc_gil;
         944                 :          0 :     alloc.free = tracemalloc_free;
         945                 :            :
         946                 :          0 :     alloc.ctx = &allocators.mem;
         947                 :          0 :     PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem);
         948                 :          0 :     PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
         949                 :            :
         950                 :          0 :     alloc.ctx = &allocators.obj;
         951                 :          0 :     PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj);
         952                 :          0 :     PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
         953                 :            :
         954                 :            :     /* everything is ready: start tracing Python memory allocations */
         955                 :          0 :     tracemalloc_config.tracing = 1;
         956                 :            :
         957                 :          0 :     return 0;
         958                 :            : }
         959                 :            :
         960                 :            :
         961                 :            : static void
         962                 :          1 : tracemalloc_stop(void)
         963                 :            : {
         964         [ +  - ]:          1 :     if (!tracemalloc_config.tracing)
         965                 :          1 :         return;
         966                 :            :
         967                 :            :     /* stop tracing Python memory allocations */
         968                 :          0 :     tracemalloc_config.tracing = 0;
         969                 :            :
         970                 :            :     /* unregister the hook on memory allocators */
         971                 :            : #ifdef TRACE_RAW_MALLOC
         972                 :          0 :     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
         973                 :            : #endif
         974                 :          0 :     PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem);
         975                 :          0 :     PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj);
         976                 :            :
         977                 :          0 :     tracemalloc_clear_traces();
         978                 :            :
         979                 :            :     /* release memory */
         980                 :          0 :     raw_free(tracemalloc_traceback);
         981                 :          0 :     tracemalloc_traceback = NULL;
         982                 :            : }
         983                 :            :
         984                 :            :
         985                 :            :
         986                 :            : /*[clinic input]
         987                 :            : _tracemalloc.is_tracing
         988                 :            :
         989                 :            : Return True if the tracemalloc module is tracing Python memory allocations.
         990                 :            : [clinic start generated code]*/
         991                 :            :
         992                 :            : static PyObject *
         993                 :          0 : _tracemalloc_is_tracing_impl(PyObject *module)
         994                 :            : /*[clinic end generated code: output=2d763b42601cd3ef input=af104b0a00192f63]*/
         995                 :            : {
         996                 :          0 :     return PyBool_FromLong(tracemalloc_config.tracing);
         997                 :            : }
         998                 :            :
         999                 :            :
        1000                 :            : /*[clinic input]
        1001                 :            : _tracemalloc.clear_traces
        1002                 :            :
        1003                 :            : Clear traces of memory blocks allocated by Python.
        1004                 :            : [clinic start generated code]*/
        1005                 :            :
        1006                 :            : static PyObject *
        1007                 :          0 : _tracemalloc_clear_traces_impl(PyObject *module)
        1008                 :            : /*[clinic end generated code: output=a86080ee41b84197 input=0dab5b6c785183a5]*/
        1009                 :            : {
        1010         [ #  # ]:          0 :     if (!tracemalloc_config.tracing)
        1011                 :          0 :         Py_RETURN_NONE;
        1012                 :            :
        1013                 :          0 :     set_reentrant(1);
        1014                 :          0 :     tracemalloc_clear_traces();
        1015                 :          0 :     set_reentrant(0);
        1016                 :            :
        1017                 :          0 :     Py_RETURN_NONE;
        1018                 :            : }
        1019                 :            :
        1020                 :            :
        1021                 :            : static PyObject*
        1022                 :          0 : frame_to_pyobject(frame_t *frame)
        1023                 :            : {
        1024                 :            :     PyObject *frame_obj, *lineno_obj;
        1025                 :            :
        1026                 :          0 :     frame_obj = PyTuple_New(2);
        1027         [ #  # ]:          0 :     if (frame_obj == NULL)
        1028                 :          0 :         return NULL;
        1029                 :            :
        1030                 :          0 :     PyTuple_SET_ITEM(frame_obj, 0, Py_NewRef(frame->filename));
        1031                 :            :
        1032                 :          0 :     lineno_obj = PyLong_FromUnsignedLong(frame->lineno);
        1033         [ #  # ]:          0 :     if (lineno_obj == NULL) {
        1034                 :          0 :         Py_DECREF(frame_obj);
        1035                 :          0 :         return NULL;
        1036                 :            :     }
        1037                 :          0 :     PyTuple_SET_ITEM(frame_obj, 1, lineno_obj);
        1038                 :            :
        1039                 :          0 :     return frame_obj;
        1040                 :            : }
        1041                 :            :
        1042                 :            :
        1043                 :            : static PyObject*
        1044                 :          0 : traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table)
        1045                 :            : {
        1046                 :            :     PyObject *frames;
        1047                 :            :
        1048         [ #  # ]:          0 :     if (intern_table != NULL) {
        1049                 :          0 :         frames = _Py_hashtable_get(intern_table, (const void *)traceback);
        1050         [ #  # ]:          0 :         if (frames) {
        1051                 :          0 :             return Py_NewRef(frames);
        1052                 :            :         }
        1053                 :            :     }
        1054                 :            :
        1055                 :          0 :     frames = PyTuple_New(traceback->nframe);
        1056         [ #  # ]:          0 :     if (frames == NULL)
        1057                 :          0 :         return NULL;
        1058                 :            :
        1059         [ #  # ]:          0 :     for (int i=0; i < traceback->nframe; i++) {
        1060                 :          0 :         PyObject *frame = frame_to_pyobject(&traceback->frames[i]);
        1061         [ #  # ]:          0 :         if (frame == NULL) {
        1062                 :          0 :             Py_DECREF(frames);
        1063                 :          0 :             return NULL;
        1064                 :            :         }
        1065                 :          0 :         PyTuple_SET_ITEM(frames, i, frame);
        1066                 :            :     }
        1067                 :            :
        1068         [ #  # ]:          0 :     if (intern_table != NULL) {
        1069         [ #  # ]:          0 :         if (_Py_hashtable_set(intern_table, traceback, frames) < 0) {
        1070                 :          0 :             Py_DECREF(frames);
        1071                 :          0 :             PyErr_NoMemory();
        1072                 :          0 :             return NULL;
        1073                 :            :         }
        1074                 :            :         /* intern_table keeps a new reference to frames */
        1075                 :          0 :         Py_INCREF(frames);
        1076                 :            :     }
        1077                 :          0 :     return frames;
        1078                 :            : }
        1079                 :            :
        1080                 :            :
        1081                 :            : static PyObject*
        1082                 :          0 : trace_to_pyobject(unsigned int domain, const trace_t *trace,
        1083                 :            :                   _Py_hashtable_t *intern_tracebacks)
        1084                 :            : {
        1085                 :          0 :     PyObject *trace_obj = NULL;
        1086                 :            :     PyObject *obj;
        1087                 :            :
        1088                 :          0 :     trace_obj = PyTuple_New(4);
        1089         [ #  # ]:          0 :     if (trace_obj == NULL)
        1090                 :          0 :         return NULL;
        1091                 :            :
        1092                 :          0 :     obj = PyLong_FromSize_t(domain);
        1093         [ #  # ]:          0 :     if (obj == NULL) {
        1094                 :          0 :         Py_DECREF(trace_obj);
        1095                 :          0 :         return NULL;
        1096                 :            :     }
        1097                 :          0 :     PyTuple_SET_ITEM(trace_obj, 0, obj);
        1098                 :            :
        1099                 :          0 :     obj = PyLong_FromSize_t(trace->size);
        1100         [ #  # ]:          0 :     if (obj == NULL) {
        1101                 :          0 :         Py_DECREF(trace_obj);
        1102                 :          0 :         return NULL;
        1103                 :            :     }
        1104                 :          0 :     PyTuple_SET_ITEM(trace_obj, 1, obj);
        1105                 :            :
        1106                 :          0 :     obj = traceback_to_pyobject(trace->traceback, intern_tracebacks);
        1107         [ #  # ]:          0 :     if (obj == NULL) {
        1108                 :          0 :         Py_DECREF(trace_obj);
        1109                 :          0 :         return NULL;
        1110                 :            :     }
        1111                 :          0 :     PyTuple_SET_ITEM(trace_obj, 2, obj);
        1112                 :            :
        1113                 :          0 :     obj = PyLong_FromUnsignedLong(trace->traceback->total_nframe);
        1114         [ #  # ]:          0 :     if (obj == NULL) {
        1115                 :          0 :         Py_DECREF(trace_obj);
        1116                 :          0 :         return NULL;
        1117                 :            :     }
        1118                 :          0 :     PyTuple_SET_ITEM(trace_obj, 3, obj);
        1119                 :            :
        1120                 :          0 :     return trace_obj;
        1121                 :            : }
        1122                 :            :
        1123                 :            :
        1124                 :            : typedef struct {
        1125                 :            :     _Py_hashtable_t *traces;
        1126                 :            :     _Py_hashtable_t *domains;
        1127                 :            :     _Py_hashtable_t *tracebacks;
        1128                 :            :     PyObject *list;
        1129                 :            :     unsigned int domain;
        1130                 :            : } get_traces_t;
        1131                 :            :
        1132                 :            :
        1133                 :            : static int
        1134                 :          0 : tracemalloc_copy_trace(_Py_hashtable_t *traces,
        1135                 :            :                        const void *key, const void *value,
        1136                 :            :                        void *user_data)
        1137                 :            : {
        1138                 :          0 :     _Py_hashtable_t *traces2 = (_Py_hashtable_t *)user_data;
        1139                 :            :
        1140                 :          0 :     trace_t *trace = (trace_t *)value;
        1141                 :            :
        1142                 :          0 :     trace_t *trace2 = raw_malloc(sizeof(trace_t));
        1143         [ #  # ]:          0 :     if (trace2 == NULL) {
        1144                 :          0 :         return -1;
        1145                 :            :     }
        1146                 :          0 :     *trace2 = *trace;
        1147         [ #  # ]:          0 :     if (_Py_hashtable_set(traces2, key, trace2) < 0) {
        1148                 :          0 :         raw_free(trace2);
        1149                 :          0 :         return -1;
        1150                 :            :     }
        1151                 :          0 :     return 0;
        1152                 :            : }
        1153                 :            :
        1154                 :            :
        1155                 :            : static _Py_hashtable_t*
        1156                 :          0 : tracemalloc_copy_traces(_Py_hashtable_t *traces)
        1157                 :            : {
        1158                 :          0 :     _Py_hashtable_t *traces2 = tracemalloc_create_traces_table();
        1159         [ #  # ]:          0 :     if (traces2 == NULL) {
        1160                 :          0 :         return NULL;
        1161                 :            :     }
        1162                 :            :
        1163                 :          0 :     int err = _Py_hashtable_foreach(traces,
        1164                 :            :                                     tracemalloc_copy_trace,
        1165                 :            :                                     traces2);
        1166         [ #  # ]:          0 :     if (err) {
        1167                 :          0 :         _Py_hashtable_destroy(traces2);
        1168                 :          0 :         return NULL;
        1169                 :            :     }
        1170                 :          0 :     return traces2;
        1171                 :            : }
        1172                 :            :
        1173                 :            :
        1174                 :            : static int
        1175                 :          0 : tracemalloc_copy_domain(_Py_hashtable_t *domains,
        1176                 :            :                         const void *key, const void *value,
        1177                 :            :                         void *user_data)
        1178                 :            : {
        1179                 :          0 :     _Py_hashtable_t *domains2 = (_Py_hashtable_t *)user_data;
        1180                 :            :
        1181                 :          0 :     unsigned int domain = (unsigned int)FROM_PTR(key);
        1182                 :          0 :     _Py_hashtable_t *traces = (_Py_hashtable_t *)value;
        1183                 :            :
        1184                 :          0 :     _Py_hashtable_t *traces2 = tracemalloc_copy_traces(traces);
        1185         [ #  # ]:          0 :     if (traces2 == NULL) {
        1186                 :          0 :         return -1;
        1187                 :            :     }
        1188         [ #  # ]:          0 :     if (_Py_hashtable_set(domains2, TO_PTR(domain), traces2) < 0) {
        1189                 :          0 :         _Py_hashtable_destroy(traces2);
        1190                 :          0 :         return -1;
        1191                 :            :     }
        1192                 :          0 :     return 0;
        1193                 :            : }
        1194                 :            :
        1195                 :            :
        1196                 :            : static _Py_hashtable_t*
        1197                 :          0 : tracemalloc_copy_domains(_Py_hashtable_t *domains)
        1198                 :            : {
        1199                 :          0 :     _Py_hashtable_t *domains2 = tracemalloc_create_domains_table();
        1200         [ #  # ]:          0 :     if (domains2 == NULL) {
        1201                 :          0 :         return NULL;
        1202                 :            :     }
        1203                 :            :
        1204                 :          0 :     int err = _Py_hashtable_foreach(domains,
        1205                 :            :                                     tracemalloc_copy_domain,
        1206                 :            :                                     domains2);
        1207         [ #  # ]:          0 :     if (err) {
        1208                 :          0 :         _Py_hashtable_destroy(domains2);
        1209                 :          0 :         return NULL;
        1210                 :            :     }
        1211                 :          0 :     return domains2;
        1212                 :            : }
        1213                 :            :
        1214                 :            :
        1215                 :            : static int
        1216                 :          0 : tracemalloc_get_traces_fill(_Py_hashtable_t *traces,
        1217                 :            :                             const void *key, const void *value,
        1218                 :            :                             void *user_data)
        1219                 :            : {
        1220                 :          0 :     get_traces_t *get_traces = user_data;
        1221                 :            :
        1222                 :          0 :     const trace_t *trace = (const trace_t *)value;
        1223                 :            :
        1224                 :          0 :     PyObject *tuple = trace_to_pyobject(get_traces->domain, trace,
        1225                 :            :                                         get_traces->tracebacks);
        1226         [ #  # ]:          0 :     if (tuple == NULL) {
        1227                 :          0 :         return 1;
        1228                 :            :     }
        1229                 :            :
        1230                 :          0 :     int res = PyList_Append(get_traces->list, tuple);
        1231                 :          0 :     Py_DECREF(tuple);
        1232         [ #  # ]:          0 :     if (res < 0) {
        1233                 :          0 :         return 1;
        1234                 :            :     }
        1235                 :            :
        1236                 :          0 :     return 0;
        1237                 :            : }
        1238                 :            :
        1239                 :            :
        1240                 :            : static int
        1241                 :          0 : tracemalloc_get_traces_domain(_Py_hashtable_t *domains,
        1242                 :            :                               const void *key, const void *value,
        1243                 :            :                               void *user_data)
        1244                 :            : {
        1245                 :          0 :     get_traces_t *get_traces = user_data;
        1246                 :            :
        1247                 :          0 :     unsigned int domain = (unsigned int)FROM_PTR(key);
        1248                 :          0 :     _Py_hashtable_t *traces = (_Py_hashtable_t *)value;
        1249                 :            :
        1250                 :          0 :     get_traces->domain = domain;
        1251                 :          0 :     return _Py_hashtable_foreach(traces,
        1252                 :            :                                  tracemalloc_get_traces_fill,
        1253                 :            :                                  get_traces);
        1254                 :            : }
        1255                 :            :
        1256                 :            :
        1257                 :            : static void
        1258                 :          0 : tracemalloc_pyobject_decref(void *value)
        1259                 :            : {
        1260                 :          0 :     PyObject *obj = (PyObject *)value;
        1261                 :          0 :     Py_DECREF(obj);
        1262                 :          0 : }
        1263                 :            :
        1264                 :            :
        1265                 :            :
        1266                 :            : /*[clinic input]
        1267                 :            : _tracemalloc._get_traces
        1268                 :            :
        1269                 :            : Get traces of all memory blocks allocated by Python.
        1270                 :            :
        1271                 :            : Return a list of (size: int, traceback: tuple) tuples.
        1272                 :            : traceback is a tuple of (filename: str, lineno: int) tuples.
        1273                 :            :
        1274                 :            : Return an empty list if the tracemalloc module is disabled.
        1275                 :            : [clinic start generated code]*/
        1276                 :            :
        1277                 :            : static PyObject *
        1278                 :          0 : _tracemalloc__get_traces_impl(PyObject *module)
        1279                 :            : /*[clinic end generated code: output=e9929876ced4b5cc input=6c7d2230b24255aa]*/
        1280                 :            : {
        1281                 :            :     get_traces_t get_traces;
        1282                 :          0 :     get_traces.domain = DEFAULT_DOMAIN;
        1283                 :          0 :     get_traces.traces = NULL;
        1284                 :          0 :     get_traces.domains = NULL;
        1285                 :          0 :     get_traces.tracebacks = NULL;
        1286                 :          0 :     get_traces.list = PyList_New(0);
        1287         [ #  # ]:          0 :     if (get_traces.list == NULL)
        1288                 :          0 :         goto error;
        1289                 :            :
        1290         [ #  # ]:          0 :     if (!tracemalloc_config.tracing)
        1291                 :          0 :         return get_traces.list;
        1292                 :            :
        1293                 :            :     /* the traceback hash table is used temporarily to intern traceback tuple
        1294                 :            :        of (filename, lineno) tuples */
        1295                 :          0 :     get_traces.tracebacks = hashtable_new(_Py_hashtable_hash_ptr,
        1296                 :            :                                           _Py_hashtable_compare_direct,
        1297                 :            :                                           NULL, tracemalloc_pyobject_decref);
        1298         [ #  # ]:          0 :     if (get_traces.tracebacks == NULL) {
        1299                 :          0 :         goto no_memory;
        1300                 :            :     }
        1301                 :            :
        1302                 :            :     // Copy all traces so tracemalloc_get_traces_fill() doesn't have to disable
        1303                 :            :     // temporarily tracemalloc which would impact other threads and so would
        1304                 :            :     // miss allocations while get_traces() is called.
        1305                 :          0 :     TABLES_LOCK();
        1306                 :          0 :     get_traces.traces = tracemalloc_copy_traces(tracemalloc_traces);
        1307                 :          0 :     TABLES_UNLOCK();
        1308                 :            :
        1309         [ #  # ]:          0 :     if (get_traces.traces == NULL) {
        1310                 :          0 :         goto no_memory;
        1311                 :            :     }
        1312                 :            :
        1313                 :          0 :     TABLES_LOCK();
        1314                 :          0 :     get_traces.domains = tracemalloc_copy_domains(tracemalloc_domains);
        1315                 :          0 :     TABLES_UNLOCK();
        1316                 :            :
        1317         [ #  # ]:          0 :     if (get_traces.domains == NULL) {
        1318                 :          0 :         goto no_memory;
        1319                 :            :     }
        1320                 :            :
        1321                 :            :     // Convert traces to a list of tuples
        1322                 :          0 :     set_reentrant(1);
        1323                 :          0 :     int err = _Py_hashtable_foreach(get_traces.traces,
        1324                 :            :                                     tracemalloc_get_traces_fill,
        1325                 :            :                                     &get_traces);
        1326         [ #  # ]:          0 :     if (!err) {
        1327                 :          0 :         err = _Py_hashtable_foreach(get_traces.domains,
        1328                 :            :                                     tracemalloc_get_traces_domain,
        1329                 :            :                                     &get_traces);
        1330                 :            :     }
        1331                 :          0 :     set_reentrant(0);
        1332         [ #  # ]:          0 :     if (err) {
        1333                 :          0 :         goto error;
        1334                 :            :     }
        1335                 :            :
        1336                 :          0 :     goto finally;
        1337                 :            :
        1338                 :          0 : no_memory:
        1339                 :          0 :     PyErr_NoMemory();
        1340                 :            :
        1341                 :          0 : error:
        1342         [ #  # ]:          0 :     Py_CLEAR(get_traces.list);
        1343                 :            :
        1344                 :          0 : finally:
        1345         [ #  # ]:          0 :     if (get_traces.tracebacks != NULL) {
        1346                 :          0 :         _Py_hashtable_destroy(get_traces.tracebacks);
        1347                 :            :     }
        1348         [ #  # ]:          0 :     if (get_traces.traces != NULL) {
        1349                 :          0 :         _Py_hashtable_destroy(get_traces.traces);
        1350                 :            :     }
        1351         [ #  # ]:          0 :     if (get_traces.domains != NULL) {
        1352                 :          0 :         _Py_hashtable_destroy(get_traces.domains);
        1353                 :            :     }
        1354                 :            :
        1355                 :          0 :     return get_traces.list;
        1356                 :            : }
        1357                 :            :
        1358                 :            :
        1359                 :            : static traceback_t*
        1360                 :          0 : tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr)
        1361                 :            : {
        1362                 :            :
        1363         [ #  # ]:          0 :     if (!tracemalloc_config.tracing)
        1364                 :          0 :         return NULL;
        1365                 :            :
        1366                 :            :     trace_t *trace;
        1367                 :          0 :     TABLES_LOCK();
        1368                 :          0 :     _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
        1369         [ #  # ]:          0 :     if (traces) {
        1370                 :          0 :         trace = _Py_hashtable_get(traces, TO_PTR(ptr));
        1371                 :            :     }
        1372                 :            :     else {
        1373                 :          0 :         trace = NULL;
        1374                 :            :     }
        1375                 :          0 :     TABLES_UNLOCK();
        1376                 :            :
        1377         [ #  # ]:          0 :     if (!trace) {
        1378                 :          0 :         return NULL;
        1379                 :            :     }
        1380                 :            :
        1381                 :          0 :     return trace->traceback;
        1382                 :            : }
        1383                 :            :
        1384                 :            :
        1385                 :            :
        1386                 :            : /*[clinic input]
        1387                 :            : _tracemalloc._get_object_traceback
        1388                 :            :
        1389                 :            :     obj: object
        1390                 :            :     /
        1391                 :            :
        1392                 :            : Get the traceback where the Python object obj was allocated.
        1393                 :            :
        1394                 :            : Return a tuple of (filename: str, lineno: int) tuples.
        1395                 :            : Return None if the tracemalloc module is disabled or did not
        1396                 :            : trace the allocation of the object.
        1397                 :            : [clinic start generated code]*/
        1398                 :            :
        1399                 :            : static PyObject *
        1400                 :          0 : _tracemalloc__get_object_traceback(PyObject *module, PyObject *obj)
        1401                 :            : /*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/
        1402                 :            : {
        1403                 :            :     PyTypeObject *type;
        1404                 :            :     traceback_t *traceback;
        1405                 :            :
        1406                 :          0 :     type = Py_TYPE(obj);
        1407                 :          0 :     const size_t presize = _PyType_PreHeaderSize(type);
        1408                 :          0 :     uintptr_t ptr = (uintptr_t)((char *)obj - presize);
        1409                 :            :
        1410                 :          0 :     traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, ptr);
        1411         [ #  # ]:          0 :     if (traceback == NULL) {
        1412                 :          0 :         Py_RETURN_NONE;
        1413                 :            :     }
        1414                 :            :
        1415                 :          0 :     return traceback_to_pyobject(traceback, NULL);
        1416                 :            : }
        1417                 :            :
        1418                 :            :
        1419                 :            : #define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str))
        1420                 :            :
        1421                 :            : static void
        1422                 :          0 : _PyMem_DumpFrame(int fd, frame_t * frame)
        1423                 :            : {
        1424                 :          0 :     PUTS(fd, "  File \"");
        1425                 :          0 :     _Py_DumpASCII(fd, frame->filename);
        1426                 :          0 :     PUTS(fd, "\", line ");
        1427                 :          0 :     _Py_DumpDecimal(fd, frame->lineno);
        1428                 :          0 :     PUTS(fd, "\n");
        1429                 :          0 : }
        1430                 :            :
        1431                 :            : /* Dump the traceback where a memory block was allocated into file descriptor
        1432                 :            :    fd. The function may block on TABLES_LOCK() but it is unlikely. */
        1433                 :            : void
        1434                 :          0 : _PyMem_DumpTraceback(int fd, const void *ptr)
        1435                 :            : {
        1436                 :            :     traceback_t *traceback;
        1437                 :            :     int i;
        1438                 :            :
        1439         [ #  # ]:          0 :     if (!tracemalloc_config.tracing) {
        1440                 :          0 :         PUTS(fd, "Enable tracemalloc to get the memory block "
        1441                 :            :                  "allocation traceback\n\n");
        1442                 :          0 :         return;
        1443                 :            :     }
        1444                 :            :
        1445                 :          0 :     traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr);
        1446         [ #  # ]:          0 :     if (traceback == NULL)
        1447                 :          0 :         return;
        1448                 :            :
        1449                 :          0 :     PUTS(fd, "Memory block allocated at (most recent call first):\n");
        1450         [ #  # ]:          0 :     for (i=0; i < traceback->nframe; i++) {
        1451                 :          0 :         _PyMem_DumpFrame(fd, &traceback->frames[i]);
        1452                 :            :     }
        1453                 :          0 :     PUTS(fd, "\n");
        1454                 :            : }
        1455                 :            :
        1456                 :            : #undef PUTS
        1457                 :            :
        1458                 :            :
        1459                 :            :
        1460                 :            : /*[clinic input]
        1461                 :            : _tracemalloc.start
        1462                 :            :
        1463                 :            :     nframe: int = 1
        1464                 :            :     /
        1465                 :            :
        1466                 :            : Start tracing Python memory allocations.
        1467                 :            :
        1468                 :            : Also set the maximum number of frames stored in the traceback of a
        1469                 :            : trace to nframe.
        1470                 :            : [clinic start generated code]*/
        1471                 :            :
        1472                 :            : static PyObject *
        1473                 :          0 : _tracemalloc_start_impl(PyObject *module, int nframe)
        1474                 :            : /*[clinic end generated code: output=caae05c23c159d3c input=40d849b5b29d1933]*/
        1475                 :            : {
        1476         [ #  # ]:          0 :     if (tracemalloc_start(nframe) < 0) {
        1477                 :          0 :         return NULL;
        1478                 :            :     }
        1479                 :          0 :     Py_RETURN_NONE;
        1480                 :            : }
        1481                 :            :
        1482                 :            :
        1483                 :            : /*[clinic input]
        1484                 :            : _tracemalloc.stop
        1485                 :            :
        1486                 :            : Stop tracing Python memory allocations.
        1487                 :            :
        1488                 :            : Also clear traces of memory blocks allocated by Python.
        1489                 :            : [clinic start generated code]*/
        1490                 :            :
        1491                 :            : static PyObject *
        1492                 :          0 : _tracemalloc_stop_impl(PyObject *module)
        1493                 :            : /*[clinic end generated code: output=c3c42ae03e3955cd input=7478f075e51dae18]*/
        1494                 :            : {
        1495                 :          0 :     tracemalloc_stop();
        1496                 :          0 :     Py_RETURN_NONE;
        1497                 :            : }
        1498                 :            :
        1499                 :            :
        1500                 :            : /*[clinic input]
        1501                 :            : _tracemalloc.get_traceback_limit
        1502                 :            :
        1503                 :            : Get the maximum number of frames stored in the traceback of a trace.
        1504                 :            :
        1505                 :            : By default, a trace of an allocated memory block only stores
        1506                 :            : the most recent frame: the limit is 1.
        1507                 :            : [clinic start generated code]*/
        1508                 :            :
        1509                 :            : static PyObject *
        1510                 :          0 : _tracemalloc_get_traceback_limit_impl(PyObject *module)
        1511                 :            : /*[clinic end generated code: output=d556d9306ba95567 input=da3cd977fc68ae3b]*/
        1512                 :            : {
        1513                 :          0 :     return PyLong_FromLong(tracemalloc_config.max_nframe);
        1514                 :            : }
        1515                 :            :
        1516                 :            :
        1517                 :            : static int
        1518                 :          0 : tracemalloc_get_tracemalloc_memory_cb(_Py_hashtable_t *domains,
        1519                 :            :                                       const void *key, const void *value,
        1520                 :            :                                       void *user_data)
        1521                 :            : {
        1522                 :          0 :     const _Py_hashtable_t *traces = value;
        1523                 :          0 :     size_t *size = (size_t*)user_data;
        1524                 :          0 :     *size += _Py_hashtable_size(traces);
        1525                 :          0 :     return 0;
        1526                 :            : }
        1527                 :            :
        1528                 :            :
        1529                 :            : /*[clinic input]
        1530                 :            : _tracemalloc.get_tracemalloc_memory
        1531                 :            :
        1532                 :            : Get the memory usage in bytes of the tracemalloc module.
        1533                 :            :
        1534                 :            : This memory is used internally to trace memory allocations.
        1535                 :            : [clinic start generated code]*/
        1536                 :            :
        1537                 :            : static PyObject *
        1538                 :          0 : _tracemalloc_get_tracemalloc_memory_impl(PyObject *module)
        1539                 :            : /*[clinic end generated code: output=e3f14e280a55f5aa input=5d919c0f4d5132ad]*/
        1540                 :            : {
        1541                 :            :     size_t size;
        1542                 :            :
        1543                 :          0 :     size = _Py_hashtable_size(tracemalloc_tracebacks);
        1544                 :          0 :     size += _Py_hashtable_size(tracemalloc_filenames);
        1545                 :            :
        1546                 :          0 :     TABLES_LOCK();
        1547                 :          0 :     size += _Py_hashtable_size(tracemalloc_traces);
        1548                 :          0 :     _Py_hashtable_foreach(tracemalloc_domains,
        1549                 :            :                           tracemalloc_get_tracemalloc_memory_cb, &size);
        1550                 :          0 :     TABLES_UNLOCK();
        1551                 :            :
        1552                 :          0 :     return PyLong_FromSize_t(size);
        1553                 :            : }
        1554                 :            :
        1555                 :            :
        1556                 :            :
        1557                 :            : /*[clinic input]
        1558                 :            : _tracemalloc.get_traced_memory
        1559                 :            :
        1560                 :            : Get the current size and peak size of memory blocks traced by tracemalloc.
        1561                 :            :
        1562                 :            : Returns a tuple: (current: int, peak: int).
        1563                 :            : [clinic start generated code]*/
        1564                 :            :
        1565                 :            : static PyObject *
        1566                 :          0 : _tracemalloc_get_traced_memory_impl(PyObject *module)
        1567                 :            : /*[clinic end generated code: output=5b167189adb9e782 input=61ddb5478400ff66]*/
        1568                 :            : {
        1569                 :            :     Py_ssize_t size, peak_size;
        1570                 :            :
        1571         [ #  # ]:          0 :     if (!tracemalloc_config.tracing)
        1572                 :          0 :         return Py_BuildValue("ii", 0, 0);
        1573                 :            :
        1574                 :          0 :     TABLES_LOCK();
        1575                 :          0 :     size = tracemalloc_traced_memory;
        1576                 :          0 :     peak_size = tracemalloc_peak_traced_memory;
        1577                 :          0 :     TABLES_UNLOCK();
        1578                 :            :
        1579                 :          0 :     return Py_BuildValue("nn", size, peak_size);
        1580                 :            : }
        1581                 :            :
        1582                 :            : /*[clinic input]
        1583                 :            : _tracemalloc.reset_peak
        1584                 :            :
        1585                 :            : Set the peak size of memory blocks traced by tracemalloc to the current size.
        1586                 :            :
        1587                 :            : Do nothing if the tracemalloc module is not tracing memory allocations.
        1588                 :            :
        1589                 :            : [clinic start generated code]*/
        1590                 :            :
        1591                 :            : static PyObject *
        1592                 :          0 : _tracemalloc_reset_peak_impl(PyObject *module)
        1593                 :            : /*[clinic end generated code: output=140c2870f691dbb2 input=18afd0635066e9ce]*/
        1594                 :            : {
        1595         [ #  # ]:          0 :     if (!tracemalloc_config.tracing) {
        1596                 :          0 :         Py_RETURN_NONE;
        1597                 :            :     }
        1598                 :            :
        1599                 :          0 :     TABLES_LOCK();
        1600                 :          0 :     tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
        1601                 :          0 :     TABLES_UNLOCK();
        1602                 :            :
        1603                 :          0 :     Py_RETURN_NONE;
        1604                 :            : }
        1605                 :            :
        1606                 :            :
        1607                 :            : static PyMethodDef module_methods[] = {
        1608                 :            :     _TRACEMALLOC_IS_TRACING_METHODDEF
        1609                 :            :     _TRACEMALLOC_CLEAR_TRACES_METHODDEF
        1610                 :            :     _TRACEMALLOC__GET_TRACES_METHODDEF
        1611                 :            :     _TRACEMALLOC__GET_OBJECT_TRACEBACK_METHODDEF
        1612                 :            :     _TRACEMALLOC_START_METHODDEF
        1613                 :            :     _TRACEMALLOC_STOP_METHODDEF
        1614                 :            :     _TRACEMALLOC_GET_TRACEBACK_LIMIT_METHODDEF
        1615                 :            :     _TRACEMALLOC_GET_TRACEMALLOC_MEMORY_METHODDEF
        1616                 :            :     _TRACEMALLOC_GET_TRACED_MEMORY_METHODDEF
        1617                 :            :     _TRACEMALLOC_RESET_PEAK_METHODDEF
        1618                 :            :     /* sentinel */
        1619                 :            :     {NULL, NULL}
        1620                 :            : };
        1621                 :            :
        1622                 :            : PyDoc_STRVAR(module_doc,
        1623                 :            : "Debug module to trace memory blocks allocated by Python.");
        1624                 :            :
        1625                 :            : static struct PyModuleDef module_def = {
        1626                 :            :     PyModuleDef_HEAD_INIT,
        1627                 :            :     "_tracemalloc",
        1628                 :            :     module_doc,
        1629                 :            :     0, /* non-negative size to be able to unload the module */
        1630                 :            :     module_methods,
        1631                 :            :     NULL,
        1632                 :            : };
        1633                 :            :
        1634                 :            : PyMODINIT_FUNC
        1635                 :          1 : PyInit__tracemalloc(void)
        1636                 :            : {
        1637                 :            :     PyObject *m;
        1638                 :          1 :     m = PyModule_Create(&module_def);
        1639         [ -  + ]:          1 :     if (m == NULL)
        1640                 :          0 :         return NULL;
        1641                 :            :
        1642         [ -  + ]:          1 :     if (tracemalloc_init() < 0) {
        1643                 :          0 :         Py_DECREF(m);
        1644                 :          0 :         return NULL;
        1645                 :            :     }
        1646                 :            :
        1647                 :          1 :     return m;
        1648                 :            : }
        1649                 :            :
        1650                 :            :
        1651                 :            : int
        1652                 :         25 : _PyTraceMalloc_Init(int nframe)
        1653                 :            : {
        1654                 :            :     assert(PyGILState_Check());
        1655         [ +  - ]:         25 :     if (nframe == 0) {
        1656                 :         25 :         return 0;
        1657                 :            :     }
        1658                 :          0 :     return tracemalloc_start(nframe);
        1659                 :            : }
        1660                 :            :
        1661                 :            :
        1662                 :            : void
        1663                 :         25 : _PyTraceMalloc_Fini(void)
        1664                 :            : {
        1665                 :            :     assert(PyGILState_Check());
        1666                 :         25 :     tracemalloc_deinit();
        1667                 :         25 : }
        1668                 :            :
        1669                 :            : int
        1670                 :          0 : PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
        1671                 :            :                     size_t size)
        1672                 :            : {
        1673                 :            :     int res;
        1674                 :            :     PyGILState_STATE gil_state;
        1675                 :            :
        1676         [ #  # ]:          0 :     if (!tracemalloc_config.tracing) {
        1677                 :            :         /* tracemalloc is not tracing: do nothing */
        1678                 :          0 :         return -2;
        1679                 :            :     }
        1680                 :            :
        1681                 :          0 :     gil_state = PyGILState_Ensure();
        1682                 :            :
        1683                 :          0 :     TABLES_LOCK();
        1684                 :          0 :     res = tracemalloc_add_trace(domain, ptr, size);
        1685                 :          0 :     TABLES_UNLOCK();
        1686                 :            :
        1687                 :          0 :     PyGILState_Release(gil_state);
        1688                 :          0 :     return res;
        1689                 :            : }
        1690                 :            :
        1691                 :            :
        1692                 :            : int
        1693                 :          0 : PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
        1694                 :            : {
        1695         [ #  # ]:          0 :     if (!tracemalloc_config.tracing) {
        1696                 :            :         /* tracemalloc is not tracing: do nothing */
        1697                 :          0 :         return -2;
        1698                 :            :     }
        1699                 :            :
        1700                 :          0 :     TABLES_LOCK();
        1701                 :          0 :     tracemalloc_remove_trace(domain, ptr);
        1702                 :          0 :     TABLES_UNLOCK();
        1703                 :            :
        1704                 :          0 :     return 0;
        1705                 :            : }
        1706                 :            :
        1707                 :            :
        1708                 :            : /* If the object memory block is already traced, update its trace
        1709                 :            :    with the current Python traceback.
        1710                 :            :
        1711                 :            :    Do nothing if tracemalloc is not tracing memory allocations
        1712                 :            :    or if the object memory block is not already traced. */
        1713                 :            : int
        1714                 :          0 : _PyTraceMalloc_NewReference(PyObject *op)
        1715                 :            : {
        1716                 :            :     assert(PyGILState_Check());
        1717                 :            :
        1718         [ #  # ]:          0 :     if (!tracemalloc_config.tracing) {
        1719                 :            :         /* tracemalloc is not tracing: do nothing */
        1720                 :          0 :         return -1;
        1721                 :            :     }
        1722                 :            :
        1723                 :          0 :     PyTypeObject *type = Py_TYPE(op);
        1724                 :          0 :     const size_t presize = _PyType_PreHeaderSize(type);
        1725                 :          0 :     uintptr_t ptr = (uintptr_t)((char *)op - presize);
        1726                 :            :
        1727                 :          0 :     int res = -1;
        1728                 :            :
        1729                 :          0 :     TABLES_LOCK();
        1730                 :          0 :     trace_t *trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr));
        1731         [ #  # ]:          0 :     if (trace != NULL) {
        1732                 :            :         /* update the traceback of the memory block */
        1733                 :          0 :         traceback_t *traceback = traceback_new();
        1734         [ #  # ]:          0 :         if (traceback != NULL) {
        1735                 :          0 :             trace->traceback = traceback;
        1736                 :          0 :             res = 0;
        1737                 :            :         }
        1738                 :            :     }
        1739                 :            :     /* else: cannot track the object, its memory block size is unknown */
        1740                 :          0 :     TABLES_UNLOCK();
        1741                 :            :
        1742                 :          0 :     return res;
        1743                 :            : }
        1744                 :            :
        1745                 :            :
        1746                 :            : PyObject*
        1747                 :          0 : _PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
        1748                 :            : {
        1749                 :            :     traceback_t *traceback;
        1750                 :            :
        1751                 :          0 :     traceback = tracemalloc_get_traceback(domain, ptr);
        1752         [ #  # ]:          0 :     if (traceback == NULL)
        1753                 :          0 :         Py_RETURN_NONE;
        1754                 :            :
        1755                 :          0 :     return traceback_to_pyobject(traceback, NULL);
        1756                 :            : }

Generated by: LCOV version 1.14