From: Michael Snyder Date: Tue, 29 Apr 1997 04:27:26 +0000 (+0000) Subject: Mon Apr 28 21:25:32 1997 Michael Snyder X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=c889a1eb873d3e87d2fe38b83c7d27a2915eb792;p=binutils-gdb.git Mon Apr 28 21:25:32 1997 Michael Snyder * Makefile.in: Add rule for gnu-nat.o and i386gnu-nat.o (Gnu Hurd) * config/i386/i386gnu.mh: remove rules for [i386]gnu-nat.o, now in Makefile.in (as for other targets); add NATDEPFILE corelow.o to satisfy symbol dependancy in solib.c (core_ops). * target.[ch] conditionalize Mach-specific signals so that they won't show up in non-Mach gdb's! * thread.c: add function thread_id_to_pid; change name of static function "thread_switch" to "switch_to_thread", to avoid conflict with Mach global symbol; move thread_cmd_list to global scope so targets can add their own thread commands. * infrun.c: sig_print_info: allow for long signal names. * gnu-nat.[ch]: tidying up comments. * gnu-nat.c: move several functions to static scope, just for tidy; remove calls to prune_threads and renumber_threads; gnu_wait must not return -1 when inferior exits; attach_to_child will modify inferior_pid in a way that allows fork_inferior to remain unchanged; remove extra arg from startup_inferior; move mach-specific thread commands here from thread.c. --- diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c index 55953c0f334..fd05467754c 100644 --- a/gdb/gnu-nat.c +++ b/gdb/gnu-nat.c @@ -85,43 +85,45 @@ int gnu_debug_flag = 0; /* Forward decls */ extern struct target_ops gnu_ops; +extern char *strerror(); -struct inf *make_inf (); -void inf_clear_wait (struct inf *inf); -void inf_cleanup (struct inf *inf); -void inf_startup (struct inf *inf, int pid, task_t task); -int inf_update_suspends (struct inf *inf); -void inf_set_task (struct inf *inf, task_t port); -void inf_validate_procs (struct inf *inf); -void inf_steal_exc_ports (struct inf *inf); -void inf_restore_exc_ports (struct inf *inf); int inf_update_procs (struct inf *inf); -struct proc *inf_tid_to_proc (struct inf *inf, int tid); -inline void inf_set_threads_resume_sc (struct inf *inf, struct proc - *run_thread, int run_others); -inline int inf_set_threads_resume_sc_for_signal_thread (struct inf *inf); -inline void inf_suspend (struct inf *inf); -inline void inf_resume (struct inf *inf); -void inf_set_step_thread (struct inf *inf, struct proc *proc); -void inf_detach (struct inf *inf); -void inf_attach (struct inf *inf, int pid); -void inf_signal (struct inf *inf, enum target_signal sig); +static struct inf *make_inf (); +static void inf_clear_wait (struct inf *inf); +static void inf_cleanup (struct inf *inf); +static void inf_startup (struct inf *inf, int pid, task_t task); +static int inf_update_suspends (struct inf *inf); +static void inf_set_task (struct inf *inf, task_t port); +static void inf_validate_procs (struct inf *inf); +static void inf_steal_exc_ports (struct inf *inf); +static void inf_restore_exc_ports (struct inf *inf); +static struct proc *inf_tid_to_proc (struct inf *inf, int tid); +static inline void inf_set_threads_resume_sc (struct inf *inf, + struct proc *run_thread, + int run_others); +static inline int inf_set_threads_resume_sc_for_signal_thread (struct inf *inf); +static inline void inf_suspend (struct inf *inf); +static inline void inf_resume (struct inf *inf); +static void inf_set_step_thread (struct inf *inf, struct proc *proc); +static void inf_detach (struct inf *inf); +static void inf_attach (struct inf *inf, int pid); +static void inf_signal (struct inf *inf, enum target_signal sig); #define inf_debug(_inf, msg, args...) \ do { struct inf *__inf = (_inf); \ debug ("{inf %d %p}: " msg, __inf->pid, __inf , ##args); } while (0) -struct proc *make_proc (struct inf *inf, mach_port_t port, int tid); -struct proc *_proc_free (struct proc *proc); -int proc_update_sc (struct proc *proc); void proc_abort (struct proc *proc, int force); thread_state_t proc_get_state (struct proc *proc, int force); -error_t proc_get_exception_port (struct proc *proc, mach_port_t *port); -error_t proc_set_exception_port (struct proc *proc, mach_port_t port); +static struct proc *make_proc (struct inf *inf, mach_port_t port, int tid); +static struct proc *_proc_free (struct proc *proc); +static int proc_update_sc (struct proc *proc); +static error_t proc_get_exception_port (struct proc *proc, mach_port_t *port); +static error_t proc_set_exception_port (struct proc *proc, mach_port_t port); static mach_port_t _proc_get_exc_port (struct proc *proc); -void proc_steal_exc_port (struct proc *proc, mach_port_t exc_port); -void proc_restore_exc_port (struct proc *proc); -int proc_trace (struct proc *proc, int set); +static void proc_steal_exc_port (struct proc *proc, mach_port_t exc_port); +static void proc_restore_exc_port (struct proc *proc); +static int proc_trace (struct proc *proc, int set); char *proc_string (struct proc *proc); /* Evaluate RPC_EXPR in a scope with the variables MSGPORT and REFPORT bound @@ -238,6 +240,7 @@ struct inf int want_exceptions; }; + int __proc_pid (struct proc *proc) { return proc->inf->pid; @@ -245,7 +248,7 @@ int __proc_pid (struct proc *proc) /* Update PROC's real suspend count to match it's desired one. Returns true if we think PROC is now in a runnable state. */ -int +static int proc_update_sc (struct proc *proc) { int running; @@ -374,7 +377,8 @@ proc_get_state (struct proc *proc, int will_modify) return 0; } -error_t +/* Set PORT to PROC's exception port. */ +static error_t proc_get_exception_port (struct proc *proc, mach_port_t *port) { if (proc_is_task (proc)) @@ -383,7 +387,8 @@ proc_get_exception_port (struct proc *proc, mach_port_t *port) return thread_get_exception_port (proc->port, port); } -error_t +/* Set PROC's exception port to PORT. */ +static error_t proc_set_exception_port (struct proc *proc, mach_port_t port) { proc_debug (proc, "setting exception port: %d", port); @@ -416,7 +421,7 @@ _proc_get_exc_port (struct proc *proc) /* Replace PROC's exception port with EXC_PORT, unless it's already been done. Stash away any existing exception port so we can restore it later. */ -void +static void proc_steal_exc_port (struct proc *proc, mach_port_t exc_port) { mach_port_t cur_exc_port = _proc_get_exc_port (proc); @@ -456,10 +461,10 @@ proc_steal_exc_port (struct proc *proc, mach_port_t exc_port) } } -/* If we previously replaced PROC's exception port, put back what we found - there at the time, unless *our* exception port has since be overwritten, - in which case who knows what's going on. */ -void +/* If we previously replaced PROC's exception port, put back what we + found there at the time, unless *our* exception port has since been + overwritten, in which case who knows what's going on. */ +static void proc_restore_exc_port (struct proc *proc) { mach_port_t cur_exc_port = _proc_get_exc_port (proc); @@ -486,9 +491,9 @@ proc_restore_exc_port (struct proc *proc) } } -/* Turns hardware tracing in PROC on or off when SET is true or fals, +/* Turns hardware tracing in PROC on or off when SET is true or false, respectively. Returns true on success. */ -int +static int proc_trace (struct proc *proc, int set) { thread_state_t state = proc_get_state (proc, 1); @@ -576,9 +581,9 @@ make_proc (struct inf *inf, mach_port_t port, int tid) return proc; } -/* Frees PROC and any resources it uses, and returns the value of PROC's next - field. */ -struct proc * +/* Frees PROC and any resources it uses, and returns the value of PROC's + next field. */ +static struct proc * _proc_free (struct proc *proc) { struct inf *inf = proc->inf; @@ -646,7 +651,8 @@ struct inf *make_inf () return inf; } -void +/* clear INF's target wait status. */ +static void inf_clear_wait (struct inf *inf) { inf_debug (inf, "clearing wait"); @@ -665,7 +671,7 @@ inf_clear_wait (struct inf *inf) } } -void +static void inf_cleanup (struct inf *inf) { inf_debug (inf, "cleanup"); @@ -687,7 +693,7 @@ inf_cleanup (struct inf *inf) } } -void +static void inf_startup (struct inf *inf, int pid, task_t task) { error_t err; @@ -719,7 +725,8 @@ inf_startup (struct inf *inf, int pid, task_t task) } } -void +/* close current process, if any, and attach INF to process PORT */ +static void inf_set_task (struct inf *inf, mach_port_t port) { struct proc *task = inf->task; @@ -794,7 +801,7 @@ inf_validate_task_sc (struct inf *inf) /* Turns tracing for INF on or off, depending on ON, unless it already is. If INF is running, the resume_sc count of INF's threads will be modified, and the signal thread will briefly be run to change the trace state. */ -void +static void inf_set_traced (struct inf *inf, int on) { if (on != inf->traced) @@ -825,7 +832,7 @@ inf_set_traced (struct inf *inf, int on) /* Makes all the real suspend count deltas of all the procs in INF match the desired values. Careful to always do thread/task suspend counts in the safe order. Returns true if at least one thread is thought to be running.*/ -int +static int inf_update_suspends (struct inf *inf) { struct proc *task = inf->task; @@ -879,6 +886,8 @@ struct proc * inf_tid_to_thread (struct inf *inf, int tid) { struct proc *thread = inf->threads; + if (tid == inf->pid) /* main thread */ + return thread; while (thread) if (thread->tid == tid) return thread; @@ -888,7 +897,7 @@ inf_tid_to_thread (struct inf *inf, int tid) } /* Converts a thread port to a struct proc. */ -struct proc * +static struct proc * inf_port_to_thread (struct inf *inf, mach_port_t port) { struct proc *thread = inf->threads; @@ -901,7 +910,7 @@ inf_port_to_thread (struct inf *inf, mach_port_t port) } /* Make INF's list of threads be consistent with reality of TASK. */ -void +static void inf_validate_procs (struct inf *inf) { int i; @@ -979,11 +988,23 @@ inf_validate_procs (struct inf *inf) else /* THREADS[I] is a thread we don't know about yet! */ { +#if 0 thread = make_proc (inf, threads[i], next_thread_id++); +#else + thread = make_proc (inf, threads[i], threads[i]); +#endif (last ? last->next : inf->threads) = thread; last = thread; proc_debug (thread, "new thread: %d", threads[i]); +#if 1 /* try leaving this out, see what happens */ add_thread (thread->tid); /* Tell GDB's generic thread code. */ +#if 0 /* seems to cause kernel exceptions??? */ + target_terminal_ours_for_output (); /* allow debugger I/O */ + printf_filtered ("[gnu-nat: New %s]\n", + target_pid_to_str (thread->tid)); + target_terminal_inferior (); /* give it back */ +#endif +#endif } vm_deallocate(mach_task_self(), @@ -1005,7 +1026,7 @@ inf_update_procs (struct inf *inf) /* Sets the resume_sc of each thread in inf. That of RUN_THREAD is set to 0, and others are set to their run_sc if RUN_OTHERS is true, and otherwise their pause_sc. */ -inline void +static inline void inf_set_threads_resume_sc (struct inf *inf, struct proc *run_thread, int run_others) { @@ -1022,7 +1043,7 @@ inf_set_threads_resume_sc (struct inf *inf, /* Cause INF to continue execution immediately; individual threads may still be suspended (but their suspend counts will be updated). */ -inline void +static inline void inf_resume (struct inf *inf) { struct proc *thread; @@ -1047,7 +1068,7 @@ inf_resume (struct inf *inf) /* Cause INF to stop execution immediately; individual threads may still be running. */ -inline void +static inline void inf_suspend (struct inf *inf) { struct proc *thread; @@ -1063,10 +1084,10 @@ inf_suspend (struct inf *inf) inf_update_suspends (inf); } -/* INF has one thread PROC that is in single-stepping mode. This functions +/* INF has one thread PROC that is in single-stepping mode. This function changes it to be PROC, changing any old step_thread to be a normal one. A - PROC of 0 clears an any existing value. */ -void + PROC of 0 clears any existing value. */ +static void inf_set_step_thread (struct inf *inf, struct proc *thread) { assert (!thread || proc_is_thread (thread)); @@ -1091,7 +1112,7 @@ inf_set_step_thread (struct inf *inf, struct proc *thread) /* Set up the thread resume_sc's so that only the signal thread is running (plus whatever other thread are set to always run). Returns true if we did so, or false if we can't find a signal thread. */ -inline int +static inline int inf_set_threads_resume_sc_for_signal_thread (struct inf *inf) { if (inf->signal_thread) @@ -1112,7 +1133,7 @@ inf_update_signal_thread (struct inf *inf) } /* Detachs from INF's inferior task, letting it run once again... */ -void +static void inf_detach (struct inf *inf) { struct proc *task = inf->task; @@ -1147,7 +1168,7 @@ inf_detach (struct inf *inf) /* Attaches INF to the process with process id PID, returning it in a suspended state suitable for debugging. */ -void +static void inf_attach (struct inf *inf, int pid) { error_t err; @@ -1196,7 +1217,7 @@ void inf_restore_exc_ports (struct inf *inf) /* Deliver signal SIG to INF. If INF is stopped, delivering a signal, even signal 0, will continue it. INF is assumed to be in a paused state, and the resume_sc's of INF's threads may be affected. */ -void +static void inf_signal (struct inf *inf, enum target_signal sig) { error_t err = 0; @@ -1237,7 +1258,7 @@ inf_signal (struct inf *inf, enum target_signal sig) else /* A Unix signal. */ if (inf->stopped) - /* The process is stopped an expecting a signal. Just send off a + /* The process is stopped and expecting a signal. Just send off a request and let it get handled when we resume everything. */ { inf_debug (inf, "sending %s to stopped process", NAME); @@ -1404,12 +1425,14 @@ gnu_wait (int tid, struct target_waitstatus *status) /* Ah hah! A SIGTRAP from the inferior while starting up probably means we've succesfully completed an exec! */ { +#if 0 /* do we need this? */ if (--inf->pending_execs == 0) /* We're done! */ { prune_threads (1); /* Get rid of the old shell threads */ renumber_threads (0); /* Give our threads reasonable names. */ } +#endif inf_debug (inf, "pending exec completed, pending_execs => %d", inf->pending_execs); } @@ -1438,7 +1461,7 @@ gnu_wait (int tid, struct target_waitstatus *status) if (inf_update_procs (inf) && inf->threads) tid = inf->threads->tid; /* The first available thread. */ else - tid = -1; + tid = inferior_pid; /* let wait_for_inferior handle exit case */ if (thread && tid >= 0 && status->kind != TARGET_WAITKIND_SPURIOUS && inf->pause_sc == 0 && thread->pause_sc == 0) @@ -1546,7 +1569,7 @@ S_exception_raise_request (mach_port_t port, mach_port_t reply_port, /* Fill in INF's wait field after a task has died without giving us more detailed information. */ -void +static void inf_task_died_status (struct inf *inf) { warning ("Pid %d died with unknown exit status, using SIGKILL.", inf->pid); @@ -1849,15 +1872,15 @@ gnu_mourn_inferior () /* Fork an inferior process, and start debugging it. */ /* Set INFERIOR_PID to the first thread available in the child, if any. */ -static void -pick_first_thread () +static int +inf_pick_first_thread () { if (current_inferior->task && current_inferior->threads) /* The first thread. */ - inferior_pid = current_inferior->threads->tid; + return current_inferior->threads->tid; else /* What may be the next thread. */ - inferior_pid = next_thread_id; + return next_thread_id; } static struct inf * @@ -1889,7 +1912,6 @@ gnu_create_inferior (exec_file, allargs, env) inf_debug (inf, "attaching to child: %d", pid); inf_attach (inf, pid); - pick_first_thread (); attach_flag = 0; push_target (&gnu_ops); @@ -1900,10 +1922,11 @@ gnu_create_inferior (exec_file, allargs, env) /* Now let the child run again, knowing that it will stop immediately because of the ptrace. */ inf_resume (inf); + inferior_pid = inf_pick_first_thread (); - startup_inferior (pid, inf->pending_execs); + startup_inferior (inf->pending_execs); - return pid; + return inferior_pid; } inf_debug (inf, "creating inferior"); @@ -1971,7 +1994,7 @@ gnu_attach (args, from_tty) inf_attach (inf, pid); inf_update_procs (inf); - pick_first_thread (); + inferior_pid = inf_pick_first_thread (); attach_flag = 1; push_target (&gnu_ops); @@ -1983,7 +2006,11 @@ gnu_attach (args, from_tty) time the user does a continue. */ inf_validate_stopped (inf); +#if 0 /* Do we need this? Actually I can see where it + might be helpful here, but it's just cosmetic. + Let's come back to it later. XXX Fix me. */ renumber_threads (0); /* Give our threads reasonable names. */ +#endif } /* Take a program previously attached to and detaches it. @@ -2111,8 +2138,8 @@ struct vm_region_list { struct obstack region_obstack; /* - * Write inferior task's LEN bytes from ADDR and copy it to MYADDR - * in gdb's address space. + * Write gdb's LEN bytes from MYADDR and copy it to ADDR + * in inferior task's address space. */ int gnu_write_inferior (task, addr, myaddr, length) @@ -2354,6 +2381,7 @@ struct target_ops gnu_ops = { OPS_MAGIC /* to_magic */ }; +/* Return printable description of proc. */ char *proc_string (struct proc *proc) { static char tid_str[80]; @@ -2362,7 +2390,12 @@ char *proc_string (struct proc *proc) else sprintf (tid_str, "thread %d.%d", proc->inf->pid, - pid_to_thread_id (proc->tid)); +#if 0 + pid_to_thread_id (proc->tid) +#else + proc->tid +#endif +); return tid_str; } @@ -2386,9 +2419,41 @@ gnu_target_pid_to_str (int tid) struct cmd_list_element *set_task_cmd_list = 0; struct cmd_list_element *show_task_cmd_list = 0; +/* User thread commands. */ + +/* Commands with a prefix of `set/show thread'. */ +extern struct cmd_list_element *thread_cmd_list; +struct cmd_list_element *set_thread_cmd_list = NULL; +struct cmd_list_element *show_thread_cmd_list = NULL; + +/* Commands with a prefix of `set/show thread default'. */ +struct cmd_list_element *set_thread_default_cmd_list = NULL; +struct cmd_list_element *show_thread_default_cmd_list = NULL; + +static void +set_thread_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"set thread\" must be followed by the name of a thread +property, or \"default\".\n"); +} + +static void +show_thread_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"show thread\" must be followed by the name of a thread property, or \"default\".\n"); +} + +static void +set_thread_default_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"set thread default\" must be followed by the name of a thread property.\n"); +} -extern struct cmd_list_element *set_thread_default_cmd_list; -extern struct cmd_list_element *show_thread_default_cmd_list; +static void +show_thread_default_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"show thread default\" must be followed by the name of a thread property.\n"); +} static int parse_int_arg (char *args, char *cmd_prefix) @@ -2905,11 +2970,6 @@ them).", add_info_alias ("psets", "port-sets", 1); } -/* User thread commands. */ - -extern struct cmd_list_element *set_thread_cmd_list; -extern struct cmd_list_element *show_thread_cmd_list; -extern struct cmd_list_element *thread_cmd_list; static void set_thread_pause_cmd (char *args, int from_tty) @@ -2977,12 +3037,7 @@ set_thread_exc_port_cmd (char *args, int from_tty) steal_exc_port (thread, parse_and_eval_address (args)); } -static void -set_thread_cmd (char *args, int from_tty) -{ - printf_unfiltered ("\"set thread\" must be followed by the name of a thread property.\n"); -} - +#if 0 static void show_thread_cmd (char *args, int from_tty) { @@ -2993,6 +3048,7 @@ show_thread_cmd (char *args, int from_tty) if (thread->detach_sc != 0) show_thread_detach_sc_cmd (0, from_tty); } +#endif static void thread_takeover_sc_cmd (char *args, int from_tty) @@ -3014,6 +3070,21 @@ thread_takeover_sc_cmd (char *args, int from_tty) add_thread_commands () { + add_prefix_cmd ("thread", no_class, set_thread_cmd, + "Command prefix for setting thread properties.", + &set_thread_cmd_list, "set thread ", 0, &setlist); + add_prefix_cmd ("default", no_class, show_thread_cmd, + "Command prefix for setting default thread properties.", + &set_thread_default_cmd_list, "set thread default ", 0, + &set_thread_cmd_list); + add_prefix_cmd ("thread", no_class, set_thread_default_cmd, + "Command prefix for showing thread properties.", + &show_thread_cmd_list, "show thread ", 0, &showlist); + add_prefix_cmd ("default", no_class, show_thread_default_cmd, + "Command prefix for showing default thread properties.", + &show_thread_default_cmd_list, "show thread default ", 0, + &show_thread_cmd_list); + add_cmd ("pause", class_run, set_thread_pause_cmd, "Set whether the current thread is suspended while gdb has control.\n" "A value of \"on\" takes effect immediately, otherwise nothing\n" diff --git a/gdb/gnu-nat.h b/gdb/gnu-nat.h index 8f0ab7b3c82..84ce64bbe17 100644 --- a/gdb/gnu-nat.h +++ b/gdb/gnu-nat.h @@ -46,8 +46,9 @@ struct proc int cur_sc; /* Implemented suspend count. */ int run_sc; /* Default sc when the program is running. */ int pause_sc; /* Default sc when gdb has control. */ - int resume_sc; /* Sc resulting form the last resume. */ - int detach_sc; /* SC to leave around when detaching from program. */ + int resume_sc; /* Sc resulting from the last resume. */ + int detach_sc; /* SC to leave around when detaching + from program. */ thread_state_data_t state; /* Registers, &c. */ int state_valid : 1; /* True if STATE is up to date. */ diff --git a/gdb/thread.c b/gdb/thread.c index 2d1826a46a5..cd7fb02235e 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -65,7 +65,7 @@ static void prune_threads PARAMS ((void)); static void -thread_switch PARAMS ((int pid)); +switch_to_thread PARAMS ((int pid)); static struct thread_info * find_thread_id PARAMS ((int num)); @@ -164,6 +164,17 @@ pid_to_thread_id (pid) return 0; } +int +thread_id_to_pid (num) + int num; +{ + struct thread_info *thread = find_thread_id (num); + if (thread) + return thread->pid; + else + return -1; +} + int in_thread_list (pid) int pid; @@ -309,18 +320,18 @@ info_threads_command (arg, from_tty) printf_filtered ("%d %s ", tp->num, target_pid_to_str (tp->pid)); - thread_switch (tp->pid); + switch_to_thread (tp->pid); print_stack_frame (selected_frame, -1, 0); } - thread_switch (current_pid); + switch_to_thread (current_pid); prune_threads (); } /* Switch from one thread to another. */ static void -thread_switch (pid) +switch_to_thread (pid) int pid; { if (pid == inferior_pid) @@ -338,7 +349,7 @@ restore_current_thread (pid) int pid; { if (pid != inferior_pid) - thread_switch (pid); + switch_to_thread (pid); } /* Apply a GDB command to a list of threads. List syntax is a whitespace @@ -365,7 +376,7 @@ thread_apply_all_command (cmd, from_tty) for (tp = thread_list; tp; tp = tp->next) { - thread_switch (tp->pid); + switch_to_thread (tp->pid); printf_filtered ("\nThread %d (%s):\n", tp->num, target_pid_to_str (inferior_pid)); execute_command (cmd, from_tty); @@ -428,7 +439,7 @@ thread_apply_command (tidlist, from_tty) continue; } - thread_switch (tp->pid); + switch_to_thread (tp->pid); printf_filtered ("\nThread %d (%s):\n", tp->num, target_pid_to_str (inferior_pid)); execute_command (cmd, from_tty); @@ -459,16 +470,18 @@ see the IDs of currently known threads."); error ("Thread ID %d not known. Use the \"info threads\" command to\n\ see the IDs of currently known threads.", num); - thread_switch (tp->pid); + switch_to_thread (tp->pid); printf_filtered ("[Switching to %s]\n", target_pid_to_str (inferior_pid)); print_stack_frame (selected_frame, selected_frame_level, 1); } +/* Commands with a prefix of `thread'. */ +struct cmd_list_element *thread_cmd_list = NULL; + void _initialize_thread () { - static struct cmd_list_element *thread_cmd_list = NULL; static struct cmd_list_element *thread_apply_list = NULL; extern struct cmd_list_element *cmdlist;