diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 9e391604a1a59d..7ebc34f47fc39b 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -975,3 +975,12 @@ Debug-mode variables shutting down the interpreter. Need Python configured with the :option:`--with-trace-refs` build option. + +.. envvar:: PYTHONDUMPREFSFILE=FILENAME + + If set, Python will dump objects and reference counts still alive + after shutting down the interpreter into a file called *FILENAME*. + + Need Python configured with the :option:`--with-trace-refs` build option. + + .. versionadded:: 3.11 diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 5f03b8c57f8bc8..22ad0f14e58004 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -143,6 +143,7 @@ typedef struct PyConfig { int no_debug_ranges; int show_ref_count; int dump_refs; + wchar_t *dump_refs_file; int malloc_stats; wchar_t *filesystem_encoding; wchar_t *filesystem_errors; diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-08-14-20-13-21.bpo-44895.Ic9m90.rst b/Misc/NEWS.d/next/Core and Builtins/2021-08-14-20-13-21.bpo-44895.Ic9m90.rst new file mode 100644 index 00000000000000..d369ac76505974 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-08-14-20-13-21.bpo-44895.Ic9m90.rst @@ -0,0 +1,2 @@ +A debug variable :envvar:`PYTHONDUMPREFSFILE` is added for creating a dump file +which is generated by :option:`--with-trace-refs`. Patch by Dong-hee Na. diff --git a/Python/initconfig.c b/Python/initconfig.c index d328f227cead25..61cd0e6213ed17 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -898,6 +898,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2) COPY_ATTR(no_debug_ranges); COPY_ATTR(show_ref_count); COPY_ATTR(dump_refs); + COPY_ATTR(dump_refs_file); COPY_ATTR(malloc_stats); COPY_WSTR_ATTR(pycache_prefix); @@ -1701,6 +1702,14 @@ config_read_env_vars(PyConfig *config) config->malloc_stats = 1; } + if (config->dump_refs_file == NULL) { + status = CONFIG_GET_ENV_DUP(config, &config->dump_refs_file, + L"PYTHONDUMPREFSFILE", "PYTHONDUMPREFSFILE"); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + } + if (config->pythonpath_env == NULL) { status = CONFIG_GET_ENV_DUP(config, &config->pythonpath_env, L"PYTHONPATH", "PYTHONPATH"); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index eeaf20b4617a20..f3b6b0ac68a1ea 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1737,6 +1737,7 @@ Py_FinalizeEx(void) #endif #ifdef Py_TRACE_REFS int dump_refs = tstate->interp->config.dump_refs; + wchar_t *dump_refs_file = tstate->interp->config.dump_refs_file; #endif #ifdef WITH_PYMALLOC int malloc_stats = tstate->interp->config.malloc_stats; @@ -1835,9 +1836,22 @@ Py_FinalizeEx(void) * Alas, a lot of stuff may still be alive now that will be cleaned * up later. */ + + FILE *dump_refs_fp = NULL; + if (dump_refs_file != NULL) { + dump_refs_fp = _Py_wfopen(dump_refs_file, L"w"); + if (dump_refs_fp == NULL) { + fprintf(stderr, "PYTHONDUMPREFSFILE: cannot create file: %ls\n", dump_refs_file); + } + } + if (dump_refs) { _Py_PrintReferences(stderr); } + + if (dump_refs_fp != NULL) { + _Py_PrintReferences(dump_refs_fp); + } #endif /* Py_TRACE_REFS */ finalize_interp_clear(tstate); @@ -1848,9 +1862,15 @@ Py_FinalizeEx(void) * An address can be used to find the repr of the object, printed * above by _Py_PrintReferences. */ + if (dump_refs) { _Py_PrintReferenceAddresses(stderr); } + + if (dump_refs_fp != NULL) { + _Py_PrintReferenceAddresses(dump_refs_fp); + fclose(dump_refs_fp); + } #endif /* Py_TRACE_REFS */ #ifdef WITH_PYMALLOC if (malloc_stats) {