GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Isolate the tracemalloc Module Between Interpreters · Issue #101520 · python/cpython

Isolate the tracemalloc Module Between Interpreters #101520

New issue
Copy link
New issue
Copy link
Closed
Closed
Isolate the tracemalloc Module Between Interpreters#101520
Copy link
Labels
3.12only security fixesonly security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-subinterpreterstype-featureA feature request or enhancementA feature request or enhancement

Description

@ericsnowcurrently
ericsnowcurrently
opened on Feb 2, 2023
Issue body actions

(See #100227.)

Currently the tracemalloc module has some state in _PyRuntimeState, including objects, which is shared by all interpreters. Interpreters should be isolated from each other, for a variety of reasons (including the possibility of a per-interpreter GIL). Isolating the module will involve moving some of the state to PyInterpreterState (and, under per-interpreter GIL, guarding other state with a global lock).


Analysis:

(expand)

Allocators

The module installs a custom allocator (using the PEP 445 API
(docs).

(expand)

the allocator functions:

domain func wraps wraps (reentrant) actually wraps

-

tracemalloc_alloc() <original malloc() or calloc()> <---

-

tracemalloc_realloc() <original realloc() or free()> <---

-

tracemalloc_raw_alloc() tracemalloc_alloc() * <original malloc() or calloc()>

-

tracemalloc_alloc_gil() tracemalloc_alloc() <original malloc() or calloc()>
raw
tracemalloc_raw_malloc() tracemalloc_alloc() <original malloc()> tracemalloc_raw_alloc()
tracemalloc_raw_calloc() tracemalloc_alloc() <original calloc()> tracemalloc_raw_alloc()
tracemalloc_raw_realloc() tracemalloc_realloc() * <original realloc()>
tracemalloc_free() <original free()> <---
mem
obj
tracemalloc_malloc_gil() tracemalloc_alloc_gil() <---
tracemalloc_calloc_gil() tracemalloc_alloc_gil() <---
tracemalloc_realloc_gil() tracemalloc_realloc() <original realloc()>
tracemalloc_free() <original free()> <---

* Note that tracemalloc_raw_alloc() wraps the tracemalloc_alloc() call
with PyGILState_Ensure()/PyGILState_Release().
Likewise for tracemalloc_raw_realloc() where it calls tracemalloc_realloc().
In no other case does an allocator function use the GILState API for any calls.

State

Fields

(expand)

https://github.com/python/cpython/blob/main/Include/internal/pycore_tracemalloc.h#L57-L107
https://github.com/python/cpython/blob/main/Include/internal/pycore_runtime.h#L147

raw

cpython/Include/internal/pycore_tracemalloc.h

Lines 43 to 55 in 0675b8f

struct
#ifdef __GNUC__
__attribute__((packed))
#endif
tracemalloc_frame {
/* filename cannot be NULL: "<unknown>" is used if the Python frame
filename is NULL */
PyObject *filename;
unsigned int lineno;
};
#ifdef _MSC_VER
#pragma pack(pop)
#endif

cpython/Include/internal/pycore_tracemalloc.h

Lines 57 to 64 in 0675b8f

struct tracemalloc_traceback {
Py_uhash_t hash;
/* Number of frames stored */
uint16_t nframe;
/* Total number of frames the traceback had */
uint16_t total_nframe;
struct tracemalloc_frame frames[1];
};

cpython/Include/internal/pycore_tracemalloc.h

Lines 57 to 107 in 0675b8f

struct tracemalloc_traceback {
Py_uhash_t hash;
/* Number of frames stored */
uint16_t nframe;
/* Total number of frames the traceback had */
uint16_t total_nframe;
struct tracemalloc_frame frames[1];
};
struct _tracemalloc_runtime_state {
struct _PyTraceMalloc_Config config;
/* Protected by the GIL */
struct {
PyMemAllocatorEx mem;
PyMemAllocatorEx raw;
PyMemAllocatorEx obj;
} allocators;
#if defined(TRACE_RAW_MALLOC)
PyThread_type_lock tables_lock;
#endif
/* Size in bytes of currently traced memory.
Protected by TABLES_LOCK(). */
size_t traced_memory;
/* Peak size in bytes of traced memory.
Protected by TABLES_LOCK(). */
size_t peak_traced_memory;
/* Hash table used as a set to intern filenames:
PyObject* => PyObject*.
Protected by the GIL */
_Py_hashtable_t *filenames;
/* Buffer to store a new traceback in traceback_new().
Protected by the GIL. */
struct tracemalloc_traceback *traceback;
/* Hash table used as a set to intern tracebacks:
traceback_t* => traceback_t*
Protected by the GIL */
_Py_hashtable_t *tracebacks;
/* pointer (void*) => trace (trace_t*).
Protected by TABLES_LOCK(). */
_Py_hashtable_t *traces;
/* domain (unsigned int) => traces (_Py_hashtable_t).
Protected by TABLES_LOCK(). */
_Py_hashtable_t *domains;
struct tracemalloc_traceback empty_traceback;
Py_tss_t reentrant_key;
};

cpython/Modules/_tracemalloc.c

Lines 54 to 55 in 0675b8f

typedef struct tracemalloc_frame frame_t;
typedef struct tracemalloc_traceback traceback_t;

cpython/Modules/_tracemalloc.c

Lines 69 to 76 in 0675b8f

/* Trace of a memory block */
typedef struct {
/* Size of the memory block in bytes */
size_t size;
/* Traceback where the memory block was allocated */
traceback_t *traceback;
} trace_t;

name type protected by #ifdef notes
config struct _PyTraceMalloc_Config
    . initialized enum {} GIL
    . tracing bool GIL
    . max_nframe int GIL
allocators see PEP 445 (docs)
    . mem PyMemAllocatorEx GIL
    . raw PyMemAllocatorEx GIL
    . obj PyMemAllocatorEx GIL
tables_lock PyThread_type_lock GIL TRACE_RAW_MALLOC
traced_memory size_t tables_lock
peak_traced_memory size_t tables_lock
filenames _Py_hashtable_t * GIL interned; effectively a set of objects
traceback struct tracemalloc_traceback * GIL a temporary buffer
tracebacks _Py_hashtable_t * GIL interned; effectively a set of traceback_t
traces _Py_hashtable_t * tables_lock void-ptr -> trace_t
domains _Py_hashtable_t * tables_lock domain -> _Py_hashtable_t * (per-domain traces)
empty_traceback struct tracemalloc_traceback ???
reentrant_key Py_tss_t ???

notes:

  • each frame in struct tracemalloc_traceback holds a filename object
  • traceback_t is a typedef for struct tracemalloc_traceback
  • frame_t is a typedef for struct tracemalloc_frame

hold objects:

  • filenames
  • traceback (filename in each frame)
  • tracebacks (filename in each frame of each traceback)
  • traces (filename in each frame of each traceback)
  • domains (filename in each frame of each traceback in each domain)

Usage

(expand)

simple:

name context get set
config
    . initialized lifecycle tracemalloc_init()
tracemalloc_deinit()
tracemalloc_init()
tracemalloc_deinit()
    . tracing module _tracemalloc_is_tracing_impl()
_tracemalloc__get_traces_impl()
_tracemalloc_clear_traces_impl()
_tracemalloc_get_traceback_limit_impl()
_tracemalloc_get_traced_memory_impl()
_tracemalloc_reset_peak_impl()
C-API PyTraceMalloc_Track()
PyTraceMalloc_Untrack()
_PyTraceMalloc_NewReference()
_PyMem_DumpTraceback()
lifecycle tracemalloc_start()
tracemalloc_stop()
tracemalloc_start()
tracemalloc_stop()
internal tracemalloc_get_traceback()
    . max_nframe module _tracemalloc_get_traceback_limit_impl()
lifecycle tracemalloc_start()
internal traceback_get_frames()
allocators
    . mem lifecycle tracemalloc_start() +
tracemalloc_stop()
    . raw lifecycle tracemalloc_init() +
tracemalloc_start() +
tracemalloc_stop()
internal raw_malloc()
raw_free()
    . obj lifecycle tracemalloc_start() +
tracemalloc_stop()
tables_lock module _tracemalloc__get_traces_impl() +
_tracemalloc_get_tracemalloc_memory_impl() +
_tracemalloc_get_traced_memory_impl() +
_tracemalloc_reset_peak_impl() +
C-API PyTraceMalloc_Track() +
PyTraceMalloc_Untrack() +
_PyTraceMalloc_NewReference() +
lifecycle tracemalloc_init()
tracemalloc_deinit()
tracemalloc_init() *
tracemalloc_deinit() *
allocator tracemalloc_alloc() +
tracemalloc_realloc() +
tracemalloc_free() +
tracemalloc_realloc_gil() +
tracemalloc_raw_realloc() +
internal tracemalloc_get_traceback() +
tracemalloc_clear_traces() +
traced_memory module _tracemalloc_get_traced_memory_impl()
_tracemalloc_reset_peak_impl()
internal tracemalloc_add_trace() tracemalloc_add_trace()
tracemalloc_remove_trace()
tracemalloc_clear_traces()
peak_traced_memory module _tracemalloc_get_traced_memory_impl() _tracemalloc_reset_peak_impl()
internal tracemalloc_add_trace() tracemalloc_add_trace()
tracemalloc_clear_traces()
filenames module _tracemalloc_get_tracemalloc_memory_impl()
lifecycle tracemalloc_init() *
internal tracemalloc_get_frame()
tracemalloc_clear_traces() +
traceback lifecycle tracemalloc_stop() tracemalloc_start() *
tracemalloc_stop() *
internal traceback_new() +
tracebacks module _tracemalloc_get_tracemalloc_memory_impl()
lifecycle tracemalloc_init() *
tracemalloc_deinit() +
internal traceback_new() +
tracemalloc_clear_traces() +
traces module _tracemalloc__get_traces_impl()
_tracemalloc_get_tracemalloc_memory_impl()
C-API _PyTraceMalloc_NewReference()
lifecycle tracemalloc_deinit() + tracemalloc_init() *
internal tracemalloc_get_traces_table()
tracemalloc_add_trace() (indirect)
tracemalloc_remove_trace() (indirect)
tracemalloc_get_traceback() (indirect)
tracemalloc_clear_traces() +
domains module _tracemalloc__get_traces_impl()
_tracemalloc_get_tracemalloc_memory_impl()
lifecycle tracemalloc_deinit() + tracemalloc_init() *
internal tracemalloc_get_traces_table()
tracemalloc_remove_trace() (indirect)
tracemalloc_get_traceback() (indirect)
tracemalloc_clear_traces() +
tracemalloc_add_trace() +
empty_traceback lifecycle tracemalloc_init() +
internal traceback_new()
reentrant_key lifecycle tracemalloc_init() +
tracemalloc_deinit() +
allocator tracemalloc_alloc_gil() (indirect) +
tracemalloc_realloc_gil() (indirect) +
tracemalloc_raw_alloc() (indirect) +
tracemalloc_raw_realloc() (indirect) +
internal get_reentrant()
set_reentrant() +

* the function allocates/deallocates the value (see below)
+ the function mutates the value (see below)

simple (extraneous):

name context allocate/deallocate get (assert-only)
config.tracing internal tracemalloc_add_trace()
tracemalloc_remove_trace()
tables_lock lifecycle tracemalloc_init()
tracemalloc_deinit()
traced_memory internal tracemalloc_remove_trace()
filenames lifecycle tracemalloc_init()
traceback lifecycle tracemalloc_start()
tracemalloc_stop()
tracemalloc_start()
tracebacks lifecycle tracemalloc_init()
traces lifecycle tracemalloc_init()
domains lifecycle tracemalloc_init()

mutation of complex fields:

name context initialize finalize clear modify
allocators.mem lifecycle tracemalloc_start()
allocators.raw lifecycle tracemalloc_init()
tracemalloc_start()
allocators.obj lifecycle tracemalloc_start()
tables_lock module _tracemalloc__get_traces_impl()
_tracemalloc_get_tracemalloc_memory_impl()
_tracemalloc_get_traced_memory_impl()
_tracemalloc_reset_peak_impl()
C-API PyTraceMalloc_Track()
PyTraceMalloc_Untrack()
_PyTraceMalloc_NewReference()
allocator tracemalloc_alloc() tracemalloc_alloc()
tracemalloc_realloc()
tracemalloc_free()
tracemalloc_realloc_gil()
tracemalloc_raw_realloc()
internal tracemalloc_clear_traces()
tracemalloc_get_traceback()
filenames internal tracemalloc_clear_traces() tracemalloc_get_frame()
tracemalloc_clear_traces()
lifecycle tracemalloc_deinit()
traceback internal traceback_new()
tracebacks lifecycle tracemalloc_deinit()
internal tracemalloc_clear_traces() traceback_new()
traces lifecycle tracemalloc_deinit()
internal tracemalloc_clear_traces()
domains lifecycle tracemalloc_deinit()
internal tracemalloc_clear_traces() tracemalloc_add_trace()
reentrant_key lifecycle tracemalloc_init() tracemalloc_deinit()
internal set_reentrant()

indirection:

name context direct indirect
allocators.raw lifecycle tracemalloc_start()
tracemalloc_copy_trace()
raw_malloc()
tracemalloc_stop() raw_free()
internal traceback_new()
tracemalloc_add_trace()
tracemalloc_copy_trace()
raw_malloc()
traceback_new()
tracemalloc_add_trace()
tracemalloc_remove_trace()
raw_free()
traces internal tracemalloc_add_trace()
tracemalloc_remove_trace()
tracemalloc_get_traceback()
tracemalloc_get_traces_table()
domains internal tracemalloc_add_trace()
tracemalloc_remove_trace()
tracemalloc_get_traceback()
tracemalloc_get_traces_table()
reentrant_key allocator tracemalloc_alloc_gil()
tracemalloc_realloc_gil()
tracemalloc_raw_alloc()
tracemalloc_raw_realloc()
get_reentrant()
tracemalloc_alloc_gil()
tracemalloc_realloc_gil()
tracemalloc_raw_alloc()
tracemalloc_raw_realloc()
set_reentrant()

Linked PRs

  • GH-101520: Move tracemalloc functionality into core, leaving interface in Modules. #104508
Reacted by Erlend E. Aasland

Activity

  1. ericsnowcurrently
    added
    type-featureA feature request or enhancementA feature request or enhancement
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    topic-subinterpreters
    3.12only security fixesonly security fixes
    on Feb 2, 2023
  2. ezio-melotti
    added this to Subinterpreterson Feb 2, 2023
  3. markshannon
    added a commit that references this issue on May 17, 2023

    GH-101520: Move tracemalloc functionality into core, leaving interfac…

    f7df173
  4. carljm
    added a commit that references this issue on May 17, 2023

    Merge branch 'main' into 709funconly

    b1bb3a7
  5. JelleZijlstra
    added a commit that references this issue on May 18, 2023

    pythonGH-101520: Move tracemalloc functionality into core, leaving in…

    4fea66d
  6. erlend-aasland commented on Jan 12, 2024

    @erlend-aasland
    erlend-aasland
    on Jan 12, 2024
    Contributor
    More actions

    Are there more actionable items left here?

  7. encukou commented on Jan 25, 2024

    @encukou
    encukou
    on Jan 25, 2024
    Member
    More actions

    As far as I can see, there aren't.

    Reacted by Erlend E. Aasland
  8. encukou
    closed this as completedon Jan 25, 2024
  9. github-project-automation
    moved this to Done in Subinterpreterson Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixesonly security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-subinterpreterstype-featureA feature request or enhancementA feature request or enhancement

    Projects

    • Subinterpreters
      Status
      Done
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.