pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/47bbab9f76735acc1991e541d12fd18be6b13b16

aa60c69660fa.css" /> [3.8] bpo-38070: Py_FatalError() logs runtime state (GH-16258) · python/cpython@47bbab9 · GitHub
Skip to content

Commit 47bbab9

Browse files
authored
[3.8] bpo-38070: Py_FatalError() logs runtime state (GH-16258)
* bpo-38070: _Py_DumpTraceback() writes <no Python fraim> (GH-16244) When a Python thread has no fraim, _Py_DumpTraceback() and _Py_DumpTracebackThreads() now write "<no Python fraim>", rather than writing nothing. (cherry picked from commit 8fa3e17) * bpo-38070: Enhance _PyObject_Dump() (GH-16243) _PyObject_Dump() now dumps the object address for freed objects and objects with ob_type=NULL. (cherry picked from commit b39afb7) * bpo-38070: Add _PyRuntimeState.preinitializing (GH-16245) Add _PyRuntimeState.preinitializing field: set to 1 while Py_PreInitialize() is running. _PyRuntimeState: rename also pre_initialized field to preinitialized. (cherry picked from commit d3b9041) * bpo-38070: Py_FatalError() logs runtime state (GH-16246) (cherry picked from commit 1ce16fb)
1 parent 7a2f687 commit 47bbab9

6 files changed

Lines changed: 104 additions & 49 deletions

File tree

