]>
Commit | Line | Data |
---|---|---|
c906108c | 1 | /* Multi-process/thread control for GDB, the GNU debugger. |
b6ba6518 KB |
2 | Copyright 1986, 1987, 1988, 1993, 1994, 1995, 1996, 1997, 1998, 1999, |
3 | 2000, 2001 | |
c906108c | 4 | Free Software Foundation, Inc. |
b6ba6518 KB |
5 | Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA. |
6 | ||
c906108c | 7 | |
c5aa993b | 8 | This file is part of GDB. |
c906108c | 9 | |
c5aa993b JM |
10 | This program is free software; you can redistribute it and/or modify |
11 | it under the terms of the GNU General Public License as published by | |
12 | the Free Software Foundation; either version 2 of the License, or | |
13 | (at your option) any later version. | |
c906108c | 14 | |
c5aa993b JM |
15 | This program is distributed in the hope that it will be useful, |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | GNU General Public License for more details. | |
c906108c | 19 | |
c5aa993b JM |
20 | You should have received a copy of the GNU General Public License |
21 | along with this program; if not, write to the Free Software | |
22 | Foundation, Inc., 59 Temple Place - Suite 330, | |
23 | Boston, MA 02111-1307, USA. */ | |
c906108c SS |
24 | |
25 | #include "defs.h" | |
26 | #include "symtab.h" | |
27 | #include "frame.h" | |
28 | #include "inferior.h" | |
29 | #include "environ.h" | |
30 | #include "value.h" | |
31 | #include "target.h" | |
32 | #include "gdbthread.h" | |
33 | #include "command.h" | |
34 | #include "gdbcmd.h" | |
4e052eda | 35 | #include "regcache.h" |
c906108c SS |
36 | |
37 | #include <ctype.h> | |
38 | #include <sys/types.h> | |
39 | #include <signal.h> | |
8b93c638 JM |
40 | #ifdef UI_OUT |
41 | #include "ui-out.h" | |
42 | #endif | |
c906108c | 43 | |
c5aa993b | 44 | /*#include "lynxos-core.h" */ |
c906108c | 45 | |
0d06e24b | 46 | /* Definition of struct thread_info exported to gdbthread.h */ |
c906108c SS |
47 | |
48 | /* Prototypes for exported functions. */ | |
49 | ||
a14ed312 | 50 | void _initialize_thread (void); |
c906108c SS |
51 | |
52 | /* Prototypes for local functions. */ | |
53 | ||
c906108c SS |
54 | static struct thread_info *thread_list = NULL; |
55 | static int highest_thread_num; | |
56 | ||
a14ed312 | 57 | static struct thread_info *find_thread_id (int num); |
c906108c | 58 | |
a14ed312 KB |
59 | static void thread_command (char *tidstr, int from_tty); |
60 | static void thread_apply_all_command (char *, int); | |
61 | static int thread_alive (struct thread_info *); | |
62 | static void info_threads_command (char *, int); | |
63 | static void thread_apply_command (char *, int); | |
64 | static void restore_current_thread (int); | |
65 | static void switch_to_thread (int pid); | |
66 | static void prune_threads (void); | |
c906108c | 67 | |
7c952b6d ND |
68 | static void |
69 | free_thread (struct thread_info *tp) | |
70 | { | |
71 | /* NOTE: this will take care of any left-over step_resume breakpoints, | |
72 | but not any user-specified thread-specific breakpoints. */ | |
73 | if (tp->step_resume_breakpoint) | |
74 | delete_breakpoint (tp->step_resume_breakpoint); | |
75 | ||
76 | /* FIXME: do I ever need to call the back-end to give it a | |
77 | chance at this private data before deleting the thread? */ | |
78 | if (tp->private) | |
b8c9b27d | 79 | xfree (tp->private); |
7c952b6d | 80 | |
b8c9b27d | 81 | xfree (tp); |
7c952b6d ND |
82 | } |
83 | ||
c906108c | 84 | void |
fba45db2 | 85 | init_thread_list (void) |
c906108c SS |
86 | { |
87 | struct thread_info *tp, *tpnext; | |
88 | ||
7c952b6d | 89 | highest_thread_num = 0; |
c906108c SS |
90 | if (!thread_list) |
91 | return; | |
92 | ||
93 | for (tp = thread_list; tp; tp = tpnext) | |
94 | { | |
95 | tpnext = tp->next; | |
7c952b6d | 96 | free_thread (tp); |
c906108c SS |
97 | } |
98 | ||
99 | thread_list = NULL; | |
c906108c SS |
100 | } |
101 | ||
0d06e24b JM |
102 | /* add_thread now returns a pointer to the new thread_info, |
103 | so that back_ends can initialize their private data. */ | |
104 | ||
105 | struct thread_info * | |
fba45db2 | 106 | add_thread (int pid) |
c906108c SS |
107 | { |
108 | struct thread_info *tp; | |
109 | ||
110 | tp = (struct thread_info *) xmalloc (sizeof (struct thread_info)); | |
111 | ||
112 | tp->pid = pid; | |
113 | tp->num = ++highest_thread_num; | |
114 | tp->prev_pc = 0; | |
115 | tp->prev_func_start = 0; | |
116 | tp->prev_func_name = NULL; | |
117 | tp->step_range_start = 0; | |
118 | tp->step_range_end = 0; | |
c5aa993b | 119 | tp->step_frame_address = 0; |
c906108c SS |
120 | tp->step_resume_breakpoint = 0; |
121 | tp->through_sigtramp_breakpoint = 0; | |
122 | tp->handling_longjmp = 0; | |
123 | tp->trap_expected = 0; | |
124 | tp->another_trap = 0; | |
125 | tp->stepping_through_solib_after_catch = 0; | |
126 | tp->stepping_through_solib_catchpoints = NULL; | |
127 | tp->stepping_through_sigtramp = 0; | |
128 | tp->next = thread_list; | |
c5394b80 | 129 | tp->private = NULL; |
c906108c | 130 | thread_list = tp; |
0d06e24b | 131 | return tp; |
c906108c SS |
132 | } |
133 | ||
134 | void | |
fba45db2 | 135 | delete_thread (int pid) |
c906108c SS |
136 | { |
137 | struct thread_info *tp, *tpprev; | |
138 | ||
139 | tpprev = NULL; | |
140 | ||
141 | for (tp = thread_list; tp; tpprev = tp, tp = tp->next) | |
142 | if (tp->pid == pid) | |
143 | break; | |
144 | ||
145 | if (!tp) | |
146 | return; | |
147 | ||
148 | if (tpprev) | |
149 | tpprev->next = tp->next; | |
150 | else | |
151 | thread_list = tp->next; | |
152 | ||
7c952b6d | 153 | free_thread (tp); |
c906108c SS |
154 | } |
155 | ||
156 | static struct thread_info * | |
fba45db2 | 157 | find_thread_id (int num) |
c906108c SS |
158 | { |
159 | struct thread_info *tp; | |
160 | ||
161 | for (tp = thread_list; tp; tp = tp->next) | |
162 | if (tp->num == num) | |
163 | return tp; | |
164 | ||
165 | return NULL; | |
166 | } | |
167 | ||
0d06e24b JM |
168 | /* Find a thread_info by matching 'pid'. */ |
169 | struct thread_info * | |
fba45db2 | 170 | find_thread_pid (int pid) |
0d06e24b JM |
171 | { |
172 | struct thread_info *tp; | |
173 | ||
174 | for (tp = thread_list; tp; tp = tp->next) | |
175 | if (tp->pid == pid) | |
176 | return tp; | |
177 | ||
178 | return NULL; | |
179 | } | |
180 | ||
181 | /* | |
182 | * Thread iterator function. | |
183 | * | |
184 | * Calls a callback function once for each thread, so long as | |
185 | * the callback function returns false. If the callback function | |
186 | * returns true, the iteration will end and the current thread | |
187 | * will be returned. This can be useful for implementing a | |
188 | * search for a thread with arbitrary attributes, or for applying | |
189 | * some operation to every thread. | |
190 | * | |
191 | * FIXME: some of the existing functionality, such as | |
192 | * "Thread apply all", might be rewritten using this functionality. | |
193 | */ | |
194 | ||
195 | struct thread_info * | |
fd118b61 KB |
196 | iterate_over_threads (int (*callback) (struct thread_info *, void *), |
197 | void *data) | |
0d06e24b JM |
198 | { |
199 | struct thread_info *tp; | |
200 | ||
201 | for (tp = thread_list; tp; tp = tp->next) | |
202 | if ((*callback) (tp, data)) | |
203 | return tp; | |
204 | ||
205 | return NULL; | |
206 | } | |
207 | ||
c906108c | 208 | int |
fba45db2 | 209 | valid_thread_id (int num) |
c906108c SS |
210 | { |
211 | struct thread_info *tp; | |
212 | ||
213 | for (tp = thread_list; tp; tp = tp->next) | |
214 | if (tp->num == num) | |
215 | return 1; | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | int | |
fba45db2 | 221 | pid_to_thread_id (int pid) |
c906108c SS |
222 | { |
223 | struct thread_info *tp; | |
224 | ||
225 | for (tp = thread_list; tp; tp = tp->next) | |
226 | if (tp->pid == pid) | |
227 | return tp->num; | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | int | |
fba45db2 | 233 | thread_id_to_pid (int num) |
c906108c SS |
234 | { |
235 | struct thread_info *thread = find_thread_id (num); | |
236 | if (thread) | |
237 | return thread->pid; | |
238 | else | |
239 | return -1; | |
240 | } | |
241 | ||
242 | int | |
fba45db2 | 243 | in_thread_list (int pid) |
c906108c SS |
244 | { |
245 | struct thread_info *tp; | |
246 | ||
247 | for (tp = thread_list; tp; tp = tp->next) | |
248 | if (tp->pid == pid) | |
249 | return 1; | |
250 | ||
251 | return 0; /* Never heard of 'im */ | |
252 | } | |
8b93c638 JM |
253 | #ifdef UI_OUT |
254 | /* Print a list of thread ids currently known, and the total number of | |
255 | threads. To be used from within catch_errors. */ | |
256 | static int | |
257 | do_captured_list_thread_ids (void *arg) | |
258 | { | |
259 | struct thread_info *tp; | |
260 | int num = 0; | |
261 | ||
262 | ui_out_list_begin (uiout, "thread-ids"); | |
263 | ||
264 | for (tp = thread_list; tp; tp = tp->next) | |
265 | { | |
266 | num++; | |
267 | ui_out_field_int (uiout, "thread-id", tp->num); | |
268 | } | |
269 | ||
270 | ui_out_list_end (uiout); | |
271 | ui_out_field_int (uiout, "number-of-threads", num); | |
272 | return GDB_RC_OK; | |
273 | } | |
274 | ||
275 | /* Official gdblib interface function to get a list of thread ids and | |
276 | the total number. */ | |
277 | enum gdb_rc | |
278 | gdb_list_thread_ids (/* output object */) | |
279 | { | |
280 | return catch_errors (do_captured_list_thread_ids, NULL, | |
281 | NULL, RETURN_MASK_ALL); | |
282 | } | |
283 | #endif | |
c906108c SS |
284 | |
285 | /* Load infrun state for the thread PID. */ | |
286 | ||
c5aa993b | 287 | void |
fba45db2 KB |
288 | load_infrun_state (int pid, CORE_ADDR *prev_pc, CORE_ADDR *prev_func_start, |
289 | char **prev_func_name, int *trap_expected, | |
290 | struct breakpoint **step_resume_breakpoint, | |
291 | struct breakpoint **through_sigtramp_breakpoint, | |
292 | CORE_ADDR *step_range_start, CORE_ADDR *step_range_end, | |
293 | CORE_ADDR *step_frame_address, int *handling_longjmp, | |
294 | int *another_trap, int *stepping_through_solib_after_catch, | |
295 | bpstat *stepping_through_solib_catchpoints, | |
296 | int *stepping_through_sigtramp) | |
c906108c SS |
297 | { |
298 | struct thread_info *tp; | |
299 | ||
300 | /* If we can't find the thread, then we're debugging a single threaded | |
301 | process. No need to do anything in that case. */ | |
302 | tp = find_thread_id (pid_to_thread_id (pid)); | |
303 | if (tp == NULL) | |
304 | return; | |
305 | ||
306 | *prev_pc = tp->prev_pc; | |
307 | *prev_func_start = tp->prev_func_start; | |
308 | *prev_func_name = tp->prev_func_name; | |
309 | *step_resume_breakpoint = tp->step_resume_breakpoint; | |
310 | *step_range_start = tp->step_range_start; | |
311 | *step_range_end = tp->step_range_end; | |
312 | *step_frame_address = tp->step_frame_address; | |
313 | *through_sigtramp_breakpoint = tp->through_sigtramp_breakpoint; | |
314 | *handling_longjmp = tp->handling_longjmp; | |
315 | *trap_expected = tp->trap_expected; | |
316 | *another_trap = tp->another_trap; | |
317 | *stepping_through_solib_after_catch = tp->stepping_through_solib_after_catch; | |
318 | *stepping_through_solib_catchpoints = tp->stepping_through_solib_catchpoints; | |
319 | *stepping_through_sigtramp = tp->stepping_through_sigtramp; | |
320 | } | |
321 | ||
322 | /* Save infrun state for the thread PID. */ | |
323 | ||
c5aa993b | 324 | void |
fba45db2 KB |
325 | save_infrun_state (int pid, CORE_ADDR prev_pc, CORE_ADDR prev_func_start, |
326 | char *prev_func_name, int trap_expected, | |
327 | struct breakpoint *step_resume_breakpoint, | |
328 | struct breakpoint *through_sigtramp_breakpoint, | |
329 | CORE_ADDR step_range_start, CORE_ADDR step_range_end, | |
330 | CORE_ADDR step_frame_address, int handling_longjmp, | |
331 | int another_trap, int stepping_through_solib_after_catch, | |
332 | bpstat stepping_through_solib_catchpoints, | |
333 | int stepping_through_sigtramp) | |
c906108c SS |
334 | { |
335 | struct thread_info *tp; | |
336 | ||
337 | /* If we can't find the thread, then we're debugging a single-threaded | |
338 | process. Nothing to do in that case. */ | |
339 | tp = find_thread_id (pid_to_thread_id (pid)); | |
340 | if (tp == NULL) | |
341 | return; | |
342 | ||
343 | tp->prev_pc = prev_pc; | |
344 | tp->prev_func_start = prev_func_start; | |
345 | tp->prev_func_name = prev_func_name; | |
346 | tp->step_resume_breakpoint = step_resume_breakpoint; | |
347 | tp->step_range_start = step_range_start; | |
348 | tp->step_range_end = step_range_end; | |
349 | tp->step_frame_address = step_frame_address; | |
350 | tp->through_sigtramp_breakpoint = through_sigtramp_breakpoint; | |
351 | tp->handling_longjmp = handling_longjmp; | |
352 | tp->trap_expected = trap_expected; | |
353 | tp->another_trap = another_trap; | |
354 | tp->stepping_through_solib_after_catch = stepping_through_solib_after_catch; | |
355 | tp->stepping_through_solib_catchpoints = stepping_through_solib_catchpoints; | |
356 | tp->stepping_through_sigtramp = stepping_through_sigtramp; | |
357 | } | |
358 | ||
359 | /* Return true if TP is an active thread. */ | |
360 | static int | |
fba45db2 | 361 | thread_alive (struct thread_info *tp) |
c906108c SS |
362 | { |
363 | if (tp->pid == -1) | |
364 | return 0; | |
c5aa993b | 365 | if (!target_thread_alive (tp->pid)) |
c906108c | 366 | { |
c5aa993b | 367 | tp->pid = -1; /* Mark it as dead */ |
c906108c SS |
368 | return 0; |
369 | } | |
370 | return 1; | |
371 | } | |
372 | ||
373 | static void | |
fba45db2 | 374 | prune_threads (void) |
c906108c | 375 | { |
d4f3574e | 376 | struct thread_info *tp, *next; |
c906108c | 377 | |
c906108c SS |
378 | for (tp = thread_list; tp; tp = next) |
379 | { | |
380 | next = tp->next; | |
381 | if (!thread_alive (tp)) | |
53a5351d | 382 | delete_thread (tp->pid); |
c906108c SS |
383 | } |
384 | } | |
385 | ||
386 | /* Print information about currently known threads | |
c5aa993b | 387 | |
c906108c SS |
388 | * Note: this has the drawback that it _really_ switches |
389 | * threads, which frees the frame cache. A no-side | |
390 | * effects info-threads command would be nicer. | |
391 | */ | |
392 | ||
393 | static void | |
fba45db2 | 394 | info_threads_command (char *arg, int from_tty) |
c906108c SS |
395 | { |
396 | struct thread_info *tp; | |
c5aa993b JM |
397 | int current_pid; |
398 | struct frame_info *cur_frame; | |
399 | int saved_frame_level = selected_frame_level; | |
400 | int counter; | |
0d06e24b | 401 | char *extra_info; |
c906108c SS |
402 | |
403 | /* Avoid coredumps which would happen if we tried to access a NULL | |
404 | selected_frame. */ | |
c5aa993b JM |
405 | if (!target_has_stack) |
406 | error ("No stack."); | |
c906108c SS |
407 | |
408 | prune_threads (); | |
b83266a0 | 409 | target_find_new_threads (); |
c906108c SS |
410 | current_pid = inferior_pid; |
411 | for (tp = thread_list; tp; tp = tp->next) | |
412 | { | |
413 | if (tp->pid == current_pid) | |
414 | printf_filtered ("* "); | |
415 | else | |
416 | printf_filtered (" "); | |
417 | ||
418 | #ifdef HPUXHPPA | |
0d06e24b | 419 | printf_filtered ("%d %s", tp->num, target_tid_to_str (tp->pid)); |
c906108c | 420 | #else |
0d06e24b | 421 | printf_filtered ("%d %s", tp->num, target_pid_to_str (tp->pid)); |
c906108c | 422 | #endif |
0d06e24b JM |
423 | |
424 | extra_info = target_extra_thread_info (tp); | |
425 | if (extra_info) | |
426 | printf_filtered (" (%s)", extra_info); | |
427 | puts_filtered (" "); | |
428 | ||
c906108c SS |
429 | switch_to_thread (tp->pid); |
430 | if (selected_frame) | |
431 | print_only_stack_frame (selected_frame, -1, 0); | |
432 | else | |
433 | printf_filtered ("[No stack.]\n"); | |
434 | } | |
435 | ||
436 | switch_to_thread (current_pid); | |
437 | ||
438 | /* Code below copied from "up_silently_base" in "stack.c". | |
439 | * It restores the frame set by the user before the "info threads" | |
440 | * command. We have finished the info-threads display by switching | |
441 | * back to the current thread. That switch has put us at the top | |
442 | * of the stack (leaf frame). | |
443 | */ | |
c5aa993b JM |
444 | counter = saved_frame_level; |
445 | cur_frame = find_relative_frame (selected_frame, &counter); | |
c906108c SS |
446 | if (counter != 0) |
447 | { | |
448 | /* Ooops, can't restore, tell user where we are. */ | |
449 | warning ("Couldn't restore frame in current thread, at frame 0"); | |
450 | print_stack_frame (selected_frame, -1, 0); | |
451 | } | |
452 | else | |
453 | { | |
c5aa993b | 454 | select_frame (cur_frame, saved_frame_level); |
c906108c SS |
455 | } |
456 | ||
457 | /* re-show current frame. */ | |
c5aa993b | 458 | show_stack_frame (cur_frame); |
c906108c SS |
459 | } |
460 | ||
461 | /* Switch from one thread to another. */ | |
462 | ||
463 | static void | |
fba45db2 | 464 | switch_to_thread (int pid) |
c906108c SS |
465 | { |
466 | if (pid == inferior_pid) | |
467 | return; | |
468 | ||
469 | inferior_pid = pid; | |
470 | flush_cached_frames (); | |
471 | registers_changed (); | |
c5aa993b | 472 | stop_pc = read_pc (); |
c906108c SS |
473 | select_frame (get_current_frame (), 0); |
474 | } | |
475 | ||
476 | static void | |
fba45db2 | 477 | restore_current_thread (int pid) |
c906108c | 478 | { |
c5aa993b | 479 | if (pid != inferior_pid) |
c906108c SS |
480 | { |
481 | switch_to_thread (pid); | |
c5aa993b | 482 | print_stack_frame (get_current_frame (), 0, -1); |
c906108c SS |
483 | } |
484 | } | |
485 | ||
6ecce94d AC |
486 | struct current_thread_cleanup |
487 | { | |
488 | int inferior_pid; | |
489 | }; | |
490 | ||
491 | static void | |
492 | do_restore_current_thread_cleanup (void *arg) | |
493 | { | |
494 | struct current_thread_cleanup *old = arg; | |
495 | restore_current_thread (old->inferior_pid); | |
b8c9b27d | 496 | xfree (old); |
6ecce94d AC |
497 | } |
498 | ||
499 | static struct cleanup * | |
500 | make_cleanup_restore_current_thread (int inferior_pid) | |
501 | { | |
502 | struct current_thread_cleanup *old | |
503 | = xmalloc (sizeof (struct current_thread_cleanup)); | |
504 | old->inferior_pid = inferior_pid; | |
505 | return make_cleanup (do_restore_current_thread_cleanup, old); | |
506 | } | |
507 | ||
c906108c SS |
508 | /* Apply a GDB command to a list of threads. List syntax is a whitespace |
509 | seperated list of numbers, or ranges, or the keyword `all'. Ranges consist | |
510 | of two numbers seperated by a hyphen. Examples: | |
511 | ||
c5aa993b JM |
512 | thread apply 1 2 7 4 backtrace Apply backtrace cmd to threads 1,2,7,4 |
513 | thread apply 2-7 9 p foo(1) Apply p foo(1) cmd to threads 2->7 & 9 | |
514 | thread apply all p x/i $pc Apply x/i $pc cmd to all threads | |
515 | */ | |
c906108c SS |
516 | |
517 | static void | |
fba45db2 | 518 | thread_apply_all_command (char *cmd, int from_tty) |
c906108c SS |
519 | { |
520 | struct thread_info *tp; | |
521 | struct cleanup *old_chain; | |
e35ce267 CF |
522 | struct cleanup *saved_cmd_cleanup_chain; |
523 | char *saved_cmd; | |
c906108c SS |
524 | |
525 | if (cmd == NULL || *cmd == '\000') | |
526 | error ("Please specify a command following the thread ID list"); | |
527 | ||
6ecce94d | 528 | old_chain = make_cleanup_restore_current_thread (inferior_pid); |
c906108c | 529 | |
e9d196c5 MS |
530 | /* It is safe to update the thread list now, before |
531 | traversing it for "thread apply all". MVS */ | |
532 | target_find_new_threads (); | |
533 | ||
e35ce267 CF |
534 | /* Save a copy of the command in case it is clobbered by |
535 | execute_command */ | |
5b616ba1 | 536 | saved_cmd = xstrdup (cmd); |
b8c9b27d | 537 | saved_cmd_cleanup_chain = make_cleanup (xfree, (void *) saved_cmd); |
c906108c SS |
538 | for (tp = thread_list; tp; tp = tp->next) |
539 | if (thread_alive (tp)) | |
540 | { | |
541 | switch_to_thread (tp->pid); | |
542 | #ifdef HPUXHPPA | |
543 | printf_filtered ("\nThread %d (%s):\n", | |
544 | tp->num, | |
545 | target_tid_to_str (inferior_pid)); | |
546 | #else | |
547 | printf_filtered ("\nThread %d (%s):\n", tp->num, | |
548 | target_pid_to_str (inferior_pid)); | |
549 | #endif | |
550 | execute_command (cmd, from_tty); | |
e35ce267 | 551 | strcpy (cmd, saved_cmd); /* Restore exact command used previously */ |
c906108c | 552 | } |
6ecce94d | 553 | |
e35ce267 | 554 | do_cleanups (saved_cmd_cleanup_chain); |
6ecce94d | 555 | do_cleanups (old_chain); |
c906108c SS |
556 | } |
557 | ||
558 | static void | |
fba45db2 | 559 | thread_apply_command (char *tidlist, int from_tty) |
c906108c SS |
560 | { |
561 | char *cmd; | |
562 | char *p; | |
563 | struct cleanup *old_chain; | |
e35ce267 CF |
564 | struct cleanup *saved_cmd_cleanup_chain; |
565 | char *saved_cmd; | |
c906108c SS |
566 | |
567 | if (tidlist == NULL || *tidlist == '\000') | |
568 | error ("Please specify a thread ID list"); | |
569 | ||
c5aa993b | 570 | for (cmd = tidlist; *cmd != '\000' && !isalpha (*cmd); cmd++); |
c906108c SS |
571 | |
572 | if (*cmd == '\000') | |
573 | error ("Please specify a command following the thread ID list"); | |
574 | ||
6ecce94d | 575 | old_chain = make_cleanup_restore_current_thread (inferior_pid); |
c906108c | 576 | |
e35ce267 CF |
577 | /* Save a copy of the command in case it is clobbered by |
578 | execute_command */ | |
5b616ba1 | 579 | saved_cmd = xstrdup (cmd); |
b8c9b27d | 580 | saved_cmd_cleanup_chain = make_cleanup (xfree, (void *) saved_cmd); |
c906108c SS |
581 | while (tidlist < cmd) |
582 | { | |
583 | struct thread_info *tp; | |
584 | int start, end; | |
585 | ||
586 | start = strtol (tidlist, &p, 10); | |
587 | if (p == tidlist) | |
588 | error ("Error parsing %s", tidlist); | |
589 | tidlist = p; | |
590 | ||
591 | while (*tidlist == ' ' || *tidlist == '\t') | |
592 | tidlist++; | |
593 | ||
594 | if (*tidlist == '-') /* Got a range of IDs? */ | |
595 | { | |
c5aa993b | 596 | tidlist++; /* Skip the - */ |
c906108c SS |
597 | end = strtol (tidlist, &p, 10); |
598 | if (p == tidlist) | |
599 | error ("Error parsing %s", tidlist); | |
600 | tidlist = p; | |
601 | ||
602 | while (*tidlist == ' ' || *tidlist == '\t') | |
603 | tidlist++; | |
604 | } | |
605 | else | |
606 | end = start; | |
607 | ||
608 | for (; start <= end; start++) | |
609 | { | |
610 | tp = find_thread_id (start); | |
611 | ||
612 | if (!tp) | |
613 | warning ("Unknown thread %d.", start); | |
614 | else if (!thread_alive (tp)) | |
615 | warning ("Thread %d has terminated.", start); | |
616 | else | |
617 | { | |
618 | switch_to_thread (tp->pid); | |
619 | #ifdef HPUXHPPA | |
620 | printf_filtered ("\nThread %d (%s):\n", tp->num, | |
621 | target_tid_to_str (inferior_pid)); | |
622 | #else | |
623 | printf_filtered ("\nThread %d (%s):\n", tp->num, | |
624 | target_pid_to_str (inferior_pid)); | |
625 | #endif | |
626 | execute_command (cmd, from_tty); | |
e35ce267 | 627 | strcpy (cmd, saved_cmd); /* Restore exact command used previously */ |
c906108c SS |
628 | } |
629 | } | |
630 | } | |
6ecce94d | 631 | |
e35ce267 | 632 | do_cleanups (saved_cmd_cleanup_chain); |
6ecce94d | 633 | do_cleanups (old_chain); |
c906108c SS |
634 | } |
635 | ||
636 | /* Switch to the specified thread. Will dispatch off to thread_apply_command | |
637 | if prefix of arg is `apply'. */ | |
638 | ||
639 | static void | |
fba45db2 | 640 | thread_command (char *tidstr, int from_tty) |
c906108c | 641 | { |
c906108c SS |
642 | if (!tidstr) |
643 | { | |
644 | /* Don't generate an error, just say which thread is current. */ | |
645 | if (target_has_stack) | |
646 | printf_filtered ("[Current thread is %d (%s)]\n", | |
c5aa993b | 647 | pid_to_thread_id (inferior_pid), |
c906108c | 648 | #if defined(HPUXHPPA) |
c5aa993b | 649 | target_tid_to_str (inferior_pid) |
c906108c | 650 | #else |
c5aa993b | 651 | target_pid_to_str (inferior_pid) |
c906108c | 652 | #endif |
c5aa993b | 653 | ); |
c906108c SS |
654 | else |
655 | error ("No stack."); | |
656 | return; | |
657 | } | |
c5394b80 JM |
658 | |
659 | gdb_thread_select (tidstr); | |
660 | } | |
661 | ||
662 | static int | |
663 | do_captured_thread_select (void *tidstr) | |
664 | { | |
665 | int num; | |
666 | struct thread_info *tp; | |
667 | ||
81490ea1 | 668 | num = value_as_long (parse_and_eval (tidstr)); |
c906108c SS |
669 | |
670 | tp = find_thread_id (num); | |
671 | ||
8b93c638 JM |
672 | #ifdef UI_OUT |
673 | if (!tp) | |
674 | error ("Thread ID %d not known.", num); | |
675 | #else | |
c906108c SS |
676 | if (!tp) |
677 | error ("Thread ID %d not known. Use the \"info threads\" command to\n\ | |
678 | see the IDs of currently known threads.", num); | |
8b93c638 | 679 | #endif |
c906108c SS |
680 | |
681 | if (!thread_alive (tp)) | |
682 | error ("Thread ID %d has terminated.\n", num); | |
683 | ||
684 | switch_to_thread (tp->pid); | |
685 | ||
8b93c638 JM |
686 | #ifdef UI_OUT |
687 | ui_out_text (uiout, "[Switching to thread "); | |
688 | ui_out_field_int (uiout, "new-thread-id", pid_to_thread_id (inferior_pid)); | |
689 | ui_out_text (uiout, " ("); | |
690 | #if defined(HPUXHPPA) | |
691 | ui_out_text (uiout, target_tid_to_str (inferior_pid)); | |
692 | #else | |
693 | ui_out_text (uiout, target_pid_to_str (inferior_pid)); | |
694 | #endif | |
695 | ui_out_text (uiout, ")]"); | |
696 | #else /* UI_OUT */ | |
c906108c SS |
697 | printf_filtered ("[Switching to thread %d (%s)]\n", |
698 | pid_to_thread_id (inferior_pid), | |
699 | #if defined(HPUXHPPA) | |
700 | target_tid_to_str (inferior_pid) | |
701 | #else | |
702 | target_pid_to_str (inferior_pid) | |
703 | #endif | |
c5aa993b | 704 | ); |
8b93c638 | 705 | #endif /* UI_OUT */ |
c5394b80 | 706 | |
c906108c | 707 | print_stack_frame (selected_frame, selected_frame_level, 1); |
c5394b80 JM |
708 | return GDB_RC_OK; |
709 | } | |
710 | ||
711 | enum gdb_rc | |
712 | gdb_thread_select (char *tidstr) | |
713 | { | |
714 | return catch_errors (do_captured_thread_select, tidstr, | |
715 | NULL, RETURN_MASK_ALL); | |
c906108c SS |
716 | } |
717 | ||
718 | /* Commands with a prefix of `thread'. */ | |
719 | struct cmd_list_element *thread_cmd_list = NULL; | |
720 | ||
721 | void | |
fba45db2 | 722 | _initialize_thread (void) |
c906108c SS |
723 | { |
724 | static struct cmd_list_element *thread_apply_list = NULL; | |
c906108c SS |
725 | |
726 | add_info ("threads", info_threads_command, | |
727 | "IDs of currently known threads."); | |
728 | ||
729 | add_prefix_cmd ("thread", class_run, thread_command, | |
730 | "Use this command to switch between threads.\n\ | |
731 | The new thread ID must be currently known.", &thread_cmd_list, "thread ", 1, | |
732 | &cmdlist); | |
733 | ||
734 | add_prefix_cmd ("apply", class_run, thread_apply_command, | |
735 | "Apply a command to a list of threads.", | |
736 | &thread_apply_list, "apply ", 1, &thread_cmd_list); | |
737 | ||
738 | add_cmd ("all", class_run, thread_apply_all_command, | |
739 | "Apply a command to all threads.", | |
740 | &thread_apply_list); | |
741 | ||
742 | if (!xdb_commands) | |
743 | add_com_alias ("t", "thread", class_run, 1); | |
744 | } |