Skip to content

Commit 169e88a

Browse files
vstinnermcepl
authored andcommitted
bpo-40609: _tracemalloc allocates traces (pythonGH-20064)
Rewrite _tracemalloc to store "trace_t*" rather than directly "trace_t" in traces hash tables. Traces are now allocated on the heap memory, outside the hash table. Add tracemalloc_copy_traces() and tracemalloc_copy_domains() helper functions. Remove _Py_hashtable_copy() function since there is no API to copy a key or a value. Remove also _Py_hashtable_delete() function which was commented.
1 parent fbead89 commit 169e88a

File tree

3 files changed

+192
-88
lines changed

3 files changed

+192
-88
lines changed

Modules/_tracemalloc.c

Lines changed: 192 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ static traceback_t *tracemalloc_traceback = NULL;
141141
Protected by the GIL */
142142
static _Py_hashtable_t *tracemalloc_tracebacks = NULL;
143143

144-
/* pointer (void*) => trace (trace_t).
144+
/* pointer (void*) => trace (trace_t*).
145145
Protected by TABLES_LOCK(). */
146146
static _Py_hashtable_t *tracemalloc_traces = NULL;
147147

@@ -516,14 +516,23 @@ traceback_new(void)
516516
}
517517

518518

519-
static int
520-
tracemalloc_use_domain_cb(_Py_hashtable_t *old_traces,
521-
_Py_hashtable_entry_t *entry, void *user_data)
519+
static void
520+
tracemalloc_destroy_trace_cb(_Py_hashtable_t *traces,
521+
_Py_hashtable_entry_t *entry)
522522
{
523-
return hashtable_new(sizeof(trace_t),
523+
trace_t *trace;
524+
_Py_HASHTABLE_ENTRY_READ_DATA(traces, entry, trace);
525+
raw_free(trace);
526+
}
527+
528+
529+
static _Py_hashtable_t*
530+
tracemalloc_create_traces_table(void)
531+
{
532+
return hashtable_new(sizeof(trace_t*),
524533
_Py_hashtable_hash_ptr,
525534
_Py_hashtable_compare_direct,
526-
NULL);
535+
tracemalloc_destroy_trace_cb);
527536
}
528537

529538
_Py_HASHTABLE_ENTRY_READ_KEY(old_traces, entry, ptr);
@@ -569,8 +578,13 @@ tracemalloc_remove_trace(_PyTraceMalloc_domain_t domain, uintptr_t ptr)
569578
return;
570579
}
571580

572-
assert(tracemalloc_traced_memory >= trace.size);
573-
tracemalloc_traced_memory -= trace.size;
581+
trace_t *trace;
582+
if (!_Py_HASHTABLE_POP(traces, TO_PTR(ptr), trace)) {
583+
return;
584+
}
585+
assert(tracemalloc_traced_memory >= trace->size);
586+
tracemalloc_traced_memory -= trace->size;
587+
raw_free(trace);
574588
}
575589

576590
#define REMOVE_TRACE(ptr) \
@@ -609,19 +623,24 @@ tracemalloc_add_trace(_PyTraceMalloc_domain_t domain, uintptr_t ptr,
609623
entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, ptr);
610624
}
611625

