37 self.threadNumber = 1 |
37 self.threadNumber = 1 |
38 self.enableImportHooks = True |
38 self.enableImportHooks = True |
39 self._original_start_new_thread = None |
39 self._original_start_new_thread = None |
40 self.threadingAttached = False |
40 self.threadingAttached = False |
41 self.qtThreadAttached = False |
41 self.qtThreadAttached = False |
|
42 self.greenlet = False |
42 |
43 |
43 self.clientLock = threading.RLock() |
44 self.clientLock = threading.RLock() |
44 |
45 |
45 # dictionary of all threads running {id: DebugBase} |
46 # dictionary of all threads running {id: DebugBase} |
46 self.threads = {_thread.get_ident(): self} |
47 self.threads = {_thread.get_ident(): self} |
62 del sys.modules[self.threadModName] |
63 del sys.modules[self.threadModName] |
63 del sys.modules['threading'] |
64 del sys.modules['threading'] |
64 |
65 |
65 sys.meta_path.insert(0, self) |
66 sys.meta_path.insert(0, self) |
66 |
67 |
67 def attachThread(self, target=None, args=None, kwargs={}, |
68 def attachThread(self, target=None, args=None, kwargs=None, |
68 mainThread=False): |
69 mainThread=False): |
69 """ |
70 """ |
70 Public method to setup a standard thread for DebugClient to debug. |
71 Public method to setup a standard thread for DebugClient to debug. |
71 |
72 |
72 If mainThread is True, then we are attaching to the already |
73 If mainThread is True, then we are attaching to the already |
78 @param kwargs keyword arguments to pass to target |
79 @param kwargs keyword arguments to pass to target |
79 @param mainThread True, if we are attaching to the already |
80 @param mainThread True, if we are attaching to the already |
80 started mainthread of the app |
81 started mainthread of the app |
81 @return identifier of the created thread |
82 @return identifier of the created thread |
82 """ |
83 """ |
|
84 if kwargs is None: |
|
85 kwargs = {} |
|
86 |
83 if mainThread: |
87 if mainThread: |
84 ident = _thread.get_ident() |
88 ident = _thread.get_ident() |
85 name = 'MainThread' |
89 name = 'MainThread' |
86 newThread = self.mainThread |
90 newThread = self.mainThread |
87 newThread.isMainThread = True |
91 newThread.isMainThread = True |
138 try: |
142 try: |
139 self.clientLock.release() |
143 self.clientLock.release() |
140 except AssertionError: |
144 except AssertionError: |
141 pass |
145 pass |
142 |
146 |
143 def setCurrentThread(self, id): |
147 def setCurrentThread(self, threadId): |
144 """ |
148 """ |
145 Public method to set the current thread. |
149 Public method to set the current thread. |
146 |
150 |
147 @param id the id the current thread should be set to. |
151 @param threadId the id the current thread should be set to. |
148 @type int |
152 @type int |
149 """ |
153 """ |
150 try: |
154 try: |
151 self.lockClient() |
155 self.lockClient() |
152 if id is None: |
156 if threadId is None: |
153 self.currentThread = None |
157 self.currentThread = None |
154 else: |
158 else: |
155 self.currentThread = self.threads.get(id) |
159 self.currentThread = self.threads.get(threadId) |
156 finally: |
160 finally: |
157 self.unlockClient() |
161 self.unlockClient() |
158 |
162 |
159 def dumpThreadList(self): |
163 def dumpThreadList(self): |
160 """ |
164 """ |
165 if len(self.threads) > 1: |
169 if len(self.threads) > 1: |
166 currentId = _thread.get_ident() |
170 currentId = _thread.get_ident() |
167 # update thread names set by user (threading.setName) |
171 # update thread names set by user (threading.setName) |
168 threadNames = {t.ident: t.getName() for t in threading.enumerate()} |
172 threadNames = {t.ident: t.getName() for t in threading.enumerate()} |
169 |
173 |
170 for id, thd in self.threads.items(): |
174 for threadId, thd in self.threads.items(): |
171 d = {"id": id} |
175 d = {"id": threadId} |
172 try: |
176 try: |
173 d["name"] = threadNames.get(id, thd.name) |
177 d["name"] = threadNames.get(threadId, thd.name) |
174 d["broken"] = thd.isBroken |
178 d["broken"] = thd.isBroken |
175 except Exception: |
179 except Exception: |
176 d["name"] = 'UnknownThread' |
180 d["name"] = 'UnknownThread' |
177 d["broken"] = False |
181 d["broken"] = False |
178 |
182 |
213 def updateThreadList(self): |
217 def updateThreadList(self): |
214 """ |
218 """ |
215 Public method to update the list of running threads. |
219 Public method to update the list of running threads. |
216 """ |
220 """ |
217 frames = sys._current_frames() |
221 frames = sys._current_frames() |
218 for id, frame in frames.items(): |
222 for threadId, frame in frames.items(): |
219 # skip our own timer thread |
223 # skip our own timer thread |
220 if frame.f_code.co_name == '__eventPollTimer': |
224 if frame.f_code.co_name == '__eventPollTimer': |
221 continue |
225 continue |
222 |
226 |
223 # Unknown thread |
227 # Unknown thread |
224 if id not in self.threads: |
228 if threadId not in self.threads: |
225 newThread = DebugBase(self) |
229 newThread = DebugBase(self) |
226 name = 'Thread-{0}'.format(self.threadNumber) |
230 name = 'Thread-{0}'.format(self.threadNumber) |
227 self.threadNumber += 1 |
231 self.threadNumber += 1 |
228 |
232 |
229 newThread.id = id |
233 newThread.id = threadId |
230 newThread.name = name |
234 newThread.name = name |
231 self.threads[id] = newThread |
235 self.threads[threadId] = newThread |
232 |
236 |
233 # adjust current frame |
237 # adjust current frame |
234 if "__pypy__" not in sys.builtin_module_names: |
238 if "__pypy__" not in sys.builtin_module_names: |
235 # Don't update with None |
239 # Don't update with None |
236 currentFrame = self.getExecutedFrame(frame) |
240 currentFrame = self.getExecutedFrame(frame) |
237 if (currentFrame is not None and |
241 if (currentFrame is not None and |
238 self.threads[id].isBroken is False): |
242 self.threads[threadId].isBroken is False): |
239 self.threads[id].currentFrame = currentFrame |
243 self.threads[threadId].currentFrame = currentFrame |
240 |
244 |
241 # Clean up obsolet because terminated threads |
245 # Clean up obsolet because terminated threads |
242 self.threads = {id_: thrd for id_, thrd in self.threads.items() |
246 self.threads = {id_: thrd for id_, thrd in self.threads.items() |
243 if id_ in frames} |
247 if id_ in frames} |
244 |
248 |
255 """ |
259 """ |
256 if fullname in sys.modules or not self.debugging: |
260 if fullname in sys.modules or not self.debugging: |
257 return None |
261 return None |
258 |
262 |
259 if fullname in [self.threadModName, 'PyQt4.QtCore', 'PyQt5.QtCore', |
263 if fullname in [self.threadModName, 'PyQt4.QtCore', 'PyQt5.QtCore', |
260 'PySide.QtCore', 'PySide2.QtCore', |
264 'PySide.QtCore', 'PySide2.QtCore', 'greenlet', |
261 'threading'] and self.enableImportHooks: |
265 'threading'] and self.enableImportHooks: |
262 # Disable hook to be able to import original module |
266 # Disable hook to be able to import original module |
263 self.enableImportHooks = False |
267 self.enableImportHooks = False |
264 return self |
268 return self |
265 |
269 |
280 self._original_start_new_thread is None): |
284 self._original_start_new_thread is None): |
281 # make thread hooks available to system |
285 # make thread hooks available to system |
282 self._original_start_new_thread = module.start_new_thread |
286 self._original_start_new_thread = module.start_new_thread |
283 module.start_new_thread = self.attachThread |
287 module.start_new_thread = self.attachThread |
284 |
288 |
|
289 elif (fullname == 'greenlet' and self.greenlet is False): |
|
290 # Check for greenlet.settrace |
|
291 if hasattr(module, 'settrace'): |
|
292 self.greenlet = True |
|
293 DebugBase.pollTimerEnabled = False |
|
294 |
|
295 # TODO: Implement the debugger extension for greenlets |
|
296 |
285 # Add hook for threading.run() |
297 # Add hook for threading.run() |
286 elif (fullname == "threading" and self.threadingAttached is False): |
298 elif (fullname == "threading" and self.threadingAttached is False): |
287 self.threadingAttached = True |
299 self.threadingAttached = True |
288 |
300 |
289 # _debugClient as a class attribute can't be accessed in following |
301 # _debugClient as a class attribute can't be accessed in following |
295 Bootstrap for threading, which reports exceptions correctly. |
307 Bootstrap for threading, which reports exceptions correctly. |
296 |
308 |
297 @param run the run method of threading.Thread |
309 @param run the run method of threading.Thread |
298 @type method pointer |
310 @type method pointer |
299 """ |
311 """ |
300 newThread = _debugClient.threads[self.ident] |
312 newThread = DebugBase(_debugClient) |
|
313 _debugClient.threads[self.ident] = newThread |
301 newThread.name = self.name |
314 newThread.name = self.name |
302 # see DebugBase.bootstrap |
315 # see DebugBase.bootstrap |
303 sys.settrace(newThread.trace_dispatch) |
316 sys.settrace(newThread.trace_dispatch) |
304 try: |
317 try: |
305 run() |
318 run() |