mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	bpo-44466: Faulthandler now detects the GC (GH-26823)
The faulthandler module now detects if a fatal error occurs during a garbage collector collection (only if all_threads is true).
This commit is contained in:
		
							parent
							
								
									fb68791a26
								
							
						
					
					
						commit
						d19163912b
					
				
					 5 changed files with 70 additions and 14 deletions
				
			
		|  | @ -76,6 +76,10 @@ Fault handler state | |||
|    .. versionchanged:: 3.6 | ||||
|       On Windows, a handler for Windows exception is also installed. | ||||
| 
 | ||||
|    .. versionchanged:: 3.10 | ||||
|       The dump now mentions if a garbage collector collection is running | ||||
|       if *all_threads* is true. | ||||
| 
 | ||||
| .. function:: disable() | ||||
| 
 | ||||
|    Disable the fault handler: uninstall the signal handlers installed by | ||||
|  |  | |||
|  | @ -1003,6 +1003,13 @@ Add *encoding* and *errors* parameters in :func:`fileinput.input` and | |||
| when *mode* is "r" and file is compressed, like uncompressed files. | ||||
| (Contributed by Inada Naoki in :issue:`5758`.) | ||||
| 
 | ||||
| faulthandler | ||||
| ------------ | ||||
| 
 | ||||
| The :mod:`faulthandler` module now detects if a fatal error occurs during a | ||||
| garbage collector collection. | ||||
| (Contributed by Victor Stinner in :issue:`44466`.) | ||||
| 
 | ||||
| gc | ||||
| -- | ||||
| 
 | ||||
|  |  | |||
|  | @ -89,10 +89,12 @@ def get_output(self, code, filename=None, fd=None): | |||
|             output = output.decode('ascii', 'backslashreplace') | ||||
|         return output.splitlines(), exitcode | ||||
| 
 | ||||
|     def check_error(self, code, line_number, fatal_error, *, | ||||
|     def check_error(self, code, lineno, fatal_error, *, | ||||
|                     filename=None, all_threads=True, other_regex=None, | ||||
|                     fd=None, know_current_thread=True, | ||||
|                     py_fatal_error=False): | ||||
|                     py_fatal_error=False, | ||||
|                     garbage_collecting=False, | ||||
|                     function='<module>'): | ||||
|         """ | ||||
|         Check that the fault handler for fatal errors is enabled and check the | ||||
|         traceback from the child process output. | ||||
|  | @ -106,20 +108,21 @@ def check_error(self, code, line_number, fatal_error, *, | |||
|                 header = 'Thread 0x[0-9a-f]+' | ||||
|         else: | ||||
|             header = 'Stack' | ||||
|         regex = r""" | ||||
|             (?m)^{fatal_error} | ||||
| 
 | ||||
|             {header} \(most recent call first\): | ||||
|               File "<string>", line {lineno} in <module> | ||||
|             """ | ||||
|         regex = [f'^{fatal_error}'] | ||||
|         if py_fatal_error: | ||||
|             fatal_error += "\nPython runtime state: initialized" | ||||
|         regex = dedent(regex).format( | ||||
|             lineno=line_number, | ||||
|             fatal_error=fatal_error, | ||||
|             header=header).strip() | ||||
|             regex.append("Python runtime state: initialized") | ||||
|         regex.append('') | ||||
|         regex.append(fr'{header} \(most recent call first\):') | ||||
|         if garbage_collecting: | ||||
|             regex.append('  Garbage-collecting') | ||||
|         regex.append(fr'  File "<string>", line {lineno} in {function}') | ||||
|         regex = '\n'.join(regex) | ||||
| 
 | ||||
|         if other_regex: | ||||
|             regex += '|' + other_regex | ||||
|             regex = f'(?:{regex}|{other_regex})' | ||||
| 
 | ||||
|         # Enable MULTILINE flag | ||||
|         regex = f'(?m){regex}' | ||||
|         output, exitcode = self.get_output(code, filename=filename, fd=fd) | ||||
|         output = '\n'.join(output) | ||||
|         self.assertRegex(output, regex) | ||||
|  | @ -168,6 +171,42 @@ def test_sigsegv(self): | |||
|             3, | ||||
|             'Segmentation fault') | ||||
| 
 | ||||
|     @skip_segfault_on_android | ||||
|     def test_gc(self): | ||||
|         # bpo-44466: Detect if the GC is running | ||||
|         self.check_fatal_error(""" | ||||
|             import faulthandler | ||||
|             import gc | ||||
|             import sys | ||||
| 
 | ||||
|             faulthandler.enable() | ||||
| 
 | ||||
|             class RefCycle: | ||||
|                 def __del__(self): | ||||
|                     faulthandler._sigsegv() | ||||
| 
 | ||||
|             # create a reference cycle which triggers a fatal | ||||
|             # error in a destructor | ||||
|             a = RefCycle() | ||||
|             b = RefCycle() | ||||
|             a.b = b | ||||
|             b.a = a | ||||
| 
 | ||||
|             # Delete the objects, not the cycle | ||||
|             a = None | ||||
|             b = None | ||||
| 
 | ||||
|             # Break the reference cycle: call __del__() | ||||
|             gc.collect() | ||||
| 
 | ||||
|             # Should not reach this line | ||||
|             print("exit", file=sys.stderr) | ||||
|             """, | ||||
|             9, | ||||
|             'Segmentation fault', | ||||
|             function='__del__', | ||||
|             garbage_collecting=True) | ||||
| 
 | ||||
|     def test_fatal_error_c_thread(self): | ||||
|         self.check_fatal_error(""" | ||||
|             import faulthandler | ||||
|  |  | |||
|  | @ -0,0 +1,2 @@ | |||
| The :mod:`faulthandler` module now detects if a fatal error occurs during a | ||||
| garbage collector collection. Patch by Victor Stinner. | ||||
|  | @ -4,6 +4,7 @@ | |||
| #include "Python.h" | ||||
| 
 | ||||
| #include "code.h" | ||||
| #include "pycore_interp.h"        // PyInterpreterState.gc | ||||
| #include "frameobject.h"          // PyFrame_GetBack() | ||||
| #include "structmember.h"         // PyMemberDef | ||||
| #include "osdefs.h"               // SEP | ||||
|  | @ -914,6 +915,9 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, | |||
|             break; | ||||
|         } | ||||
|         write_thread_id(fd, tstate, tstate == current_tstate); | ||||
|         if (tstate == current_tstate && tstate->interp->gc.collecting) { | ||||
|             PUTS(fd, "  Garbage-collecting\n"); | ||||
|         } | ||||
|         dump_traceback(fd, tstate, 0); | ||||
|         tstate = PyThreadState_Next(tstate); | ||||
|         nthreads++; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Victor Stinner
						Victor Stinner