From f22cd7ee4e5ab517c77f43fd0d4052fb1994fb58 Mon Sep 17 00:00:00 2001 From: eduardo-elizondo Date: Mon, 12 Mar 2018 14:48:59 -0700 Subject: [PATCH 1/3] [DEBUG] COUNT_ALLOCS: Enhanced to be ABI Compatible --- Include/object.h | 12 ++--- Modules/_testcapimodule.c | 8 --- Objects/object.c | 100 +++++++++++++++++--------------------- 3 files changed, 49 insertions(+), 71 deletions(-) diff --git a/Include/object.h b/Include/object.h index c772deaf57dbb5b..9e079aaa979e3fd 100644 --- a/Include/object.h +++ b/Include/object.h @@ -423,16 +423,16 @@ typedef struct _typeobject { unsigned int tp_version_tag; destructor tp_finalize; - +} PyTypeObject; #ifdef COUNT_ALLOCS - /* these must be last and never explicitly initialized */ +typedef struct _typeobject_ext { + const char *tp_name; /* To match with PyTypeObject */ Py_ssize_t tp_allocs; Py_ssize_t tp_frees; Py_ssize_t tp_maxalloc; - struct _typeobject *tp_prev; - struct _typeobject *tp_next; + struct _typeobject_ext *tp_next; +} PyTypeObjectExt; #endif -} PyTypeObject; #endif typedef struct{ @@ -752,12 +752,10 @@ PyAPI_FUNC(void) inc_count(PyTypeObject *); PyAPI_FUNC(void) dec_count(PyTypeObject *); #define _Py_INC_TPALLOCS(OP) inc_count(Py_TYPE(OP)) #define _Py_INC_TPFREES(OP) dec_count(Py_TYPE(OP)) -#define _Py_DEC_TPFREES(OP) Py_TYPE(OP)->tp_frees-- #define _Py_COUNT_ALLOCS_COMMA , #else #define _Py_INC_TPALLOCS(OP) #define _Py_INC_TPFREES(OP) -#define _Py_DEC_TPFREES(OP) #define _Py_COUNT_ALLOCS_COMMA #endif /* COUNT_ALLOCS */ diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index afce6c9448952b2..2ae1c234175ae91 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3222,14 +3222,7 @@ slot_tp_del(PyObject *self) _Py_DEC_REFTOTAL; /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object * chain, so no more to do there. - * If COUNT_ALLOCS, the original decref bumped tp_frees, and - * _Py_NewReference bumped tp_allocs: both of those need to be - * undone. */ -#ifdef COUNT_ALLOCS - --Py_TYPE(self)->tp_frees; - --Py_TYPE(self)->tp_allocs; -#endif } static PyObject * @@ -4563,7 +4556,6 @@ static PyMethodDef TestMethods[] = { {"datetime_check_delta", datetime_check_delta, METH_VARARGS}, {"datetime_check_tzinfo", datetime_check_tzinfo, METH_VARARGS}, {"make_timezones_capi", make_timezones_capi, METH_NOARGS}, - {"get_timezones_offset_zero", get_timezones_offset_zero, METH_NOARGS}, {"get_timezone_utc_capi", get_timezone_utc_capi, METH_VARARGS}, {"test_list_api", (PyCFunction)test_list_api, METH_NOARGS}, {"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS}, diff --git a/Objects/object.c b/Objects/object.c index 220aa90bf59c5bc..97e222b1d305d59 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -82,17 +82,46 @@ _Py_AddToAllObjects(PyObject *op, int force) #endif /* Py_TRACE_REFS */ #ifdef COUNT_ALLOCS -static PyTypeObject *type_list; -/* All types are added to type_list, at least when - they get one object created. That makes them - immortal, which unfortunately contributes to - garbage itself. If unlist_types_without_objects - is set, they will be removed from the type_list - once the last object is deallocated. */ -static int unlist_types_without_objects; +/* COUNT_ALLOCS will maintain a static list of 'PyTypeObjectExt'. + * One per every type allocated throughout the entire execution. + */ +static PyTypeObjectExt *type_list; extern Py_ssize_t tuple_zero_allocs, fast_tuple_allocs; extern Py_ssize_t quick_int_allocs, quick_neg_int_allocs; extern Py_ssize_t null_strings, one_strings; + +PyTypeObjectExt * +PyTypeObjectExt_New(PyTypeObject* tp_orig) { + PyTypeObjectExt *tp; + tp = PyMem_MALLOC(sizeof(*tp)); + tp->tp_name = strdup(tp_orig->tp_name); + tp->tp_allocs = 0; + tp->tp_frees = 0; + tp->tp_maxalloc = 0; + tp->tp_next = NULL; + return tp; +} + +PyTypeObjectExt * +find_ext(PyTypeObject *tp_orig) { + // Base case + if (type_list == NULL) { + type_list = PyTypeObjectExt_New(tp_orig); + } + + PyTypeObjectExt *tp = type_list; + while (strcmp(tp->tp_name, tp_orig->tp_name) != 0 && tp->tp_next) { + tp = tp->tp_next; + } + + if (strcmp(tp->tp_name, tp_orig->tp_name) == 0) { + return tp; // Type Found + } + tp->tp_next = PyTypeObjectExt_New(tp_orig); + + return tp->tp_next; +} + void dump_counts(FILE* f) { @@ -101,7 +130,7 @@ dump_counts(FILE* f) return; } - PyTypeObject *tp; + PyTypeObjectExt *tp; for (tp = type_list; tp; tp = tp->tp_next) fprintf(f, "%s alloc'd: %" PY_FORMAT_SIZE_T "d, " "freed: %" PY_FORMAT_SIZE_T "d, " @@ -122,7 +151,7 @@ dump_counts(FILE* f) PyObject * get_counts(void) { - PyTypeObject *tp; + PyTypeObjectExt *tp; PyObject *result; PyObject *v; @@ -147,52 +176,18 @@ get_counts(void) } void -inc_count(PyTypeObject *tp) -{ - if (tp->tp_next == NULL && tp->tp_prev == NULL) { - /* first time; insert in linked list */ - if (tp->tp_next != NULL) /* sanity check */ - Py_FatalError("XXX inc_count sanity check"); - if (type_list) - type_list->tp_prev = tp; - tp->tp_next = type_list; - /* Note that as of Python 2.2, heap-allocated type objects - * can go away, but this code requires that they stay alive - * until program exit. That's why we're careful with - * refcounts here. type_list gets a new reference to tp, - * while ownership of the reference type_list used to hold - * (if any) was transferred to tp->tp_next in the line above. - * tp is thus effectively immortal after this. - */ - Py_INCREF(tp); - type_list = tp; -#ifdef Py_TRACE_REFS - /* Also insert in the doubly-linked list of all objects, - * if not already there. - */ - _Py_AddToAllObjects((PyObject *)tp, 0); -#endif - } +inc_count(PyTypeObject *tp_orig) +{ + PyTypeObjectExt *tp = find_ext(tp_orig); tp->tp_allocs++; if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc) tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees; } -void dec_count(PyTypeObject *tp) +void dec_count(PyTypeObject *tp_orig) { + PyTypeObjectExt *tp = find_ext(tp_orig); tp->tp_frees++; - if (unlist_types_without_objects && - tp->tp_allocs == tp->tp_frees) { - /* unlink the type from type_list */ - if (tp->tp_prev) - tp->tp_prev->tp_next = tp->tp_next; - else - type_list = tp->tp_next; - if (tp->tp_next) - tp->tp_next->tp_prev = tp->tp_prev; - tp->tp_next = tp->tp_prev = NULL; - Py_DECREF(tp); - } } #endif @@ -324,14 +319,7 @@ PyObject_CallFinalizerFromDealloc(PyObject *self) _Py_DEC_REFTOTAL; /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object * chain, so no more to do there. - * If COUNT_ALLOCS, the original decref bumped tp_frees, and - * _Py_NewReference bumped tp_allocs: both of those need to be - * undone. */ -#ifdef COUNT_ALLOCS - --Py_TYPE(self)->tp_frees; - --Py_TYPE(self)->tp_allocs; -#endif return -1; } From a42a8b5e017c32dd9750b95f47374148f007f6fe Mon Sep 17 00:00:00 2001 From: eduardo-elizondo Date: Mon, 12 Mar 2018 14:52:07 -0700 Subject: [PATCH 2/3] Fix merge artifacts --- Modules/_testcapimodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 2ae1c234175ae91..7638b64158586b9 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4556,6 +4556,7 @@ static PyMethodDef TestMethods[] = { {"datetime_check_delta", datetime_check_delta, METH_VARARGS}, {"datetime_check_tzinfo", datetime_check_tzinfo, METH_VARARGS}, {"make_timezones_capi", make_timezones_capi, METH_NOARGS}, + {"get_timezones_offset_zero", get_timezones_offset_zero, METH_NOARGS}, {"get_timezone_utc_capi", get_timezone_utc_capi, METH_VARARGS}, {"test_list_api", (PyCFunction)test_list_api, METH_NOARGS}, {"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS}, From f63b13a28d60850d4a6504fc468aa8e273e41cd9 Mon Sep 17 00:00:00 2001 From: eduardo-elizondo Date: Mon, 12 Mar 2018 16:24:58 -0700 Subject: [PATCH 3/3] Indents and better comments --- Objects/object.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Objects/object.c b/Objects/object.c index 97e222b1d305d59..01dd7abd4b3ffa0 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -92,34 +92,34 @@ extern Py_ssize_t null_strings, one_strings; PyTypeObjectExt * PyTypeObjectExt_New(PyTypeObject* tp_orig) { - PyTypeObjectExt *tp; - tp = PyMem_MALLOC(sizeof(*tp)); - tp->tp_name = strdup(tp_orig->tp_name); - tp->tp_allocs = 0; - tp->tp_frees = 0; - tp->tp_maxalloc = 0; - tp->tp_next = NULL; - return tp; + PyTypeObjectExt *tp; + tp = PyMem_MALLOC(sizeof(*tp)); + tp->tp_name = strdup(tp_orig->tp_name); + tp->tp_allocs = 0; + tp->tp_frees = 0; + tp->tp_maxalloc = 0; + tp->tp_next = NULL; + return tp; } PyTypeObjectExt * find_ext(PyTypeObject *tp_orig) { - // Base case - if (type_list == NULL) { - type_list = PyTypeObjectExt_New(tp_orig); - } - - PyTypeObjectExt *tp = type_list; - while (strcmp(tp->tp_name, tp_orig->tp_name) != 0 && tp->tp_next) { - tp = tp->tp_next; - } - - if (strcmp(tp->tp_name, tp_orig->tp_name) == 0) { - return tp; // Type Found - } - tp->tp_next = PyTypeObjectExt_New(tp_orig); - - return tp->tp_next; + // Base case + if (type_list == NULL) { + type_list = PyTypeObjectExt_New(tp_orig); + } + + PyTypeObjectExt *tp = type_list; + while (strcmp(tp->tp_name, tp_orig->tp_name) != 0 && tp->tp_next) { + tp = tp->tp_next; + } + + // Type not found + if (strcmp(tp->tp_name, tp_orig->tp_name) != 0) { + tp->tp_next = PyTypeObjectExt_New(tp_orig); + tp = tp->tp_next; + } + return tp; } void