35 self._original_start_new_thread = None |
35 self._original_start_new_thread = None |
36 |
36 |
37 self.clientLock = threading.RLock() |
37 self.clientLock = threading.RLock() |
38 |
38 |
39 # dictionary of all threads running {id: DebugBase} |
39 # dictionary of all threads running {id: DebugBase} |
40 self.threads = {} |
40 self.threads = {_thread.get_ident(): self} |
41 |
41 |
42 # the "current" thread, basically for variables view |
42 # the "current" thread, basically for variables view |
43 self.currentThread = self |
43 self.currentThread = self |
44 |
44 |
45 # special objects representing the main scripts thread and frame |
45 # special objects representing the main scripts thread and frame |
57 # provide a hook to perform a hard breakpoint |
57 # provide a hook to perform a hard breakpoint |
58 # Use it like this: |
58 # Use it like this: |
59 # if hasattr(sys, 'breakpoint): sys.breakpoint() |
59 # if hasattr(sys, 'breakpoint): sys.breakpoint() |
60 sys.breakpoint = self.set_trace |
60 sys.breakpoint = self.set_trace |
61 |
61 |
62 def attachThread(self, target=None, args=None, kwargs=None, |
62 def attachThread(self, target=None, args=None, kwargs={}, |
63 mainThread=False): |
63 mainThread=False): |
64 """ |
64 """ |
65 Public method to setup a thread for DebugClient to debug. |
65 Public method to setup a standard thread for DebugClient to debug. |
66 |
66 |
67 If mainThread is True, then we are attaching to the already |
67 If mainThread is True, then we are attaching to the already |
68 started mainthread of the app and the rest of the args are ignored. |
68 started mainthread of the app and the rest of the args are ignored. |
69 |
69 |
70 @param target the start function of the target thread (i.e. the |
70 @param target the start function of the target thread (i.e. the user |
71 user code) |
71 code) |
72 @param args arguments to pass to target |
72 @param args arguments to pass to target |
73 @param kwargs keyword arguments to pass to target |
73 @param kwargs keyword arguments to pass to target |
74 @param mainThread True, if we are attaching to the already |
74 @param mainThread True, if we are attaching to the already |
75 started mainthread of the app |
75 started mainthread of the app |
76 @return identifier of the created thread |
76 @return identifier of the created thread |
77 """ |
77 """ |
78 return |
78 if mainThread: |
79 try: |
79 ident = _thread.get_ident() |
80 self.lockClient() |
80 name = 'MainThread' |
81 newThread = DebugThread(self, target, args, kwargs, mainThread) |
81 newThread = self.mainThread |
82 ident = -1 |
82 newThread._mainThread = True |
83 if mainThread: |
83 if self.debugging: |
84 ident = _thread.get_ident() |
84 sys.setprofile(newThread.profile) |
85 self.mainThread = newThread |
85 |
86 if self.debugging: |
86 else: |
87 sys.setprofile(newThread.profile) |
87 newThread = DebugBase(self) |
88 else: |
88 ident = self._original_start_new_thread( |
89 ident = _original_start_thread(newThread.bootstrap, ()) |
89 newThread.bootstrap, (target, args, kwargs)) |
90 if self.mainThread is not None: |
90 name = 'Thread-{0}'.format(self.threadNumber) |
91 self.tracePython = self.mainThread.tracePython |
91 self.threadNumber += 1 |
92 newThread.set_ident(ident) |
92 |
93 self.threads[newThread.get_ident()] = newThread |
93 newThread.id = ident |
94 finally: |
94 newThread.name = name |
95 self.unlockClient() |
95 |
|
96 self.threads[ident] = newThread |
|
97 |
96 return ident |
98 return ident |
97 |
99 |
98 def threadTerminated(self, dbgThread): |
100 def threadTerminated(self, dbgThread): |
99 """ |
101 """ |
100 Public method called when a DebugThread has exited. |
102 Public method called when a DebugThread has exited. |
150 def dumpThreadList(self): |
152 def dumpThreadList(self): |
151 """ |
153 """ |
152 Public method to send the list of threads. |
154 Public method to send the list of threads. |
153 """ |
155 """ |
154 threadList = [] |
156 threadList = [] |
155 if self.threads and self.currentThread: |
157 if len(self.threads) > 1: |
156 # indication for the threaded debugger |
158 currentId = _thread.get_ident() |
157 currentId = self.currentThread.get_ident() |
159 # update thread names set by user (threading.setName) |
158 for t in self.threads.values(): |
160 threadNames = {t.ident: t.getName() for t in threading.enumerate()} |
159 d = {} |
161 |
160 d["id"] = t.get_ident() |
162 for id, thd in self.threads.items(): |
161 d["name"] = t.get_name() |
163 d = {"id": id} |
162 d["broken"] = t.isBroken |
164 try: |
|
165 d["name"] = threadNames.get(id, thd.name) |
|
166 d["broken"] = thd.isBroken |
|
167 except Exception: |
|
168 d["name"] = 'UnknownThread' |
|
169 d["broken"] = False |
|
170 |
163 threadList.append(d) |
171 threadList.append(d) |
164 else: |
172 else: |
165 currentId = -1 |
173 currentId = -1 |
166 d = {} |
174 d = {"id": -1} |
167 d["id"] = -1 |
|
168 d["name"] = "MainThread" |
175 d["name"] = "MainThread" |
169 if hasattr(self, "isBroken"): |
176 d["broken"] = self.isBroken |
170 d["broken"] = self.isBroken |
|
171 else: |
|
172 d["broken"] = False |
|
173 threadList.append(d) |
177 threadList.append(d) |
174 |
178 |
175 self.sendJsonCommand("ResponseThreadList", { |
179 self.sendJsonCommand("ResponseThreadList", { |
176 "currentID": currentId, |
180 "currentID": currentId, |
177 "threadList": threadList, |
181 "threadList": threadList, |