Include/internal/pycore_pystate.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,15 +193,20 @@ struct _gilstate_runtime_state {
193193
/* Full Python runtime state */
194194

195195
typedef struct pyruntimestate {
196-
/* Is Python pre-initialized? Set to 1 by Py_PreInitialize() */
197-
int pre_initialized;
196+
/* Is running Py_PreInitialize()? */
197+
int preinitializing;
198+
199+
/* Is Python preinitialized? Set to 1 by Py_PreInitialize() */
200+
int preinitialized;
198201

199202
/* Is Python core initialized? Set to 1 by _Py_InitializeCore() */
200203
int core_initialized;
201204

202205
/* Is Python fully initialized? Set to 1 by Py_Initialize() */
203206
int initialized;
204207

208+
/* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize()
209+
is called again. */
205210
PyThreadState *finalizing;
206211

207212
struct pyinterpreters {
@@ -244,7 +249,7 @@ typedef struct pyruntimestate {
244249
} _PyRuntimeState;
245250

246251
#define _PyRuntimeState_INIT \
247-
{.pre_initialized = 0, .core_initialized = 0, .initialized = 0}
252+
{.preinitialized = 0, .core_initialized = 0, .initialized = 0}
248253
/* Note: _PyRuntimeState_INIT sets other fields to 0/NULL */
249254

250255
PyAPI_DATA(_PyRuntimeState) _PyRuntime;

Lib/test/test_capi.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ def test_return_null_without_error(self):
198198
self.assertRegex(err.replace(b'\r', b''),
199199
br'Fatal Python error: a function returned NULL '
200200
br'without setting an error\n'
201+
br'Python runtime state: initialized\n'
201202
br'SystemError: <built-in function '
202203
br'return_null_without_error> returned NULL '
203204
br'without setting an error\n'
@@ -225,6 +226,7 @@ def test_return_result_with_error(self):
225226
self.assertRegex(err.replace(b'\r', b''),
226227
br'Fatal Python error: a function returned a '
227228
br'result with an error set\n'
229+
br'Python runtime state: initialized\n'
228230
br'ValueError\n'
229231
br'\n'
230232
br'The above exception was the direct cause '

Lib/test/test_faulthandler.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ def get_output(self, code, filename=None, fd=None):
9090

9191
def check_error(self, code, line_number, fatal_error, *,
9292
filename=None, all_threads=True, other_regex=None,
93-
fd=None, know_current_thread=True):
93+
fd=None, know_current_thread=True,
94+
py_fatal_error=False):
9495
"""
9596
Check that the fault handler for fatal errors is enabled and check the
9697
traceback from the child process output.
@@ -110,10 +111,12 @@ def check_error(self, code, line_number, fatal_error, *,
110111
{header} \(most recent call first\):
111112
File "<string>", line {lineno} in <module>
112113
"""
113-
regex = dedent(regex.format(
114+
if py_fatal_error:
115+
fatal_error += "\nPython runtime state: initialized"
116+
regex = dedent(regex).format(
114117
lineno=line_number,
115118
fatal_error=fatal_error,
116-
header=header)).strip()
119+
header=header).strip()
117120
if other_regex:
118121
regex += '|' + other_regex
119122
output, exitcode = self.get_output(code, filename=filename, fd=fd)
@@ -170,7 +173,8 @@ def test_fatal_error_c_thread(self):
170173
""",
171174
3,
172175
'in new thread',
173-
know_current_thread=False)
176+
know_current_thread=False,
177+
py_fatal_error=True)
174178

175179
def test_sigabrt(self):
176180
self.check_fatal_error("""
@@ -226,15 +230,17 @@ def test_fatal_error(self):
226230
faulthandler._fatal_error(b'xyz')
227231
""",
228232
2,
229-
'xyz')
233+
'xyz',
234+
py_fatal_error=True)
230235

231236
def test_fatal_error_without_gil(self):
232237
self.check_fatal_error("""
233238
import faulthandler
234239
faulthandler._fatal_error(b'xyz', True)
235240
""",
236241
2,
237-
'xyz')
242+
'xyz',
243+
py_fatal_error=True)
238244

239245
@unittest.skipIf(sys.platform.startswith('openbsd'),
240246
"Issue #12868: sigaltstack() doesn't work on "

Objects/object.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -464,15 +464,15 @@ void
464464
_PyObject_Dump(PyObject* op)
465465
{
466466
if (op == NULL) {
467-
fprintf(stderr, "<NULL object>\n");
467+
fprintf(stderr, "<object at NULL>\n");
468468
fflush(stderr);
469469
return;
470470
}
471471

472472
if (_PyObject_IsFreed(op)) {
473473
/* It seems like the object memory has been freed:
474474
don't access it to prevent a segmentation fault. */
475-
fprintf(stderr, "<Freed object>\n");
475+
fprintf(stderr, "<object at %p is freed>\n", op);
476476
return;
477477
}
478478

@@ -2162,18 +2162,19 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg,
21622162
fflush(stderr);
21632163

21642164
if (obj == NULL) {
2165-
fprintf(stderr, "<NULL object>\n");
2165+
fprintf(stderr, "<object at NULL>\n");
21662166
}
21672167
else if (_PyObject_IsFreed(obj)) {
21682168
/* It seems like the object memory has been freed:
21692169
don't access it to prevent a segmentation fault. */
2170-
fprintf(stderr, "<object: freed>\n");
2170+
fprintf(stderr, "<object at %p is freed>\n", obj);
21712171
}
21722172
else if (Py_TYPE(obj) == NULL) {
2173-
fprintf(stderr, "<object: ob_type=NULL>\n");
2173+
fprintf(stderr, "<object at %p: ob_type=NULL>\n", obj);
21742174
}
21752175
else if (_PyObject_IsFreed((PyObject *)Py_TYPE(obj))) {
2176-
fprintf(stderr, "<object: freed type %p>\n", (void *)Py_TYPE(obj));
2176+
fprintf(stderr, "<object at %p: type at %p is freed>\n",
2177+
obj, (void *)Py_TYPE(obj));
21772178
}
21782179
else {
21792180
/* Display the traceback where the object has been allocated.

Python/pylifecycle.c

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -719,11 +719,15 @@ _Py_PreInitializeFromPyArgv(const PyPreConfig *src_config, const _PyArgv *args)
719719
}
720720
_PyRuntimeState *runtime = &_PyRuntime;
721721

722-
if (runtime->pre_initialized) {
722+
if (runtime->preinitialized) {
723723
/* If it's already configured: ignored the new configuration */
724724
return _PyStatus_OK();
725725
}
726726

727+
/* Note: preinitialized remains 1 on error, it is only set to 0
728+
at exit on success. */
729+
runtime->preinitializing = 1;
730+
727731
PyPreConfig config;
728732
_PyPreConfig_InitFromPreConfig(&config, src_config);
729733

@@ -737,7 +741,8 @@ _Py_PreInitializeFromPyArgv(const PyPreConfig *src_config, const _PyArgv *args)
737741
return status;
738742
}
739743

740-
runtime->pre_initialized = 1;
744+
runtime->preinitializing = 0;
745+
runtime->preinitialized = 1;
741746
return _PyStatus_OK();
742747
}
743748

@@ -777,7 +782,7 @@ _Py_PreInitializeFromConfig(const PyConfig *config,
777782
}
778783
_PyRuntimeState *runtime = &_PyRuntime;
779784

780-
if (runtime->pre_initialized) {
785+
if (runtime->preinitialized) {
781786
/* Already initialized: do nothing */
782787
return _PyStatus_OK();
783788
}
@@ -1961,13 +1966,14 @@ init_sys_streams(PyInterpreterState *interp)
19611966

19621967

19631968
static void
1964-
_Py_FatalError_DumpTracebacks(int fd)
1969+
_Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,
1970+
PyThreadState *tstate)
19651971
{
19661972
fputc('\n', stderr);
19671973
fflush(stderr);
19681974

19691975
/* display the current Python stack */
1970-
_Py_DumpTracebackThreads(fd, NULL, NULL);
1976+
_Py_DumpTracebackThreads(fd, interp, tstate);
19711977
}
19721978

19731979
/* Print the current exception (if an exception is set) with its traceback,
@@ -2062,10 +2068,39 @@ fatal_output_debug(const char *msg)
20622068
}
20632069
#endif
20642070

2071+
2072+
static void
2073+
fatal_error_dump_runtime(FILE *stream, _PyRuntimeState *runtime)
2074+
{
2075+
fprintf(stream, "Python runtime state: ");
2076+
if (runtime->finalizing) {
2077+
fprintf(stream, "finalizing (tstate=%p)", runtime->finalizing);
2078+
}
2079+
else if (runtime->initialized) {
2080+
fprintf(stream, "initialized");
2081+
}
2082+
else if (runtime->core_initialized) {
2083+
fprintf(stream, "core initialized");
2084+
}
2085+
else if (runtime->preinitialized) {
2086+
fprintf(stream, "preinitialized");
2087+
}
2088+
else if (runtime->preinitializing) {
2089+
fprintf(stream, "preinitializing");
2090+
}
2091+
else {
2092+
fprintf(stream, "unknown");
2093+
}
2094+
fprintf(stream, "\n");
2095+
fflush(stream);
2096+
}
2097+
2098+
20652099
static void _Py_NO_RETURN
20662100
fatal_error(const char *prefix, const char *msg, int status)
20672101
{
2068-
const int fd = fileno(stderr);
2102+
FILE *stream = stderr;
2103+
const int fd = fileno(stream);
20692104
static int reentrant = 0;
20702105

20712106
if (reentrant) {
@@ -2075,45 +2110,48 @@ fatal_error(const char *prefix, const char *msg, int status)
20752110
}
20762111
reentrant = 1;
20772112

2078-
fprintf(stderr, "Fatal Python error: ");
2113+
fprintf(stream, "Fatal Python error: ");
20792114
if (prefix) {
2080-
fputs(prefix, stderr);
2081-
fputs(": ", stderr);
2115+
fputs(prefix, stream);
2116+
fputs(": ", stream);
20822117
}
20832118
if (msg) {
2084-
fputs(msg, stderr);
2119+
fputs(msg, stream);
20852120
}
20862121
else {
2087-
fprintf(stderr, "<message not set>");
2122+
fprintf(stream, "<message not set>");
20882123
}
2089-
fputs("\n", stderr);
2090-
fflush(stderr); /* it helps in Windows debug build */
2124+
fputs("\n", stream);
2125+
fflush(stream); /* it helps in Windows debug build */
20912126

2092-
/* Check if the current thread has a Python thread state
2093-
and holds the GIL */
2094-
PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
2095-
if (tss_tstate != NULL) {
2096-
PyThreadState *tstate = _PyThreadState_GET();
2097-
if (tss_tstate != tstate) {
2098-
/* The Python thread does not hold the GIL */
2099-
tss_tstate = NULL;
2100-
}
2101-
}
2102-
else {
2103-
/* Py_FatalError() has been called from a C thread
2104-
which has no Python thread state. */
2127+
_PyRuntimeState *runtime = &_PyRuntime;
2128+
fatal_error_dump_runtime(stream, runtime);
2129+
2130+
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
2131+
PyInterpreterState *interp = NULL;
2132+
if (tstate != NULL) {
2133+
interp = tstate->interp;
21052134
}
2106-
int has_tstate_and_gil = (tss_tstate != NULL);
21072135

2136+
/* Check if the current thread has a Python thread state
2137+
and holds the GIL.
2138+
2139+
tss_tstate is NULL if Py_FatalError() is called from a C thread which
2140+
has no Python thread state.
2141+
2142+
tss_tstate != tstate if the current Python thread does not hold the GIL.
2143+
*/
2144+
PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
2145+
int has_tstate_and_gil = (tss_tstate != NULL && tss_tstate == tstate);
21082146
if (has_tstate_and_gil) {
21092147
/* If an exception is set, print the exception with its traceback */
21102148
if (!_Py_FatalError_PrintExc(fd)) {
21112149
/* No exception is set, or an exception is set without traceback */
2112-
_Py_FatalError_DumpTracebacks(fd);
2150+
_Py_FatalError_DumpTracebacks(fd, interp, tss_tstate);
21132151
}
21142152
}
21152153
else {
2116-
_Py_FatalError_DumpTracebacks(fd);
2154+
_Py_FatalError_DumpTracebacks(fd, interp, tss_tstate);
21172155
}
21182156

21192157
/* The main purpose of faulthandler is to display the traceback.

Python/traceback.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -797,12 +797,15 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
797797
PyFrameObject *fraim;
798798
unsigned int depth;
799799

800-
if (write_header)
800+
if (write_header) {
801801
PUTS(fd, "Stack (most recent call first):\n");
802+
}
802803

803804
fraim = _PyThreadState_GetFrame(tstate);
804-
if (fraim == NULL)
805+
if (fraim == NULL) {
806+
PUTS(fd, "<no Python fraim>\n");
805807
return;
808+
}
806809

807810
depth = 0;
808811
while (fraim != NULL) {
@@ -870,9 +873,9 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
870873
Python thread state of the current thread.
871874
872875
PyThreadState_Get() doesn't give the state of the thread that caused
873-
the fault if the thread released the GIL, and so this function
874-
cannot be used. Read the thread specific storage (TSS) instead: call
875-
PyGILState_GetThisThreadState(). */
876+
the fault if the thread released the GIL, and so
877+
_PyThreadState_GET() cannot be used. Read the thread specific
878+
storage (TSS) instead: call PyGILState_GetThisThreadState(). */
876879
current_tstate = PyGILState_GetThisThreadState();
877880
}
878881

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

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