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