626+
_Py_hashtable_entry_t* entry = _Py_HASHTABLE_GET_ENTRY(traces, ptr);
612627
if (entry != NULL) {
613628
/* the memory block is already tracked */
614-
_Py_HASHTABLE_ENTRY_READ_DATA(tracemalloc_traces, entry, trace);
615-
assert(tracemalloc_traced_memory >= trace.size);
616-
tracemalloc_traced_memory -= trace.size;
629+
trace_t *trace;
630+
_Py_HASHTABLE_ENTRY_READ_DATA(traces, entry, trace);
631+
assert(tracemalloc_traced_memory >= trace->size);
632+
tracemalloc_traced_memory -= trace->size;
617633

618-
trace.size = size;
619-
trace.traceback = traceback;
620-
_Py_HASHTABLE_ENTRY_WRITE_DATA(tracemalloc_traces, entry, trace);
634+
trace->size = size;
635+
trace->traceback = traceback;
621636
}
622637
else {
623-
trace.size = size;
624-
trace.traceback = traceback;
638+
trace_t *trace = raw_malloc(sizeof(trace_t));
639+
if (trace == NULL) {
640+
return -1;
641+
}
642+
trace->size = size;
643+
trace->traceback = traceback;
625644

626645
if (tracemalloc_config.use_domain) {
627646
res = _Py_HASHTABLE_SET(tracemalloc_traces, key, trace);
@@ -630,6 +649,7 @@ tracemalloc_add_trace(_PyTraceMalloc_domain_t domain, uintptr_t ptr,
630649
res = _Py_HASHTABLE_SET(tracemalloc_traces, ptr, trace);
631650
}
632651
if (res != 0) {
652+
raw_free(trace);
633653
return res;
634654
}
635655
}
@@ -1275,13 +1295,94 @@ typedef struct {
12751295
PyObject *list;
12761296
} get_traces_t;
12771297

1298+
1299+
static int
1300+
tracemalloc_copy_trace(_Py_hashtable_t *traces,
1301+
_Py_hashtable_entry_t *entry,
1302+
void *traces2_raw)
1303+
{
1304+
_Py_hashtable_t *traces2 = (_Py_hashtable_t *)traces2_raw;
1305+
1306+
trace_t *trace;
1307+
_Py_HASHTABLE_ENTRY_READ_DATA(traces, entry, trace);
1308+
1309+
trace_t *trace2 = raw_malloc(sizeof(trace_t));
1310+
if (traces2 == NULL) {
1311+
return -1;
1312+
}
1313+
*trace2 = *trace;
1314+
if (_Py_HASHTABLE_SET(traces2, entry->key, trace2) < 0) {
1315+
raw_free(trace2);
1316+
return -1;
1317+
}
1318+
return 0;
1319+
}
1320+
1321+
1322+
static _Py_hashtable_t*
1323+
tracemalloc_copy_traces(_Py_hashtable_t *traces)
1324+
{
1325+
_Py_hashtable_t *traces2 = tracemalloc_create_traces_table();
1326+
if (traces2 == NULL) {
1327+
return NULL;
1328+
}
1329+
1330+
int err = _Py_hashtable_foreach(traces,
1331+
tracemalloc_copy_trace,
1332+
traces2);
1333+
if (err) {
1334+
_Py_hashtable_destroy(traces2);
1335+
return NULL;
1336+
}
1337+
return traces2;
1338+
}
1339+
1340+
1341+
static int
1342+
tracemalloc_copy_domain(_Py_hashtable_t *domains,
1343+
_Py_hashtable_entry_t *entry,
1344+
void *domains2_raw)
1345+
{
1346+
_Py_hashtable_t *domains2 = (_Py_hashtable_t *)domains2_raw;
1347+
1348+
unsigned int domain = (unsigned int)FROM_PTR(entry->key);
1349+
_Py_hashtable_t *traces;
1350+
_Py_HASHTABLE_ENTRY_READ_DATA(domains, entry, traces);
1351+
1352+
_Py_hashtable_t *traces2 = tracemalloc_copy_traces(traces);
1353+
if (_Py_HASHTABLE_SET(domains2, TO_PTR(domain), traces2) < 0) {
1354+
_Py_hashtable_destroy(traces2);
1355+
return -1;
1356+
}
1357+
return 0;
1358+
}
1359+
1360+
1361+
static _Py_hashtable_t*
1362+
tracemalloc_copy_domains(_Py_hashtable_t *domains)
1363+
{
1364+
_Py_hashtable_t *domains2 = tracemalloc_create_domains_table();
1365+
if (domains2 == NULL) {
1366+
return NULL;
1367+
}
1368+
1369+
int err = _Py_hashtable_foreach(domains,
1370+
tracemalloc_copy_domain,
1371+
domains2);
1372+
if (err) {
1373+
_Py_hashtable_destroy(domains2);
1374+
return NULL;
1375+
}
1376+
return domains2;
1377+
}
1378+
1379+
12781380
static int
12791381
tracemalloc_get_traces_fill(_Py_hashtable_t *traces, _Py_hashtable_entry_t *entry,
12801382
void *user_data)
12811383
{
12821384
get_traces_t *get_traces = user_data;
1283-
_PyTraceMalloc_domain_t domain;
1284-
trace_t trace;
1385+
trace_t *trace;
12851386
PyObject *tracemalloc_obj;
12861387
int res;
12871388

@@ -1295,7 +1396,7 @@ tracemalloc_get_traces_fill(_Py_hashtable_t *traces, _Py_hashtable_entry_t *entr
12951396
}
12961397
_Py_HASHTABLE_ENTRY_READ_DATA(traces, entry, trace);
12971398

1298-
tracemalloc_obj = trace_to_pyobject(domain, &trace, get_traces->tracebacks);
1399+
tracemalloc_obj = trace_to_pyobject(get_traces->domain, trace, get_traces->tracebacks);
12991400
if (tracemalloc_obj == NULL)
13001401
return 1;
13011402

@@ -1368,22 +1469,37 @@ py_tracemalloc_get_traces(PyObject *self, PyObject *obj)
13681469
_Py_hashtable_compare_direct,
13691470
tracemalloc_pyobject_decref_cb);
13701471
if (get_traces.tracebacks == NULL) {
1371-
PyErr_NoMemory();
1372-
goto error;
1472+
goto no_memory;
13731473
}
13741474

1475+
// Copy all traces so tracemalloc_get_traces_fill() doesn't have to disable
1476+
// temporarily tracemalloc which would impact other threads and so would
1477+
// miss allocations while get_traces() is called.
13751478
TABLES_LOCK();
1376-
get_traces.traces = _Py_hashtable_copy(tracemalloc_traces);
1479+
get_traces.traces = tracemalloc_copy_traces(tracemalloc_traces);
13771480
TABLES_UNLOCK();
13781481

13791482
if (get_traces.traces == NULL) {
1380-
PyErr_NoMemory();
1381-
goto error;
1483+
goto no_memory;
1484+
}
1485+
1486+
TABLES_LOCK();
1487+
get_traces.domains = tracemalloc_copy_domains(tracemalloc_domains);
1488+
TABLES_UNLOCK();
1489+
1490+
if (get_traces.domains == NULL) {
1491+
goto no_memory;
13821492
}
13831493

13841494
set_reentrant(1);
1385-
err = _Py_hashtable_foreach(get_traces.traces,
1386-
tracemalloc_get_traces_fill, &get_traces);
1495+
int err = _Py_hashtable_foreach(get_traces.traces,
1496+
tracemalloc_get_traces_fill,
1497+
&get_traces);
1498+
if (!err) {
1499+
err = _Py_hashtable_foreach(get_traces.domains,
1500+
tracemalloc_get_traces_domain,
1501+
&get_traces);
1502+
}
13871503
set_reentrant(0);
13881504
if (err)
13891505
goto error;
@@ -1408,7 +1524,7 @@ py_tracemalloc_get_traces(PyObject *self, PyObject *obj)
14081524
static traceback_t*
14091525
tracemalloc_get_traceback(_PyTraceMalloc_domain_t domain, uintptr_t ptr)
14101526
{
1411-
trace_t trace;
1527+
trace_t *trace;
14121528
int found;
14131529

14141530
if (!tracemalloc_config.tracing)
@@ -1424,10 +1540,11 @@ tracemalloc_get_traceback(_PyTraceMalloc_domain_t domain, uintptr_t ptr)
14241540
}
14251541
TABLES_UNLOCK();
14261542

1427-
if (!found)
1543+
if (!found) {
14281544
return NULL;
1545+
}
14291546

1430-
return trace.traceback;
1547+
return trace->traceback;
14311548
}
14321549

