1 /* TID parsing for GDB, the GNU debugger.
3 Copyright (C) 2015-2024 Free Software Foundation, Inc.
5 This file is part of GDB.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "tid-parse.h"
22 #include "gdbthread.h"
25 /* See tid-parse.h. */
28 invalid_thread_id_error (const char *string
)
30 error (_("Invalid thread ID: %s"), string
);
33 /* Wrapper for get_number_trailer that throws an error if we get back
34 a negative number. We'll see a negative value if the number is
35 stored in a negative convenience variable (e.g., $minus_one = -1).
36 STRING is the parser string to be used in the error message if we
37 do get back a negative number. */
40 get_positive_number_trailer (const char **pp
, int trailer
, const char *string
)
44 num
= get_number_trailer (pp
, trailer
);
46 error (_("negative value: %s"), string
);
50 /* Parse TIDSTR as a per-inferior thread ID, in either INF_NUM.THR_NUM
51 or THR_NUM form, and return a pair, the first item of the pair is
52 INF_NUM and the second item is THR_NUM.
54 If TIDSTR does not include an INF_NUM component, then the first item in
55 the pair will be 0 (which is an invalid inferior number), this indicates
56 that TIDSTR references the current inferior.
58 This function does not validate the INF_NUM and THR_NUM are actually
59 valid numbers, that is, they might reference inferiors or threads that
60 don't actually exist; this function just splits the string into its
63 If there is an error parsing TIDSTR then this function will raise an
66 static std::pair
<int, int>
67 parse_thread_id_1 (const char *tidstr
, const char **end
)
69 const char *number
= tidstr
;
73 dot
= strchr (number
, '.');
77 /* Parse number to the left of the dot. */
79 inf_num
= get_positive_number_trailer (&p1
, '.', number
);
81 invalid_thread_id_error (number
);
90 thr_num
= get_positive_number_trailer (&p1
, 0, number
);
92 invalid_thread_id_error (number
);
97 return { inf_num
, thr_num
};
100 /* See tid-parse.h. */
103 parse_thread_id (const char *tidstr
, const char **end
)
105 const auto [inf_num
, thr_num
] = parse_thread_id_1 (tidstr
, end
);
108 bool explicit_inf_id
= false;
112 inf
= find_inferior_id (inf_num
);
114 error (_("No inferior number '%d'"), inf_num
);
115 explicit_inf_id
= true;
118 inf
= current_inferior ();
120 thread_info
*tp
= nullptr;
121 for (thread_info
*it
: inf
->threads ())
122 if (it
->per_inf_num
== thr_num
)
130 if (show_inferior_qualified_tids () || explicit_inf_id
)
131 error (_("Unknown thread %d.%d."), inf
->num
, thr_num
);
133 error (_("Unknown thread %d."), thr_num
);
139 /* See tid-parse.h. */
142 is_thread_id (const char *tidstr
, const char **end
)
146 (void) parse_thread_id_1 (tidstr
, end
);
149 catch (const gdb_exception_error
&)
155 /* See tid-parse.h. */
157 tid_range_parser::tid_range_parser (const char *tidlist
,
158 int default_inferior
)
160 init (tidlist
, default_inferior
);
163 /* See tid-parse.h. */
166 tid_range_parser::init (const char *tidlist
, int default_inferior
)
168 m_state
= STATE_INFERIOR
;
172 m_default_inferior
= default_inferior
;
175 /* See tid-parse.h. */
178 tid_range_parser::finished () const
183 /* Parsing is finished when at end of string or null string,
184 or we are not in a range and not in front of an integer, negative
185 integer, convenience var or negative convenience var. */
186 return (*m_cur_tok
== '\0'
187 || !(isdigit (*m_cur_tok
)
189 || *m_cur_tok
== '*'));
190 case STATE_THREAD_RANGE
:
191 case STATE_STAR_RANGE
:
192 return m_range_parser
.finished ();
195 gdb_assert_not_reached ("unhandled state");
198 /* See tid-parse.h. */
201 tid_range_parser::cur_tok () const
207 case STATE_THREAD_RANGE
:
208 case STATE_STAR_RANGE
:
209 return m_range_parser
.cur_tok ();
212 gdb_assert_not_reached ("unhandled state");
216 tid_range_parser::skip_range ()
218 gdb_assert (m_state
== STATE_THREAD_RANGE
219 || m_state
== STATE_STAR_RANGE
);
221 m_range_parser
.skip_range ();
222 init (m_range_parser
.cur_tok (), m_default_inferior
);
225 /* See tid-parse.h. */
228 tid_range_parser::tid_is_qualified () const
233 /* Helper for tid_range_parser::get_tid and
234 tid_range_parser::get_tid_range. Return the next range if THR_END
235 is non-NULL, return a single thread ID otherwise. */
238 tid_range_parser::get_tid_or_range (int *inf_num
,
239 int *thr_start
, int *thr_end
)
241 if (m_state
== STATE_INFERIOR
)
246 space
= skip_to_space (m_cur_tok
);
249 while (p
< space
&& *p
!= '.')
255 /* Parse number to the left of the dot. */
257 m_inf_num
= get_positive_number_trailer (&p
, '.', m_cur_tok
);
269 m_inf_num
= m_default_inferior
;
274 m_range_parser
.init (p
);
275 if (p
[0] == '*' && (p
[1] == '\0' || isspace (p
[1])))
277 /* Setup the number range parser to return numbers in the
278 whole [1,INT_MAX] range. */
279 m_range_parser
.setup_range (1, INT_MAX
, skip_spaces (p
+ 1));
280 m_state
= STATE_STAR_RANGE
;
283 m_state
= STATE_THREAD_RANGE
;
286 *inf_num
= m_inf_num
;
287 *thr_start
= m_range_parser
.get_number ();
289 error (_("negative value: %s"), m_cur_tok
);
292 m_state
= STATE_INFERIOR
;
296 /* If we successfully parsed a thread number or finished parsing a
297 thread range, switch back to assuming the next TID is
298 inferior-qualified. */
299 if (!m_range_parser
.in_range ())
301 m_state
= STATE_INFERIOR
;
302 m_cur_tok
= m_range_parser
.cur_tok ();
305 *thr_end
= *thr_start
;
308 /* If we're midway through a range, and the caller wants the end
309 value, return it and skip to the end of the range. */
311 && (m_state
== STATE_THREAD_RANGE
312 || m_state
== STATE_STAR_RANGE
))
314 *thr_end
= m_range_parser
.end_value ();
319 return (*inf_num
!= 0 && *thr_start
!= 0);
322 /* See tid-parse.h. */
325 tid_range_parser::get_tid_range (int *inf_num
,
326 int *thr_start
, int *thr_end
)
328 gdb_assert (inf_num
!= NULL
&& thr_start
!= NULL
&& thr_end
!= NULL
);
330 return get_tid_or_range (inf_num
, thr_start
, thr_end
);
333 /* See tid-parse.h. */
336 tid_range_parser::get_tid (int *inf_num
, int *thr_num
)
338 gdb_assert (inf_num
!= NULL
&& thr_num
!= NULL
);
340 return get_tid_or_range (inf_num
, thr_num
, NULL
);
343 /* See tid-parse.h. */
346 tid_range_parser::in_star_range () const
348 return m_state
== STATE_STAR_RANGE
;
352 tid_range_parser::in_thread_range () const
354 return m_state
== STATE_THREAD_RANGE
;
357 /* See tid-parse.h. */
360 tid_is_in_list (const char *list
, int default_inferior
,
361 int inf_num
, int thr_num
)
363 if (list
== NULL
|| *list
== '\0')
366 tid_range_parser
parser (list
, default_inferior
);
367 if (parser
.finished ())
368 invalid_thread_id_error (parser
.cur_tok ());
369 while (!parser
.finished ())
371 int tmp_inf
, tmp_thr_start
, tmp_thr_end
;
373 if (!parser
.get_tid_range (&tmp_inf
, &tmp_thr_start
, &tmp_thr_end
))
374 invalid_thread_id_error (parser
.cur_tok ());
375 if (tmp_inf
== inf_num
376 && tmp_thr_start
<= thr_num
&& thr_num
<= tmp_thr_end
)