X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;ds=sidebyside;f=gdb%2Fthread.c;h=4959f938c7f33dd4fc5328803db8e8dc9c838f23;hb=441af85bd9c68dbc0c2a1dbe23bf07c6cb3c3f5d;hp=24906fa7d60b2d78d026441fdf635f6630430c42;hpb=5d7071341dd3c01d38fc01398ef8b23b1bd3783c;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/thread.c b/gdb/thread.c index 24906fa7d6..4959f938c7 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -1,6 +1,6 @@ /* Multi-process/thread control for GDB, the GNU debugger. - Copyright (C) 1986-2019 Free Software Foundation, Inc. + Copyright (C) 1986-2020 Free Software Foundation, Inc. Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA. @@ -23,7 +23,7 @@ #include "symtab.h" #include "frame.h" #include "inferior.h" -#include "common/environ.h" +#include "gdbsupport/environ.h" #include "value.h" #include "target.h" #include "gdbthread.h" @@ -39,12 +39,13 @@ #include "observable.h" #include "annotate.h" #include "cli/cli-decode.h" +#include "cli/cli-option.h" #include "gdb_regex.h" #include "cli/cli-utils.h" #include "thread-fsm.h" #include "tid-parse.h" #include -#include "common/gdb_optional.h" +#include "gdbsupport/gdb_optional.h" #include "inline-frame.h" #include "stack.h" @@ -443,7 +444,7 @@ thread_step_over_chain_remove (struct thread_info *tp) step_over_chain_remove (&step_over_queue_head, tp); } -/* Delete the thread referenced by THR. If SILENT, don't notifyi +/* Delete the thread referenced by THR. If SILENT, don't notify the observer of this exit. THR must not be NULL or a failed assertion will be raised. */ @@ -708,7 +709,7 @@ delete_exited_threads (void) delete_thread (tp); } -/* Return true value if stack temporaies are enabled for the thread +/* Return true value if stack temporaries are enabled for the thread TP. */ bool @@ -1089,85 +1090,85 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, for (inferior *inf : all_inferiors ()) for (thread_info *tp : inf->threads ()) - { - int core; + { + int core; - any_thread = true; - if (tp == current_thread && tp->state == THREAD_EXITED) - current_exited = true; + any_thread = true; + if (tp == current_thread && tp->state == THREAD_EXITED) + current_exited = true; - if (!should_print_thread (requested_threads, default_inf_num, - global_ids, pid, tp)) - continue; + if (!should_print_thread (requested_threads, default_inf_num, + global_ids, pid, tp)) + continue; - ui_out_emit_tuple tuple_emitter (uiout, NULL); + ui_out_emit_tuple tuple_emitter (uiout, NULL); - if (!uiout->is_mi_like_p ()) - { - if (tp == current_thread) - uiout->field_string ("current", "*"); - else - uiout->field_skip ("current"); + if (!uiout->is_mi_like_p ()) + { + if (tp == current_thread) + uiout->field_string ("current", "*"); + else + uiout->field_skip ("current"); - uiout->field_string ("id-in-tg", print_thread_id (tp)); - } + uiout->field_string ("id-in-tg", print_thread_id (tp)); + } - if (show_global_ids || uiout->is_mi_like_p ()) - uiout->field_int ("id", tp->global_num); + if (show_global_ids || uiout->is_mi_like_p ()) + uiout->field_signed ("id", tp->global_num); - /* For the CLI, we stuff everything into the target-id field. - This is a gross hack to make the output come out looking - correct. The underlying problem here is that ui-out has no - way to specify that a field's space allocation should be - shared by several fields. For MI, we do the right thing - instead. */ + /* For the CLI, we stuff everything into the target-id field. + This is a gross hack to make the output come out looking + correct. The underlying problem here is that ui-out has no + way to specify that a field's space allocation should be + shared by several fields. For MI, we do the right thing + instead. */ - if (uiout->is_mi_like_p ()) - { - uiout->field_string ("target-id", target_pid_to_str (tp->ptid)); + if (uiout->is_mi_like_p ()) + { + uiout->field_string ("target-id", target_pid_to_str (tp->ptid)); - const char *extra_info = target_extra_thread_info (tp); - if (extra_info != nullptr) - uiout->field_string ("details", extra_info); + const char *extra_info = target_extra_thread_info (tp); + if (extra_info != nullptr) + uiout->field_string ("details", extra_info); - const char *name = (tp->name != nullptr - ? tp->name - : target_thread_name (tp)); - if (name != NULL) - uiout->field_string ("name", name); - } - else - { - uiout->field_string ("target-id", - thread_target_id_str (tp).c_str ()); - } + const char *name = (tp->name != nullptr + ? tp->name + : target_thread_name (tp)); + if (name != NULL) + uiout->field_string ("name", name); + } + else + { + uiout->field_string ("target-id", + thread_target_id_str (tp).c_str ()); + } - if (tp->state == THREAD_RUNNING) - uiout->text ("(running)\n"); - else - { - /* The switch below puts us at the top of the stack (leaf - frame). */ - switch_to_thread (tp); - print_stack_frame (get_selected_frame (NULL), - /* For MI output, print frame level. */ - uiout->is_mi_like_p (), - LOCATION, 0); - } + if (tp->state == THREAD_RUNNING) + uiout->text ("(running)\n"); + else + { + /* The switch below puts us at the top of the stack (leaf + frame). */ + switch_to_thread (tp); + print_stack_frame (get_selected_frame (NULL), + /* For MI output, print frame level. */ + uiout->is_mi_like_p (), + LOCATION, 0); + } - if (uiout->is_mi_like_p ()) - { - const char *state = "stopped"; + if (uiout->is_mi_like_p ()) + { + const char *state = "stopped"; - if (tp->state == THREAD_RUNNING) - state = "running"; - uiout->field_string ("state", state); - } + if (tp->state == THREAD_RUNNING) + state = "running"; + uiout->field_string ("state", state); + } - core = target_core_of_thread (tp->ptid); - if (uiout->is_mi_like_p () && core != -1) - uiout->field_int ("core", core); - } + core = target_core_of_thread (tp->ptid); + if (uiout->is_mi_like_p () && core != -1) + uiout->field_signed ("core", core); + } /* This end scope restores the current thread and the frame selected before the "info threads" command, and it finishes the @@ -1177,7 +1178,7 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, if (pid == -1 && requested_threads == NULL) { if (uiout->is_mi_like_p () && inferior_ptid != null_ptid) - uiout->field_int ("current-thread-id", current_thread->global_num); + uiout->field_signed ("current-thread-id", current_thread->global_num); if (inferior_ptid != null_ptid && current_exited) uiout->message ("\n\ @@ -1198,6 +1199,33 @@ print_thread_info (struct ui_out *uiout, const char *requested_threads, print_thread_info_1 (uiout, requested_threads, 1, pid, 0); } +/* The options for the "info threads" command. */ + +struct info_threads_opts +{ + /* For "-gid". */ + bool show_global_ids = false; +}; + +static const gdb::option::option_def info_threads_option_defs[] = { + + gdb::option::flag_option_def { + "gid", + [] (info_threads_opts *opts) { return &opts->show_global_ids; }, + N_("Show global thread IDs."), + }, + +}; + +/* Create an option_def_group for the "info threads" options, with + IT_OPTS as context. */ + +static inline gdb::option::option_def_group +make_info_threads_options_def_group (info_threads_opts *it_opts) +{ + return {{info_threads_option_defs}, it_opts}; +} + /* Implementation of the "info threads" command. Note: this has the drawback that it _really_ switches @@ -1207,16 +1235,36 @@ print_thread_info (struct ui_out *uiout, const char *requested_threads, static void info_threads_command (const char *arg, int from_tty) { - int show_global_ids = 0; + info_threads_opts it_opts; + + auto grp = make_info_threads_options_def_group (&it_opts); + gdb::option::process_options + (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp); + + print_thread_info_1 (current_uiout, arg, 0, -1, it_opts.show_global_ids); +} + +/* Completer for the "info threads" command. */ + +static void +info_threads_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word_ignored) +{ + const auto grp = make_info_threads_options_def_group (nullptr); + + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp)) + return; - if (arg != NULL - && check_for_argument (&arg, "-gid", sizeof ("-gid") - 1)) + /* Convenience to let the user know what the option can accept. */ + if (*text == '\0') { - arg = skip_spaces (arg); - show_global_ids = 1; + gdb::option::complete_on_all_options (tracker, grp); + /* Keep this "ID" in sync with what "help info threads" + says. */ + tracker.add_completion (make_unique_xstrdup ("ID")); } - - print_thread_info_1 (current_uiout, arg, 0, -1, show_global_ids); } /* See gdbthread.h. */ @@ -1259,7 +1307,7 @@ switch_to_thread (thread_info *thr) reinit_frame_cache (); } -/* See common/common-gdbthread.h. */ +/* See gdbsupport/common-gdbthread.h. */ void switch_to_thread (ptid_t ptid) @@ -1426,30 +1474,30 @@ print_thread_id (struct thread_info *thr) return s; } -/* If true, tp_array_compar should sort in ascending order, otherwise - in descending order. */ +/* Sort an array of struct thread_info pointers by thread ID (first by + inferior number, and then by per-inferior thread number). Sorts in + ascending order. */ + +static bool +tp_array_compar_ascending (const thread_info *a, const thread_info *b) +{ + if (a->inf->num != b->inf->num) + return a->inf->num < b->inf->num; -static bool tp_array_compar_ascending; + return (a->per_inf_num < b->per_inf_num); +} -/* Sort an array for struct thread_info pointers by thread ID (first - by inferior number, and then by per-inferior thread number). The - order is determined by TP_ARRAY_COMPAR_ASCENDING. */ +/* Sort an array of struct thread_info pointers by thread ID (first by + inferior number, and then by per-inferior thread number). Sorts in + descending order. */ static bool -tp_array_compar (const thread_info *a, const thread_info *b) +tp_array_compar_descending (const thread_info *a, const thread_info *b) { if (a->inf->num != b->inf->num) - { - if (tp_array_compar_ascending) - return a->inf->num < b->inf->num; - else - return a->inf->num > b->inf->num; - } + return a->inf->num > b->inf->num; - if (tp_array_compar_ascending) - return (a->per_inf_num < b->per_inf_num); - else - return (a->per_inf_num > b->per_inf_num); + return (a->per_inf_num > b->per_inf_num); } /* Switch to thread THR and execute CMD. @@ -1490,6 +1538,60 @@ thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty, } } +/* Option definition of "thread apply"'s "-ascending" option. */ + +static const gdb::option::flag_option_def<> ascending_option_def = { + "ascending", + N_("\ +Call COMMAND for all threads in ascending order.\n\ +The default is descending order."), +}; + +/* The qcs command line flags for the "thread apply" commands. Keep + this in sync with the "frame apply" commands. */ + +using qcs_flag_option_def + = gdb::option::flag_option_def; + +static const gdb::option::option_def thr_qcs_flags_option_defs[] = { + qcs_flag_option_def { + "q", [] (qcs_flags *opt) { return &opt->quiet; }, + N_("Disables printing the thread information."), + }, + + qcs_flag_option_def { + "c", [] (qcs_flags *opt) { return &opt->cont; }, + N_("Print any error raised by COMMAND and continue."), + }, + + qcs_flag_option_def { + "s", [] (qcs_flags *opt) { return &opt->silent; }, + N_("Silently ignore any errors or empty output produced by COMMAND."), + }, +}; + +/* Create an option_def_group for the "thread apply all" options, with + ASCENDING and FLAGS as context. */ + +static inline std::array +make_thread_apply_all_options_def_group (bool *ascending, + qcs_flags *flags) +{ + return {{ + { {ascending_option_def.def ()}, ascending}, + { {thr_qcs_flags_option_defs}, flags }, + }}; +} + +/* Create an option_def_group for the "thread apply" options, with + FLAGS as context. */ + +static inline gdb::option::option_def_group +make_thread_apply_options_def_group (qcs_flags *flags) +{ + return {{thr_qcs_flags_option_defs}, flags}; +} + /* Apply a GDB command to a list of threads. List syntax is a whitespace separated list of numbers, or ranges, or the keyword `all'. Ranges consist of two numbers separated by a hyphen. Examples: @@ -1501,24 +1603,15 @@ thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty, static void thread_apply_all_command (const char *cmd, int from_tty) { + bool ascending = false; qcs_flags flags; - tp_array_compar_ascending = false; - - while (cmd != NULL) - { - if (check_for_argument (&cmd, "-ascending", strlen ("-ascending"))) - { - cmd = skip_spaces (cmd); - tp_array_compar_ascending = true; - continue; - } + auto group = make_thread_apply_all_options_def_group (&ascending, + &flags); + gdb::option::process_options + (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group); - if (parse_flags_qcs ("thread apply all", &cmd, &flags)) - continue; - - break; - } + validate_flags_qcs ("thread apply all", &flags); if (cmd == NULL || *cmd == '\000') error (_("Please specify a command at the end of 'thread apply all'")); @@ -1544,7 +1637,10 @@ thread_apply_all_command (const char *cmd, int from_tty) exit. */ scoped_inc_dec_ref inc_dec_ref (thr_list_cpy); - std::sort (thr_list_cpy.begin (), thr_list_cpy.end (), tp_array_compar); + auto *sorter = (ascending + ? tp_array_compar_ascending + : tp_array_compar_descending); + std::sort (thr_list_cpy.begin (), thr_list_cpy.end (), sorter); scoped_restore_current_thread restore_thread; @@ -1554,6 +1650,81 @@ thread_apply_all_command (const char *cmd, int from_tty) } } +/* Completer for "thread apply [ID list]". */ + +static void +thread_apply_command_completer (cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char * /*word*/) +{ + /* Don't leave this to complete_options because there's an early + return below. */ + tracker.set_use_custom_word_point (true); + + tid_range_parser parser; + parser.init (text, current_inferior ()->num); + + try + { + while (!parser.finished ()) + { + int inf_num, thr_start, thr_end; + + if (!parser.get_tid_range (&inf_num, &thr_start, &thr_end)) + break; + + if (parser.in_star_range () || parser.in_thread_range ()) + parser.skip_range (); + } + } + catch (const gdb_exception_error &ex) + { + /* get_tid_range throws if it parses a negative number, for + example. But a seemingly negative number may be the start of + an option instead. */ + } + + const char *cmd = parser.cur_tok (); + + if (cmd == text) + { + /* No thread ID list yet. */ + return; + } + + /* Check if we're past a valid thread ID list already. */ + if (parser.finished () + && cmd > text && !isspace (cmd[-1])) + return; + + /* We're past the thread ID list, advance word point. */ + tracker.advance_custom_word_point_by (cmd - text); + text = cmd; + + const auto group = make_thread_apply_options_def_group (nullptr); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group)) + return; + + complete_nested_command_line (tracker, text); +} + +/* Completer for "thread apply all". */ + +static void +thread_apply_all_command_completer (cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word) +{ + const auto group = make_thread_apply_all_options_def_group (nullptr, + nullptr); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group)) + return; + + complete_nested_command_line (tracker, text); +} + /* Implementation of the "thread apply" command. */ static void @@ -1577,13 +1748,16 @@ thread_apply_command (const char *tidlist, int from_tty) cmd = parser.cur_tok (); - while (parse_flags_qcs ("thread apply", &cmd, &flags)) - ; + auto group = make_thread_apply_options_def_group (&flags); + gdb::option::process_options + (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group); + + validate_flags_qcs ("thread apply", &flags); if (*cmd == '\0') error (_("Please specify a command following the thread ID list")); - if (tidlist == cmd || !isalpha (cmd[0])) + if (tidlist == cmd || isdigit (cmd[0])) invalid_thread_id_error (cmd); scoped_restore_current_thread restore_thread; @@ -1644,6 +1818,8 @@ thread_apply_command (const char *tidlist, int from_tty) static void taas_command (const char *cmd, int from_tty) { + if (cmd == NULL || *cmd == '\0') + error (_("Please specify a command to apply on all threads")); std::string expanded = std::string ("thread apply all -s ") + cmd; execute_command (expanded.c_str (), from_tty); } @@ -1653,6 +1829,8 @@ taas_command (const char *cmd, int from_tty) static void tfaas_command (const char *cmd, int from_tty) { + if (cmd == NULL || *cmd == '\0') + error (_("Please specify a command to apply on all frames of all threads")); std::string expanded = std::string ("thread apply all -s -- frame apply all -s ") + cmd; execute_command (expanded.c_str (), from_tty); @@ -1777,7 +1955,7 @@ thread_find_command (const char *arg, int from_tty) } /* Print notices when new threads are attached and detached. */ -int print_thread_events = 1; +bool print_thread_events = true; static void show_print_thread_events (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) @@ -1816,8 +1994,8 @@ print_selected_thread_frame (struct ui_out *uiout, { if (uiout->is_mi_like_p ()) { - uiout->field_int ("new-thread-id", - inferior_thread ()->global_num); + uiout->field_signed ("new-thread-id", + inferior_thread ()->global_num); } else { @@ -1941,49 +2119,75 @@ _initialize_thread (void) static struct cmd_list_element *thread_apply_list = NULL; cmd_list_element *c; - add_info ("threads", info_threads_command, - _("Display currently known threads.\n\ -Usage: info threads [-gid] [ID]...\n\ --gid: Show global thread IDs.\n\ + const auto info_threads_opts = make_info_threads_options_def_group (nullptr); + + /* Note: keep this "ID" in sync with what "info threads [TAB]" + suggests. */ + static std::string info_threads_help + = gdb::option::build_help (_("\ +Display currently known threads.\n\ +Usage: info threads [OPTION]... [ID]...\n\ +\n\ +Options:\n\ +%OPTIONS%\ If ID is given, it is a space-separated list of IDs of threads to display.\n\ -Otherwise, all threads are displayed.")); +Otherwise, all threads are displayed."), + info_threads_opts); + + c = add_info ("threads", info_threads_command, info_threads_help.c_str ()); + set_cmd_completer_handle_brkchars (c, info_threads_command_completer); add_prefix_cmd ("thread", class_run, thread_command, _("\ Use this command to switch between threads.\n\ The new thread ID must be currently known."), &thread_cmd_list, "thread ", 1, &cmdlist); -#define THREAD_APPLY_FLAGS_HELP "\ +#define THREAD_APPLY_OPTION_HELP "\ Prints per-inferior thread number and target system's thread id\n\ followed by COMMAND output.\n\ -FLAG arguments are -q (quiet), -c (continue), -s (silent).\n\ -Flag -q disables printing the thread information.\n\ -By default, if a COMMAND raises an error, thread apply is aborted.\n\ -Flag -c indicates to print the error and continue.\n\ -Flag -s indicates to silently ignore a COMMAND that raises an error\n\ -or produces no output." - - add_prefix_cmd ("apply", class_run, thread_apply_command, - _("Apply a command to a list of threads.\n\ -Usage: thread apply ID... [FLAG]... COMMAND\n\ +\n\ +By default, an error raised during the execution of COMMAND\n\ +aborts \"thread apply\".\n\ +\n\ +Options:\n\ +%OPTIONS%" + + const auto thread_apply_opts = make_thread_apply_options_def_group (nullptr); + + static std::string thread_apply_help = gdb::option::build_help (_("\ +Apply a command to a list of threads.\n\ +Usage: thread apply ID... [OPTION]... COMMAND\n\ ID is a space-separated list of IDs of threads to apply COMMAND on.\n" -THREAD_APPLY_FLAGS_HELP), - &thread_apply_list, "thread apply ", 1, &thread_cmd_list); +THREAD_APPLY_OPTION_HELP), + thread_apply_opts); + + c = add_prefix_cmd ("apply", class_run, thread_apply_command, + thread_apply_help.c_str (), + &thread_apply_list, "thread apply ", 1, + &thread_cmd_list); + set_cmd_completer_handle_brkchars (c, thread_apply_command_completer); - add_cmd ("all", class_run, thread_apply_all_command, - _("\ + const auto thread_apply_all_opts + = make_thread_apply_all_options_def_group (nullptr, nullptr); + + static std::string thread_apply_all_help = gdb::option::build_help (_("\ Apply a command to all threads.\n\ \n\ -Usage: thread apply all [-ascending] [FLAG]... COMMAND\n\ --ascending: Call COMMAND for all threads in ascending order.\n\ - The default is descending order.\n" -THREAD_APPLY_FLAGS_HELP), - &thread_apply_list); +Usage: thread apply all [OPTION]... COMMAND\n" +THREAD_APPLY_OPTION_HELP), + thread_apply_all_opts); + + c = add_cmd ("all", class_run, thread_apply_all_command, + thread_apply_all_help.c_str (), + &thread_apply_list); + set_cmd_completer_handle_brkchars (c, thread_apply_all_command_completer); - add_com ("taas", class_run, taas_command, _("\ + c = add_com ("taas", class_run, taas_command, _("\ Apply a command to all threads (ignoring errors and empty output).\n\ -Usage: taas COMMAND\n\ -shortcut for 'thread apply all -s COMMAND'")); +Usage: taas [OPTION]... COMMAND\n\ +shortcut for 'thread apply all -s [OPTION]... COMMAND'\n\ +See \"help thread apply all\" for available options.")); + set_cmd_completer_handle_brkchars (c, thread_apply_all_command_completer); c = add_com ("tfaas", class_run, tfaas_command, _("\ Apply a command to all frames of all threads (ignoring errors and empty output).\n\