]>
Commit | Line | Data |
---|---|---|
50c0c017 PA |
1 | /* Continuations for GDB, the GNU debugger. |
2 | ||
3 | Copyright (C) 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, | |
4 | 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, | |
5 | 2009, 2010, 2011 Free Software Foundation, Inc. | |
6 | ||
7 | This file is part of GDB. | |
8 | ||
9 | This program is free software; you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation; either version 3 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
21 | ||
22 | #include "defs.h" | |
23 | #include "gdbthread.h" | |
24 | #include "inferior.h" | |
25 | ||
26 | struct continuation | |
27 | { | |
28 | struct continuation *next; | |
29 | void (*function) (void *); | |
30 | void (*free_arg) (void *); | |
31 | void *arg; | |
32 | }; | |
33 | ||
34 | typedef void (make_continuation_ftype) (void *); | |
35 | ||
36 | /* Add a new continuation to the continuation chain, and return the | |
37 | previous chain pointer to be passed later to do_continuations or | |
38 | discard_continuations. Args are FUNCTION to run the continuation | |
39 | up with, and ARG to pass to it. */ | |
40 | ||
41 | static struct continuation * | |
42 | make_continuation (struct continuation **pmy_chain, | |
43 | make_continuation_ftype *function, | |
44 | void *arg, void (*free_arg) (void *)) | |
45 | { | |
46 | struct continuation *new = XNEW (struct continuation); | |
47 | struct continuation *old_chain = *pmy_chain; | |
48 | ||
49 | new->next = *pmy_chain; | |
50 | new->function = function; | |
51 | new->free_arg = free_arg; | |
52 | new->arg = arg; | |
53 | *pmy_chain = new; | |
54 | ||
55 | return old_chain; | |
56 | } | |
57 | ||
58 | static void | |
59 | do_my_continuations (struct continuation **pmy_chain, | |
60 | struct continuation *old_chain) | |
61 | { | |
62 | struct continuation *ptr; | |
63 | ||
64 | while ((ptr = *pmy_chain) != old_chain) | |
65 | { | |
66 | *pmy_chain = ptr->next; /* Do this first in case of recursion. */ | |
67 | (*ptr->function) (ptr->arg); | |
68 | if (ptr->free_arg) | |
69 | (*ptr->free_arg) (ptr->arg); | |
70 | xfree (ptr); | |
71 | } | |
72 | } | |
73 | ||
74 | void | |
75 | discard_my_continuations (struct continuation **pmy_chain, | |
76 | struct continuation *old_chain) | |
77 | { | |
78 | struct continuation *ptr; | |
79 | ||
80 | while ((ptr = *pmy_chain) != old_chain) | |
81 | { | |
82 | *pmy_chain = ptr->next; | |
83 | if (ptr->free_arg) | |
84 | (*ptr->free_arg) (ptr->arg); | |
85 | xfree (ptr); | |
86 | } | |
87 | } | |
88 | ||
89 | /* Add a continuation to the continuation list of THREAD. The new | |
90 | continuation will be added at the front. */ | |
91 | ||
92 | void | |
93 | add_continuation (struct thread_info *thread, | |
94 | void (*continuation_hook) (void *), void *args, | |
95 | void (*continuation_free_args) (void *)) | |
96 | { | |
97 | struct continuation *continuations = thread->continuations; | |
98 | make_cleanup_ftype *continuation_hook_fn = continuation_hook; | |
99 | ||
100 | make_continuation (&continuations, | |
101 | continuation_hook_fn, | |
102 | args, | |
103 | continuation_free_args); | |
104 | ||
105 | thread->continuations = continuations; | |
106 | } | |
107 | ||
108 | /* Add a continuation to the continuation list of INFERIOR. The new | |
109 | continuation will be added at the front. */ | |
110 | ||
111 | void | |
112 | add_inferior_continuation (void (*continuation_hook) (void *), void *args, | |
113 | void (*continuation_free_args) (void *)) | |
114 | { | |
115 | struct inferior *inf = current_inferior (); | |
116 | struct continuation *continuations = inf->continuations; | |
117 | make_cleanup_ftype *continuation_hook_fn = continuation_hook; | |
118 | ||
119 | make_continuation (&continuations, | |
120 | continuation_hook_fn, | |
121 | args, | |
122 | continuation_free_args); | |
123 | ||
124 | inf->continuations = continuations; | |
125 | } | |
126 | ||
127 | /* Do all continuations of the current inferior. */ | |
128 | ||
129 | void | |
130 | do_all_inferior_continuations (void) | |
131 | { | |
132 | struct continuation *continuations; | |
133 | struct inferior *inf = current_inferior (); | |
134 | ||
135 | if (inf->continuations == NULL) | |
136 | return; | |
137 | ||
138 | /* Copy the list header into another pointer, and set the global | |
139 | list header to null, so that the global list can change as a side | |
140 | effect of invoking the continuations and the processing of the | |
141 | preexisting continuations will not be affected. */ | |
142 | ||
143 | continuations = inf->continuations; | |
144 | inf->continuations = NULL; | |
145 | ||
146 | /* Work now on the list we have set aside. */ | |
147 | do_my_continuations (&continuations, NULL); | |
148 | } | |
149 | ||
150 | /* Get rid of all the inferior-wide continuations of INF. */ | |
151 | ||
152 | void | |
153 | discard_all_inferior_continuations (struct inferior *inf) | |
154 | { | |
155 | struct continuation *continuation_ptr = inf->continuations; | |
156 | ||
157 | discard_my_continuations (&continuation_ptr, NULL); | |
158 | inf->continuations = NULL; | |
159 | } | |
160 | ||
161 | static void | |
162 | restore_thread_cleanup (void *arg) | |
163 | { | |
164 | ptid_t *ptid_p = arg; | |
165 | ||
166 | switch_to_thread (*ptid_p); | |
167 | } | |
168 | ||
169 | /* Walk down the continuation list of PTID, and execute all the | |
170 | continuations. There is a problem though. In some cases new | |
171 | continuations may be added while we are in the middle of this loop. | |
172 | If this happens they will be added in the front, and done before we | |
173 | have a chance of exhausting those that were already there. We need | |
174 | to then save the beginning of the list in a pointer and do the | |
175 | continuations from there on, instead of using the global beginning | |
176 | of list as our iteration pointer. */ | |
177 | ||
178 | static void | |
179 | do_all_continuations_ptid (ptid_t ptid, | |
180 | struct continuation **continuations_p) | |
181 | { | |
182 | struct cleanup *old_chain; | |
183 | struct continuation *continuations; | |
184 | ptid_t current_thread; | |
185 | ||
186 | if (*continuations_p == NULL) | |
187 | return; | |
188 | ||
189 | current_thread = inferior_ptid; | |
190 | ||
191 | /* Restore selected thread on exit. Don't try to restore the frame | |
192 | as well, because: | |
193 | ||
194 | - When running continuations, the selected frame is always #0. | |
195 | ||
196 | - The continuations may trigger symbol file loads, which may | |
197 | change the frame layout (frame ids change), which would trigger | |
198 | a warning if we used make_cleanup_restore_current_thread. */ | |
199 | ||
200 | old_chain = make_cleanup (restore_thread_cleanup, ¤t_thread); | |
201 | ||
202 | /* Let the continuation see this thread as selected. */ | |
203 | switch_to_thread (ptid); | |
204 | ||
205 | /* Copy the list header into another pointer, and set the global | |
206 | list header to null, so that the global list can change as a side | |
207 | effect of invoking the continuations and the processing of the | |
208 | preexisting continuations will not be affected. */ | |
209 | ||
210 | continuations = *continuations_p; | |
211 | *continuations_p = NULL; | |
212 | ||
213 | /* Work now on the list we have set aside. */ | |
214 | do_my_continuations (&continuations, NULL); | |
215 | ||
216 | do_cleanups (old_chain); | |
217 | } | |
218 | ||
219 | /* Callback for iterate over threads. */ | |
220 | ||
221 | static int | |
222 | do_all_continuations_thread_callback (struct thread_info *thread, void *data) | |
223 | { | |
224 | do_all_continuations_ptid (thread->ptid, &thread->continuations); | |
225 | return 0; | |
226 | } | |
227 | ||
228 | /* Do all continuations of thread THREAD. */ | |
229 | ||
230 | void | |
231 | do_all_continuations_thread (struct thread_info *thread) | |
232 | { | |
233 | do_all_continuations_thread_callback (thread, NULL); | |
234 | } | |
235 | ||
236 | /* Do all continuations of all threads. */ | |
237 | ||
238 | void | |
239 | do_all_continuations (void) | |
240 | { | |
241 | iterate_over_threads (do_all_continuations_thread_callback, NULL); | |
242 | } | |
243 | ||
244 | /* Callback for iterate over threads. */ | |
245 | ||
246 | static int | |
247 | discard_all_continuations_thread_callback (struct thread_info *thread, | |
248 | void *data) | |
249 | { | |
250 | struct continuation *continuation_ptr = thread->continuations; | |
251 | ||
252 | discard_my_continuations (&continuation_ptr, NULL); | |
253 | thread->continuations = NULL; | |
254 | return 0; | |
255 | } | |
256 | ||
257 | /* Get rid of all the continuations of THREAD. */ | |
258 | ||
259 | void | |
260 | discard_all_continuations_thread (struct thread_info *thread) | |
261 | { | |
262 | discard_all_continuations_thread_callback (thread, NULL); | |
263 | } | |
264 | ||
265 | /* Get rid of all the continuations of all threads. */ | |
266 | ||
267 | void | |
268 | discard_all_continuations (void) | |
269 | { | |
270 | iterate_over_threads (discard_all_continuations_thread_callback, NULL); | |
271 | } | |
272 | ||
273 | ||
274 | /* Add a continuation to the intermediate continuation list of THREAD. | |
275 | The new continuation will be added at the front. */ | |
276 | ||
277 | void | |
278 | add_intermediate_continuation (struct thread_info *thread, | |
279 | void (*continuation_hook) | |
280 | (void *), void *args, | |
281 | void (*continuation_free_args) (void *)) | |
282 | { | |
283 | struct continuation *continuations = thread->intermediate_continuations; | |
284 | make_cleanup_ftype *continuation_hook_fn = continuation_hook; | |
285 | ||
286 | make_continuation (&continuations, | |
287 | continuation_hook_fn, | |
288 | args, | |
289 | continuation_free_args); | |
290 | ||
291 | thread->intermediate_continuations = continuations; | |
292 | } | |
293 | ||
294 | /* Walk down the cmd_continuation list, and execute all the | |
295 | continuations. There is a problem though. In some cases new | |
296 | continuations may be added while we are in the middle of this | |
297 | loop. If this happens they will be added in the front, and done | |
298 | before we have a chance of exhausting those that were already | |
299 | there. We need to then save the beginning of the list in a pointer | |
300 | and do the continuations from there on, instead of using the | |
301 | global beginning of list as our iteration pointer. */ | |
302 | ||
303 | static int | |
304 | do_all_intermediate_continuations_thread_callback (struct thread_info *thread, | |
305 | void *data) | |
306 | { | |
307 | do_all_continuations_ptid (thread->ptid, | |
308 | &thread->intermediate_continuations); | |
309 | return 0; | |
310 | } | |
311 | ||
312 | /* Do all intermediate continuations of thread THREAD. */ | |
313 | ||
314 | void | |
315 | do_all_intermediate_continuations_thread (struct thread_info *thread) | |
316 | { | |
317 | do_all_intermediate_continuations_thread_callback (thread, NULL); | |
318 | } | |
319 | ||
320 | /* Do all intermediate continuations of all threads. */ | |
321 | ||
322 | void | |
323 | do_all_intermediate_continuations (void) | |
324 | { | |
325 | iterate_over_threads (do_all_intermediate_continuations_thread_callback, | |
326 | NULL); | |
327 | } | |
328 | ||
329 | /* Callback for iterate over threads. */ | |
330 | ||
331 | static int | |
332 | discard_all_intermediate_continuations_thread_callback (struct thread_info *thread, | |
333 | void *data) | |
334 | { | |
335 | struct continuation *continuation_ptr = thread->intermediate_continuations; | |
336 | ||
337 | discard_my_continuations (&continuation_ptr, NULL); | |
338 | thread->intermediate_continuations = NULL; | |
339 | return 0; | |
340 | } | |
341 | ||
342 | /* Get rid of all the intermediate continuations of THREAD. */ | |
343 | ||
344 | void | |
345 | discard_all_intermediate_continuations_thread (struct thread_info *thread) | |
346 | { | |
347 | discard_all_intermediate_continuations_thread_callback (thread, NULL); | |
348 | } | |
349 | ||
350 | /* Get rid of all the intermediate continuations of all threads. */ | |
351 | ||
352 | void | |
353 | discard_all_intermediate_continuations (void) | |
354 | { | |
355 | iterate_over_threads (discard_all_intermediate_continuations_thread_callback, | |
356 | NULL); | |
357 | } |