29 from AsyncFile import AsyncFile, AsyncPendingWrite |
29 from AsyncFile import AsyncFile, AsyncPendingWrite |
30 from DebugConfig import ConfigQtNames, SpecialAttributes |
30 from DebugConfig import ConfigQtNames, SpecialAttributes |
31 from FlexCompleter import Completer |
31 from FlexCompleter import Completer |
32 from DebugUtilities import prepareJsonCommand |
32 from DebugUtilities import prepareJsonCommand |
33 from BreakpointWatch import Breakpoint, Watch |
33 from BreakpointWatch import Breakpoint, Watch |
34 ##from MultiProcessDebugExtension import patchNewProcessFunctions |
34 from MultiProcessDebugExtension import patchNewProcessFunctions |
35 |
35 |
36 from DebugUtilities import getargvalues, formatargvalues |
36 from DebugUtilities import getargvalues, formatargvalues |
37 |
37 |
38 DebugClientInstance = None |
38 DebugClientInstance = None |
39 |
39 |
66 __main__.__builtins__.__dict__['input'] = DebugClientInput |
66 __main__.__builtins__.__dict__['input'] = DebugClientInput |
67 |
67 |
68 ############################################################################### |
68 ############################################################################### |
69 |
69 |
70 |
70 |
71 def DebugClientFork(): |
|
72 """ |
|
73 Replacement for the standard os.fork(). |
|
74 |
|
75 @return result of the fork() call |
|
76 """ |
|
77 if DebugClientInstance is None: |
|
78 return DebugClientOrigFork() |
|
79 |
|
80 return DebugClientInstance.fork() |
|
81 |
|
82 # use our own fork(). |
|
83 if 'fork' in dir(os): |
|
84 DebugClientOrigFork = os.fork |
|
85 os.fork = DebugClientFork |
|
86 |
|
87 ############################################################################### |
|
88 |
|
89 |
|
90 def DebugClientClose(fd): |
71 def DebugClientClose(fd): |
91 """ |
72 """ |
92 Replacement for the standard os.close(fd). |
73 Replacement for the standard os.close(fd). |
93 |
74 |
94 @param fd open file descriptor to be closed (integer) |
75 @param fd open file descriptor to be closed (integer) |
180 self.running = None |
161 self.running = None |
181 self.test = None |
162 self.test = None |
182 self.debugging = False |
163 self.debugging = False |
183 self.multiprocessSupport = False |
164 self.multiprocessSupport = False |
184 self.noDebugList = [] |
165 self.noDebugList = [] |
185 |
|
186 self.fork_auto = False |
|
187 self.fork_child = False |
|
188 |
166 |
189 self.readstream = None |
167 self.readstream = None |
190 self.writestream = None |
168 self.writestream = None |
191 self.errorstream = None |
169 self.errorstream = None |
192 self.pollingDisabled = False |
170 self.pollingDisabled = False |
282 |
260 |
283 def __compileFileSource(self, filename, mode='exec'): |
261 def __compileFileSource(self, filename, mode='exec'): |
284 """ |
262 """ |
285 Private method to compile source code read from a file. |
263 Private method to compile source code read from a file. |
286 |
264 |
287 @param filename name of the source file (string) |
265 @param filename name of the source file |
288 @param mode kind of code to be generated (string, exec or eval) |
266 @type str |
|
267 @param mode kind of code to be generated (exec or eval) |
|
268 @type str |
289 @return compiled code object (None in case of errors) |
269 @return compiled code object (None in case of errors) |
290 """ |
270 """ |
291 with codecs.open(filename, encoding=self.__coding) as fp: |
271 with codecs.open(filename, encoding=self.__coding) as fp: |
292 statement = fp.read() |
272 statement = fp.read() |
293 |
273 |
|
274 return self.__compileCommand(statement, filename=filename, mode=mode) |
|
275 |
|
276 def __compileCommand(self, statement, filename="<string>", mode="exec"): |
|
277 """ |
|
278 Private method to compile source code. |
|
279 |
|
280 @param statement source code string to be compiled |
|
281 @type str |
|
282 @param filename name of the source file |
|
283 @type str |
|
284 @param mode kind of code to be generated (exec or eval) |
|
285 @type str |
|
286 @return compiled code object (None in case of errors) |
|
287 """ |
294 try: |
288 try: |
295 code = compile(statement + '\n', filename, mode) |
289 code = compile(statement + '\n', filename, mode) |
296 except SyntaxError: |
290 except SyntaxError: |
297 exctype, excval, exctb = sys.exc_info() |
291 exctype, excval, exctb = sys.exc_info() |
298 try: |
292 try: |
430 |
424 |
431 self.running = sys.argv[0] |
425 self.running = sys.argv[0] |
432 self.debugging = True |
426 self.debugging = True |
433 self.multiprocessSupport = params["multiprocess"] |
427 self.multiprocessSupport = params["multiprocess"] |
434 |
428 |
435 self.fork_auto = params["autofork"] |
|
436 self.fork_child = params["forkChild"] |
|
437 |
|
438 self.threads.clear() |
429 self.threads.clear() |
439 self.attachThread(mainThread=True) |
430 self.attachThread(mainThread=True) |
440 |
431 |
441 # set the system exception handling function to ensure, that |
432 # set the system exception handling function to ensure, that |
442 # we report on all unhandled exceptions |
433 # we report on all unhandled exceptions |
930 "status": 0 if result.wasSuccessful() else 1, |
918 "status": 0 if result.wasSuccessful() else 1, |
931 }) |
919 }) |
932 |
920 |
933 elif method == "RequestUTStop": |
921 elif method == "RequestUTStop": |
934 self.testResult.stop() |
922 self.testResult.stop() |
935 |
|
936 elif method == "ResponseForkTo": |
|
937 # this results from a separate event loop |
|
938 self.fork_child = (params["target"] == 'child') |
|
939 self.eventExit = True |
|
940 |
923 |
941 def __assembleTestCasesList(self, suite, start): |
924 def __assembleTestCasesList(self, suite, start): |
942 """ |
925 """ |
943 Private method to assemble a list of test cases included in a test |
926 Private method to assemble a list of test cases included in a test |
944 suite. |
927 suite. |
2052 self.mainThread.set_trace() |
2035 self.mainThread.set_trace() |
2053 |
2036 |
2054 def startProgInDebugger(self, progargs, wd='', host=None, |
2037 def startProgInDebugger(self, progargs, wd='', host=None, |
2055 port=None, exceptions=True, tracePython=False, |
2038 port=None, exceptions=True, tracePython=False, |
2056 redirect=True, passive=True, |
2039 redirect=True, passive=True, |
2057 multiprocessSupport=False): |
2040 multiprocessSupport=False, codeStr=""): |
2058 """ |
2041 """ |
2059 Public method used to start the remote debugger. |
2042 Public method used to start the remote debugger. |
2060 |
2043 |
2061 @param progargs commandline for the program to be debugged |
2044 @param progargs commandline for the program to be debugged |
2062 (list of strings) |
2045 (list of strings) |
2086 name = os.path.basename(progargs[0]) |
2071 name = os.path.basename(progargs[0]) |
2087 self.connectDebugger(port, remoteAddress, redirect, name=name) |
2072 self.connectDebugger(port, remoteAddress, redirect, name=name) |
2088 |
2073 |
2089 self._fncache = {} |
2074 self._fncache = {} |
2090 self.dircache = [] |
2075 self.dircache = [] |
2091 sys.argv = progargs[:] |
2076 if codeStr: |
2092 sys.argv[0] = os.path.abspath(sys.argv[0]) |
2077 self.running = "<string>" |
2093 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
2078 sys.argv = ["<string>"] + progargs[:] |
2094 if wd == '': |
|
2095 os.chdir(sys.path[1]) |
|
2096 else: |
2079 else: |
2097 os.chdir(wd) |
2080 sys.argv = progargs[:] |
2098 self.running = sys.argv[0] |
2081 sys.argv[0] = os.path.abspath(sys.argv[0]) |
2099 self.__setCoding(self.running) |
2082 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
|
2083 if wd == '': |
|
2084 os.chdir(sys.path[1]) |
|
2085 else: |
|
2086 os.chdir(wd) |
|
2087 self.running = sys.argv[0] |
|
2088 self.__setCoding(self.running) |
2100 self.debugging = True |
2089 self.debugging = True |
2101 self.multiprocessSupport = multiprocessSupport |
2090 self.multiprocessSupport = multiprocessSupport |
2102 |
2091 |
2103 self.passive = passive |
2092 self.passive = passive |
2104 if passive: |
2093 if passive: |
2114 self.__interceptSignals() |
2103 self.__interceptSignals() |
2115 |
2104 |
2116 # This will eventually enter a local event loop. |
2105 # This will eventually enter a local event loop. |
2117 self.debugMod.__dict__['__file__'] = self.running |
2106 self.debugMod.__dict__['__file__'] = self.running |
2118 sys.modules['__main__'] = self.debugMod |
2107 sys.modules['__main__'] = self.debugMod |
2119 code = self.__compileFileSource(self.running) |
2108 if codeStr: |
|
2109 code = self.__compileCommand(codeStr) |
|
2110 else: |
|
2111 code = self.__compileFileSource(self.running) |
2120 if code: |
2112 if code: |
2121 res = self.mainThread.run(code, self.debugMod.__dict__, debug=True) |
2113 res = self.mainThread.run(code, self.debugMod.__dict__, debug=True) |
2122 else: |
2114 else: |
2123 res = 42 # should not happen |
2115 res = 42 # should not happen |
2124 return res |
2116 return res |
2202 redirect = False |
2195 redirect = False |
2203 del args[0] |
2196 del args[0] |
2204 elif args[0] == '--no-encoding': |
2197 elif args[0] == '--no-encoding': |
2205 self.noencoding = True |
2198 self.noencoding = True |
2206 del args[0] |
2199 del args[0] |
2207 elif args[0] == '--fork-child': |
|
2208 self.fork_auto = True |
|
2209 self.fork_child = True |
|
2210 del args[0] |
|
2211 elif args[0] == '--fork-parent': |
|
2212 self.fork_auto = True |
|
2213 self.fork_child = False |
|
2214 del args[0] |
|
2215 elif args[0] == '--no-passive': |
2200 elif args[0] == '--no-passive': |
2216 passive = False |
2201 passive = False |
2217 del args[0] |
2202 del args[0] |
2218 elif args[0] == '--multiprocess': |
2203 elif args[0] == '--multiprocess': |
2219 multiprocess = True |
2204 multiprocess = True |
|
2205 del args[0] |
|
2206 elif args[0] == '--code': |
|
2207 hasCode = True |
2220 del args[0] |
2208 del args[0] |
2221 elif args[0] == '--': |
2209 elif args[0] == '--': |
2222 del args[0] |
2210 del args[0] |
2223 break |
2211 break |
2224 else: # unknown option |
2212 else: # unknown option |
2232 else: |
2220 else: |
2233 # Store options if a process is spawn |
2221 # Store options if a process is spawn |
2234 # TODO: check which ones are really needed |
2222 # TODO: check which ones are really needed |
2235 self.startOptions = ( |
2223 self.startOptions = ( |
2236 wd, host, port, exceptions, tracePython, redirect, |
2224 wd, host, port, exceptions, tracePython, redirect, |
2237 self.noencoding, self.fork_auto, self.fork_child, |
2225 self.noencoding, |
2238 ) |
2226 ) |
2239 if not self.noencoding: |
2227 if not self.noencoding: |
2240 self.__coding = self.defaultCoding |
2228 self.__coding = self.defaultCoding |
2241 ## patchNewProcessFunctions(multiprocess, self) |
2229 patchNewProcessFunctions(multiprocess, self) |
|
2230 if hasCode: |
|
2231 codeStr = args.pop(0) |
|
2232 else: |
|
2233 codeStr="" |
2242 res = self.startProgInDebugger( |
2234 res = self.startProgInDebugger( |
2243 args, wd, host, port, exceptions=exceptions, |
2235 args, wd, host, port, exceptions=exceptions, |
2244 tracePython=tracePython, redirect=redirect, |
2236 tracePython=tracePython, redirect=redirect, |
2245 passive=passive, multiprocessSupport=multiprocess |
2237 passive=passive, multiprocessSupport=multiprocess, |
|
2238 codeStr=codeStr |
2246 ) |
2239 ) |
2247 sys.exit(res) |
2240 sys.exit(res) |
2248 else: |
2241 else: |
2249 if sys.argv[1] == '--no-encoding': |
2242 if sys.argv[1] == '--no-encoding': |
2250 self.noencoding = True |
2243 self.noencoding = True |
2289 if port >= 0: |
2282 if port >= 0: |
2290 # Store options if a process is spawn |
2283 # Store options if a process is spawn |
2291 # TODO: check which ones are really needed |
2284 # TODO: check which ones are really needed |
2292 self.startOptions = ( |
2285 self.startOptions = ( |
2293 '', remoteAddress, port, True, False, redirect, |
2286 '', remoteAddress, port, True, False, redirect, |
2294 self.noencoding, self.fork_auto, self.fork_child, |
2287 self.noencoding, |
2295 ) |
2288 ) |
2296 if not self.noencoding: |
2289 if not self.noencoding: |
2297 self.__coding = self.defaultCoding |
2290 self.__coding = self.defaultCoding |
|
2291 patchNewProcessFunctions(self.multiprocessSupport, self) |
2298 self.connectDebugger(port, remoteAddress, redirect) |
2292 self.connectDebugger(port, remoteAddress, redirect) |
2299 ## patchNewProcessFunctions(self.multiprocessSupport, self) |
|
2300 self.__interact() |
2293 self.__interact() |
2301 else: |
2294 else: |
2302 print("No network port given. Aborting...") |
2295 print("No network port given. Aborting...") |
2303 # __IGNORE_WARNING_M801__ |
2296 # __IGNORE_WARNING_M801__ |
2304 |
|
2305 def fork(self): |
|
2306 """ |
|
2307 Public method implementing a fork routine deciding which branch |
|
2308 to follow. |
|
2309 |
|
2310 @return process ID (integer) |
|
2311 """ |
|
2312 ## if not self.fork_auto: |
|
2313 ## self.sendJsonCommand("RequestForkTo", {}) |
|
2314 ## self.eventLoop(True) |
|
2315 pid = DebugClientOrigFork() |
|
2316 if pid == 0: |
|
2317 # child |
|
2318 ## if not self.fork_child: |
|
2319 sys.settrace(None) |
|
2320 sys.setprofile(None) |
|
2321 self.sessionClose(False) |
|
2322 ## (wd, host, port, exceptions, tracePython, redirect, |
|
2323 ## noencoding, fork_auto, fork_child) = self.startOptions |
|
2324 ## self.startDebugger(sys.argv[0], host, port, |
|
2325 ## exceptions=exceptions, |
|
2326 ## tracePython=tracePython, |
|
2327 ## redirect=redirect, |
|
2328 ## passive=False) |
|
2329 else: |
|
2330 # parent |
|
2331 if self.fork_child: |
|
2332 sys.settrace(None) |
|
2333 sys.setprofile(None) |
|
2334 self.sessionClose(False) |
|
2335 return pid |
|
2336 |
2297 |
2337 def close(self, fd): |
2298 def close(self, fd): |
2338 """ |
2299 """ |
2339 Public method implementing a close method as a replacement for |
2300 Public method implementing a close method as a replacement for |
2340 os.close(). |
2301 os.close(). |