mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-25 19:14:52 +08:00
6665660a41
Similarly to the "frame apply" patch, this makes the "thread apply" family of commands -- "thread apply TID", "thread apply all" and "taas" use the gdb::option framework for '-'-style options. No new options are added, but there are some user-visible changes: - Can now abbreviate and complete "-ascending" - We now have a completer for "thread apply" commands Can now complete options ("thread apply all -[TAB]"), and also, 'thread apply all COMMAND[TAB]' now does what you'd expect, by making use of the new complete_command routine. - "help" output tweaked with auto-generated option descriptions: ~~~ Usage: thread apply all [OPTION]... COMMAND Prints per-inferior thread number and target system's thread id followed by COMMAND output. By default, an error raised during the execution of COMMAND aborts "thread apply". Options: -ascending Call COMMAND for all threads in ascending order. The default is descending order. -q Disables printing the thread information. -c Print any error raised by COMMAND and continue. -s Silently ignore any errors or empty output produced by COMMAND. ~~~ The "By default ..." sentence is new as well. gdb/ChangeLog: 2019-06-13 Pedro Alves <palves@redhat.com> * thread.c: Include "cli/cli-option.h". (tp_array_compar_ascending): Global. (tp_array_compar): Delete function. (tp_array_compar_ascending, tp_array_compar_descending): New functions. (ascending_option_def, qcs_flag_option_def) (thr_qcs_flags_option_defs) (make_thread_apply_all_options_def_group) (make_thread_apply_options_def_group): New. (thread_apply_all_command): Use gdb::option::process_options. (thread_apply_command_completer) (thread_apply_all_command_completer): New. (thread_apply_command): Use gdb::option::process_options. (_initialize_thread): Delete THREAD_APPLY_FLAGS_HELP, replace it with a new THREAD_APPLY_OPTION_HELP. Use gdb::option::build_help to generate help text of "thread apply". Adjust "taas"'s help. * tid-parse.c (tid_range_parser::in_thread_range): New method. * tid-parse.h (tid_range_parser::in_thread_range): New method. gdb/testsuite/ChangeLog: 2019-06-13 Pedro Alves <palves@redhat.com> * gdb.base/options.exp (test-thread-apply): New. (top level): Call it.
340 lines
7.6 KiB
C
340 lines
7.6 KiB
C
/* TID parsing for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 2015-2019 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "tid-parse.h"
|
|
#include "inferior.h"
|
|
#include "gdbthread.h"
|
|
#include <ctype.h>
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
void ATTRIBUTE_NORETURN
|
|
invalid_thread_id_error (const char *string)
|
|
{
|
|
error (_("Invalid thread ID: %s"), string);
|
|
}
|
|
|
|
/* Wrapper for get_number_trailer that throws an error if we get back
|
|
a negative number. We'll see a negative value if the number is
|
|
stored in a negative convenience variable (e.g., $minus_one = -1).
|
|
STRING is the parser string to be used in the error message if we
|
|
do get back a negative number. */
|
|
|
|
static int
|
|
get_positive_number_trailer (const char **pp, int trailer, const char *string)
|
|
{
|
|
int num;
|
|
|
|
num = get_number_trailer (pp, trailer);
|
|
if (num < 0)
|
|
error (_("negative value: %s"), string);
|
|
return num;
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
struct thread_info *
|
|
parse_thread_id (const char *tidstr, const char **end)
|
|
{
|
|
const char *number = tidstr;
|
|
const char *dot, *p1;
|
|
struct inferior *inf;
|
|
int thr_num;
|
|
int explicit_inf_id = 0;
|
|
|
|
dot = strchr (number, '.');
|
|
|
|
if (dot != NULL)
|
|
{
|
|
/* Parse number to the left of the dot. */
|
|
int inf_num;
|
|
|
|
p1 = number;
|
|
inf_num = get_positive_number_trailer (&p1, '.', number);
|
|
if (inf_num == 0)
|
|
invalid_thread_id_error (number);
|
|
|
|
inf = find_inferior_id (inf_num);
|
|
if (inf == NULL)
|
|
error (_("No inferior number '%d'"), inf_num);
|
|
|
|
explicit_inf_id = 1;
|
|
p1 = dot + 1;
|
|
}
|
|
else
|
|
{
|
|
inf = current_inferior ();
|
|
|
|
p1 = number;
|
|
}
|
|
|
|
thr_num = get_positive_number_trailer (&p1, 0, number);
|
|
if (thr_num == 0)
|
|
invalid_thread_id_error (number);
|
|
|
|
thread_info *tp = nullptr;
|
|
for (thread_info *it : inf->threads ())
|
|
if (it->per_inf_num == thr_num)
|
|
{
|
|
tp = it;
|
|
break;
|
|
}
|
|
|
|
if (tp == NULL)
|
|
{
|
|
if (show_inferior_qualified_tids () || explicit_inf_id)
|
|
error (_("Unknown thread %d.%d."), inf->num, thr_num);
|
|
else
|
|
error (_("Unknown thread %d."), thr_num);
|
|
}
|
|
|
|
if (end != NULL)
|
|
*end = p1;
|
|
|
|
return tp;
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
tid_range_parser::tid_range_parser (const char *tidlist,
|
|
int default_inferior)
|
|
{
|
|
init (tidlist, default_inferior);
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
void
|
|
tid_range_parser::init (const char *tidlist, int default_inferior)
|
|
{
|
|
m_state = STATE_INFERIOR;
|
|
m_cur_tok = tidlist;
|
|
m_inf_num = 0;
|
|
m_qualified = false;
|
|
m_default_inferior = default_inferior;
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
bool
|
|
tid_range_parser::finished () const
|
|
{
|
|
switch (m_state)
|
|
{
|
|
case STATE_INFERIOR:
|
|
/* Parsing is finished when at end of string or null string,
|
|
or we are not in a range and not in front of an integer, negative
|
|
integer, convenience var or negative convenience var. */
|
|
return (*m_cur_tok == '\0'
|
|
|| !(isdigit (*m_cur_tok)
|
|
|| *m_cur_tok == '$'
|
|
|| *m_cur_tok == '*'));
|
|
case STATE_THREAD_RANGE:
|
|
case STATE_STAR_RANGE:
|
|
return m_range_parser.finished ();
|
|
}
|
|
|
|
gdb_assert_not_reached (_("unhandled state"));
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
const char *
|
|
tid_range_parser::cur_tok () const
|
|
{
|
|
switch (m_state)
|
|
{
|
|
case STATE_INFERIOR:
|
|
return m_cur_tok;
|
|
case STATE_THREAD_RANGE:
|
|
case STATE_STAR_RANGE:
|
|
return m_range_parser.cur_tok ();
|
|
}
|
|
|
|
gdb_assert_not_reached (_("unhandled state"));
|
|
}
|
|
|
|
void
|
|
tid_range_parser::skip_range ()
|
|
{
|
|
gdb_assert (m_state == STATE_THREAD_RANGE
|
|
|| m_state == STATE_STAR_RANGE);
|
|
|
|
m_range_parser.skip_range ();
|
|
init (m_range_parser.cur_tok (), m_default_inferior);
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
bool
|
|
tid_range_parser::tid_is_qualified () const
|
|
{
|
|
return m_qualified;
|
|
}
|
|
|
|
/* Helper for tid_range_parser::get_tid and
|
|
tid_range_parser::get_tid_range. Return the next range if THR_END
|
|
is non-NULL, return a single thread ID otherwise. */
|
|
|
|
bool
|
|
tid_range_parser::get_tid_or_range (int *inf_num,
|
|
int *thr_start, int *thr_end)
|
|
{
|
|
if (m_state == STATE_INFERIOR)
|
|
{
|
|
const char *p;
|
|
const char *space;
|
|
|
|
space = skip_to_space (m_cur_tok);
|
|
|
|
p = m_cur_tok;
|
|
while (p < space && *p != '.')
|
|
p++;
|
|
if (p < space)
|
|
{
|
|
const char *dot = p;
|
|
|
|
/* Parse number to the left of the dot. */
|
|
p = m_cur_tok;
|
|
m_inf_num = get_positive_number_trailer (&p, '.', m_cur_tok);
|
|
if (m_inf_num == 0)
|
|
return 0;
|
|
|
|
m_qualified = true;
|
|
p = dot + 1;
|
|
|
|
if (isspace (*p))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
m_inf_num = m_default_inferior;
|
|
m_qualified = false;
|
|
p = m_cur_tok;
|
|
}
|
|
|
|
m_range_parser.init (p);
|
|
if (p[0] == '*' && (p[1] == '\0' || isspace (p[1])))
|
|
{
|
|
/* Setup the number range parser to return numbers in the
|
|
whole [1,INT_MAX] range. */
|
|
m_range_parser.setup_range (1, INT_MAX, skip_spaces (p + 1));
|
|
m_state = STATE_STAR_RANGE;
|
|
}
|
|
else
|
|
m_state = STATE_THREAD_RANGE;
|
|
}
|
|
|
|
*inf_num = m_inf_num;
|
|
*thr_start = m_range_parser.get_number ();
|
|
if (*thr_start < 0)
|
|
error (_("negative value: %s"), m_cur_tok);
|
|
if (*thr_start == 0)
|
|
{
|
|
m_state = STATE_INFERIOR;
|
|
return false;
|
|
}
|
|
|
|
/* If we successfully parsed a thread number or finished parsing a
|
|
thread range, switch back to assuming the next TID is
|
|
inferior-qualified. */
|
|
if (!m_range_parser.in_range ())
|
|
{
|
|
m_state = STATE_INFERIOR;
|
|
m_cur_tok = m_range_parser.cur_tok ();
|
|
|
|
if (thr_end != NULL)
|
|
*thr_end = *thr_start;
|
|
}
|
|
|
|
/* If we're midway through a range, and the caller wants the end
|
|
value, return it and skip to the end of the range. */
|
|
if (thr_end != NULL
|
|
&& (m_state == STATE_THREAD_RANGE
|
|
|| m_state == STATE_STAR_RANGE))
|
|
{
|
|
*thr_end = m_range_parser.end_value ();
|
|
|
|
skip_range ();
|
|
}
|
|
|
|
return (*inf_num != 0 && *thr_start != 0);
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
bool
|
|
tid_range_parser::get_tid_range (int *inf_num,
|
|
int *thr_start, int *thr_end)
|
|
{
|
|
gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL);
|
|
|
|
return get_tid_or_range (inf_num, thr_start, thr_end);
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
bool
|
|
tid_range_parser::get_tid (int *inf_num, int *thr_num)
|
|
{
|
|
gdb_assert (inf_num != NULL && thr_num != NULL);
|
|
|
|
return get_tid_or_range (inf_num, thr_num, NULL);
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
bool
|
|
tid_range_parser::in_star_range () const
|
|
{
|
|
return m_state == STATE_STAR_RANGE;
|
|
}
|
|
|
|
bool
|
|
tid_range_parser::in_thread_range () const
|
|
{
|
|
return m_state == STATE_THREAD_RANGE;
|
|
}
|
|
|
|
/* See tid-parse.h. */
|
|
|
|
int
|
|
tid_is_in_list (const char *list, int default_inferior,
|
|
int inf_num, int thr_num)
|
|
{
|
|
if (list == NULL || *list == '\0')
|
|
return 1;
|
|
|
|
tid_range_parser parser (list, default_inferior);
|
|
if (parser.finished ())
|
|
invalid_thread_id_error (parser.cur_tok ());
|
|
while (!parser.finished ())
|
|
{
|
|
int tmp_inf, tmp_thr_start, tmp_thr_end;
|
|
|
|
if (!parser.get_tid_range (&tmp_inf, &tmp_thr_start, &tmp_thr_end))
|
|
invalid_thread_id_error (parser.cur_tok ());
|
|
if (tmp_inf == inf_num
|
|
&& tmp_thr_start <= thr_num && thr_num <= tmp_thr_end)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|