Thu, 10 Dec 2020 20:16:21 +0100
Added some more process creation function overrides.
# -*- coding: utf-8 -*- # Copyright (c) 2002 - 2020 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a function to patch the process creation functions to support multiprocess debugging. """ from DebugUtilities import ( patchArguments, patchArgumentStringWindows, isPythonProgram, isWindowsPlatform ) _debugClient = None def patchModule(module, functionName, createFunction): """ Function to replace a function of a module with a modified one. @param module reference to the module @type types.ModuleType @param functionName name of the function to be replaced @type str @param createFunction function creating the replacement @type types.FunctionType """ if hasattr(module, functionName): originalName = 'original_' + functionName if not hasattr(module, originalName): setattr(module, originalName, getattr(module, functionName)) setattr(module, functionName, createFunction(originalName)) def createExecl(originalName): """ Function to patch the 'execl' process creation functions. <ul> <li>os.execl(path, arg0, arg1, ...)</li> <li>os.execle(path, arg0, arg1, ..., env)</li> <li>os.execlp(file, arg0, arg1, ...)</li> <li>os.execlpe(file, arg0, arg1, ..., env)</li> </ul> """ def newExecl(path, *args): """ Function replacing the 'execl' functions of the os module. """ import os if ( _debugClient.debugging and _debugClient.multiprocessSupport ): args = patchArguments(_debugClient, args) if isPythonProgram(args[0]): path = args[0] return getattr(os, originalName)(path, *args) return newExecl def createExecv(originalName): """ Function to patch the 'execv' process creation functions. <ul> <li>os.execv(path, args)</li> <li>os.execvp(file, args)</li> </ul> """ def newExecv(path, args): """ Function replacing the 'execv' functions of the os module. """ import os if ( _debugClient.debugging and _debugClient.multiprocessSupport ): args = patchArguments(_debugClient, args) if isPythonProgram(args[0]): path = args[0] return getattr(os, originalName)(path, args) return newExecv def createExecve(originalName): """ Function to patch the 'execve' process creation functions. <ul> <li>os.execve(path, args, env)</li> <li>os.execvpe(file, args, env)</li> </ul> """ def newExecve(path, args, env): """ Function replacing the 'execve' functions of the os module. """ import os if ( _debugClient.debugging and _debugClient.multiprocessSupport ): args = patchArguments(_debugClient, args) if isPythonProgram(args[0]): path = args[0] return getattr(os, originalName)(path, args, env) return newExecve # TODO: add createSpawn... def createForkExec(originalName): """ Function to patch the 'fork_exec' process creation functions. <ul> <li>_posixsubprocess.fork_exec(args, executable_list, close_fds, ... (13 more))</li> </ul> """ def newForkExec(args, *other_args): """ Function replacing the 'fork_exec' functions of the _posixsubprocess module. """ import _posixsubprocess if ( _debugClient.debugging and _debugClient.multiprocessSupport ): args = patchArguments(_debugClient, args) return getattr(_posixsubprocess, originalName)(args, *other_args) return newForkExec def createFork(original_name): """ Function to patch the 'fork' process creation functions. <ul> <li>os.fork()</li> </ul> """ def new_fork(): """ Function replacing the 'fork' function of the os module. """ import os import sys # A simple fork will result in a new python process isNewPythonProcess = True frame = sys._getframe() multiprocess = ( _debugClient.debugging and _debugClient.multiprocessSupport ) isSubprocessFork = False while frame is not None: if ( frame.f_code.co_name == '_execute_child' and 'subprocess' in frame.f_code.co_filename ): isSubprocessFork = True # If we're actually in subprocess.Popen creating a child, it # may result in something which is not a Python process, (so, # we don't want to connect with it in the forked version). executable = frame.f_locals.get('executable') if executable is not None: isNewPythonProcess = False if isPythonProgram(executable): isNewPythonProcess = True break frame = frame.f_back frame = None # Just make sure we don't hold on to it. childProcess = getattr(os, original_name)() # fork if not childProcess: if isNewPythonProcess: sys.settrace(None) sys.setprofile(None) _debugClient.sessionClose(False) (wd, host, port, exceptions, tracePython, redirect, noencoding, fork_auto, fork_child) = _debugClient.startOptions _debugClient.startDebugger( filename=sys.argv[0], host=host, port=port, enableTrace=multiprocess and not isSubprocessFork, exceptions=exceptions, tracePython=tracePython, redirect=redirect, passive=False, multiprocessSupport=multiprocess) return childProcess return new_fork def createCreateProcess(originalName): """ Function to patch the 'CreateProcess' process creation function of Windows. """ def newCreateProcess(appName, cmdline, *args): """ Function replacing the 'CreateProcess' function of the _subprocess or _winapi module. """ try: import _subprocess except ImportError: import _winapi as _subprocess return getattr(_subprocess, originalName)( appName, patchArgumentStringWindows(_debugClient, cmdline), *args) return newCreateProcess # TODO: add 'createFork' def patchNewProcessFunctions(multiprocessEnabled, debugClient): """ Function to patch the process creation functions to support multiprocess debugging. @param multiprocessEnabled flag indicating multiprocess support @type bool @param debugClient reference to the debug client object @type DebugClient """ global _debugClient if not multiprocessEnabled: # return without patching return import os # patch 'os.exec...()' functions patchModule(os, "execl", createExecl) patchModule(os, "execle", createExecl) patchModule(os, "execlp", createExecl) patchModule(os, "execlpe", createExecl) patchModule(os, "execv", createExecv) patchModule(os, "execve", createExecve) patchModule(os, "execvp", createExecv) patchModule(os, "execvpe", createExecve) # TODO: implement patching of the various functions of the os module if isWindowsPlatform(): try: import _subprocess except ImportError: import _winapi as _subprocess patchModule(_subprocess, 'CreateProcess', createCreateProcess) else: patchModule(os, "fork", createFork) try: import _posixsubprocess patchModule(_posixsubprocess, "fork_exec", createForkExec) except ImportError: pass _debugClient = debugClient