14331550

@@ -1803,6 +1920,52 @@ _PyTraceMalloc_Untrack(_PyTraceMalloc_domain_t domain, uintptr_t ptr)
18031920
}
18041921

18051922

1923+
/* If the object memory block is already traced, update its trace
1924+
with the current Python traceback.
1925+
1926+
Do nothing if tracemalloc is not tracing memory allocations
1927+
or if the object memory block is not already traced. */
1928+
int
1929+
_PyTraceMalloc_NewReference(PyObject *op)
1930+
{
1931+
assert(PyGILState_Check());
1932+
1933+
if (!_Py_tracemalloc_config.tracing) {
1934+
/* tracemalloc is not tracing: do nothing */
1935+
return -1;
1936+
}
1937+
1938+
uintptr_t ptr;
1939+
PyTypeObject *type = Py_TYPE(op);
1940+
if (PyType_IS_GC(type)) {
1941+
ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head));
1942+
}
1943+
else {
1944+
ptr = (uintptr_t)op;
1945+
}
1946+
1947+
int res = -1;
1948+
1949+
TABLES_LOCK();
1950+
_Py_hashtable_entry_t* entry;
1951+
entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, ptr);
1952+
if (entry != NULL) {
1953+
/* update the traceback of the memory block */
1954+
traceback_t *traceback = traceback_new();
1955+
if (traceback != NULL) {
1956+
trace_t *trace;
1957+
_Py_HASHTABLE_ENTRY_READ_DATA(tracemalloc_traces, entry, trace);
1958+
trace->traceback = traceback;
1959+
res = 0;
1960+
}
1961+
}
1962+
/* else: cannot track the object, its memory block size is unknown */
1963+
TABLES_UNLOCK();
1964+
1965+
return res;
1966+
}
1967+
1968+
18061969
PyObject*
18071970
_PyTraceMalloc_GetTraceback(_PyTraceMalloc_domain_t domain, uintptr_t ptr)
18081971
{

Modules/hashtable.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,6 @@ typedef struct {
4040
sizeof(DATA)); \
4141
} while (0)
4242

43-
#define _Py_HASHTABLE_ENTRY_WRITE_DATA(TABLE, ENTRY, DATA) \
44-
do { \
45-
assert(sizeof(DATA) == (TABLE)->data_size); \
46-
memcpy((void *)_Py_HASHTABLE_ENTRY_PDATA(ENTRY), \
47-
&(DATA), sizeof(DATA)); \
48-
} while (0)
49-
5043

5144
/* _Py_hashtable: prototypes */
5245

@@ -113,9 +106,6 @@ PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new_full(
113106

114107
PyAPI_FUNC(void) _Py_hashtable_destroy(_Py_hashtable_t *ht);
115108

116-
/* Return a copy of the hash table */
117-
PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_copy(_Py_hashtable_t *src);
118-
119109
PyAPI_FUNC(void) _Py_hashtable_clear(_Py_hashtable_t *ht);
120110

121111
typedef int (*_Py_hashtable_foreach_func) (_Py_hashtable_t *ht,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy