1 /* TID parsing for GDB, the GNU debugger.
2 
3    Copyright (C) 2015-2024 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
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.
11 
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.
16 
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/>.  */
19 
20 #include "tid-parse.h"
21 #include "inferior.h"
22 #include "gdbthread.h"
23 #include <ctype.h>
24 
25 /* See tid-parse.h.  */
26 
27 void ATTRIBUTE_NORETURN
invalid_thread_id_error(const char * string)28 invalid_thread_id_error (const char *string)
29 {
30   error (_("Invalid thread ID: %s"), string);
31 }
32 
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.  */
38 
39 static int
get_positive_number_trailer(const char ** pp,int trailer,const char * string)40 get_positive_number_trailer (const char **pp, int trailer, const char *string)
41 {
42   int num;
43 
44   num = get_number_trailer (pp, trailer);
45   if (num < 0)
46     error (_("negative value: %s"), string);
47   return num;
48 }
49 
50 /* See tid-parse.h.  */
51 
52 struct thread_info *
parse_thread_id(const char * tidstr,const char ** end)53 parse_thread_id (const char *tidstr, const char **end)
54 {
55   const char *number = tidstr;
56   const char *dot, *p1;
57   struct inferior *inf;
58   int thr_num;
59   int explicit_inf_id = 0;
60 
61   dot = strchr (number, '.');
62 
63   if (dot != NULL)
64     {
65       /* Parse number to the left of the dot.  */
66       int inf_num;
67 
68       p1 = number;
69       inf_num = get_positive_number_trailer (&p1, '.', number);
70       if (inf_num == 0)
71           invalid_thread_id_error (number);
72 
73       inf = find_inferior_id (inf_num);
74       if (inf == NULL)
75           error (_("No inferior number '%d'"), inf_num);
76 
77       explicit_inf_id = 1;
78       p1 = dot + 1;
79     }
80   else
81     {
82       inf = current_inferior ();
83 
84       p1 = number;
85     }
86 
87   thr_num = get_positive_number_trailer (&p1, 0, number);
88   if (thr_num == 0)
89     invalid_thread_id_error (number);
90 
91   thread_info *tp = nullptr;
92   for (thread_info *it : inf->threads ())
93     if (it->per_inf_num == thr_num)
94       {
95           tp = it;
96           break;
97       }
98 
99   if (tp == NULL)
100     {
101       if (show_inferior_qualified_tids () || explicit_inf_id)
102           error (_("Unknown thread %d.%d."), inf->num, thr_num);
103       else
104           error (_("Unknown thread %d."), thr_num);
105     }
106 
107   if (end != NULL)
108     *end = p1;
109 
110   return tp;
111 }
112 
113 /* See tid-parse.h.  */
114 
tid_range_parser(const char * tidlist,int default_inferior)115 tid_range_parser::tid_range_parser (const char *tidlist,
116                                             int default_inferior)
117 {
118   init (tidlist, default_inferior);
119 }
120 
121 /* See tid-parse.h.  */
122 
123 void
init(const char * tidlist,int default_inferior)124 tid_range_parser::init (const char *tidlist, int default_inferior)
125 {
126   m_state = STATE_INFERIOR;
127   m_cur_tok = tidlist;
128   m_inf_num = 0;
129   m_qualified = false;
130   m_default_inferior = default_inferior;
131 }
132 
133 /* See tid-parse.h.  */
134 
135 bool
finished()136 tid_range_parser::finished () const
137 {
138   switch (m_state)
139     {
140     case STATE_INFERIOR:
141       /* Parsing is finished when at end of string or null string,
142            or we are not in a range and not in front of an integer, negative
143            integer, convenience var or negative convenience var.  */
144       return (*m_cur_tok == '\0'
145                 || !(isdigit (*m_cur_tok)
146                        || *m_cur_tok == '$'
147                        || *m_cur_tok == '*'));
148     case STATE_THREAD_RANGE:
149     case STATE_STAR_RANGE:
150       return m_range_parser.finished ();
151     }
152 
153   gdb_assert_not_reached ("unhandled state");
154 }
155 
156 /* See tid-parse.h.  */
157 
158 const char *
cur_tok()159 tid_range_parser::cur_tok () const
160 {
161   switch (m_state)
162     {
163     case STATE_INFERIOR:
164       return m_cur_tok;
165     case STATE_THREAD_RANGE:
166     case STATE_STAR_RANGE:
167       return m_range_parser.cur_tok ();
168     }
169 
170   gdb_assert_not_reached ("unhandled state");
171 }
172 
173 void
skip_range()174 tid_range_parser::skip_range ()
175 {
176   gdb_assert (m_state == STATE_THREAD_RANGE
177                 || m_state == STATE_STAR_RANGE);
178 
179   m_range_parser.skip_range ();
180   init (m_range_parser.cur_tok (), m_default_inferior);
181 }
182 
183 /* See tid-parse.h.  */
184 
185 bool
tid_is_qualified()186 tid_range_parser::tid_is_qualified () const
187 {
188   return m_qualified;
189 }
190 
191 /* Helper for tid_range_parser::get_tid and
192    tid_range_parser::get_tid_range.  Return the next range if THR_END
193    is non-NULL, return a single thread ID otherwise.  */
194 
195 bool
get_tid_or_range(int * inf_num,int * thr_start,int * thr_end)196 tid_range_parser::get_tid_or_range (int *inf_num,
197                                             int *thr_start, int *thr_end)
198 {
199   if (m_state == STATE_INFERIOR)
200     {
201       const char *p;
202       const char *space;
203 
204       space = skip_to_space (m_cur_tok);
205 
206       p = m_cur_tok;
207       while (p < space && *p != '.')
208           p++;
209       if (p < space)
210           {
211             const char *dot = p;
212 
213             /* Parse number to the left of the dot.  */
214             p = m_cur_tok;
215             m_inf_num = get_positive_number_trailer (&p, '.', m_cur_tok);
216             if (m_inf_num == 0)
217               return 0;
218 
219             m_qualified = true;
220             p = dot + 1;
221 
222             if (isspace (*p))
223               return false;
224           }
225       else
226           {
227             m_inf_num = m_default_inferior;
228             m_qualified = false;
229             p = m_cur_tok;
230           }
231 
232       m_range_parser.init (p);
233       if (p[0] == '*' && (p[1] == '\0' || isspace (p[1])))
234           {
235             /* Setup the number range parser to return numbers in the
236                whole [1,INT_MAX] range.  */
237             m_range_parser.setup_range (1, INT_MAX, skip_spaces (p + 1));
238             m_state = STATE_STAR_RANGE;
239           }
240       else
241           m_state = STATE_THREAD_RANGE;
242     }
243 
244   *inf_num = m_inf_num;
245   *thr_start = m_range_parser.get_number ();
246   if (*thr_start < 0)
247     error (_("negative value: %s"), m_cur_tok);
248   if (*thr_start == 0)
249     {
250       m_state = STATE_INFERIOR;
251       return false;
252     }
253 
254   /* If we successfully parsed a thread number or finished parsing a
255      thread range, switch back to assuming the next TID is
256      inferior-qualified.  */
257   if (!m_range_parser.in_range ())
258     {
259       m_state = STATE_INFERIOR;
260       m_cur_tok = m_range_parser.cur_tok ();
261 
262       if (thr_end != NULL)
263           *thr_end = *thr_start;
264     }
265 
266   /* If we're midway through a range, and the caller wants the end
267      value, return it and skip to the end of the range.  */
268   if (thr_end != NULL
269       && (m_state == STATE_THREAD_RANGE
270             || m_state == STATE_STAR_RANGE))
271     {
272       *thr_end = m_range_parser.end_value ();
273 
274       skip_range ();
275     }
276 
277   return (*inf_num != 0 && *thr_start != 0);
278 }
279 
280 /* See tid-parse.h.  */
281 
282 bool
get_tid_range(int * inf_num,int * thr_start,int * thr_end)283 tid_range_parser::get_tid_range (int *inf_num,
284                                          int *thr_start, int *thr_end)
285 {
286   gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL);
287 
288   return get_tid_or_range (inf_num, thr_start, thr_end);
289 }
290 
291 /* See tid-parse.h.  */
292 
293 bool
get_tid(int * inf_num,int * thr_num)294 tid_range_parser::get_tid (int *inf_num, int *thr_num)
295 {
296   gdb_assert (inf_num != NULL && thr_num != NULL);
297 
298   return get_tid_or_range (inf_num, thr_num, NULL);
299 }
300 
301 /* See tid-parse.h.  */
302 
303 bool
in_star_range()304 tid_range_parser::in_star_range () const
305 {
306   return m_state == STATE_STAR_RANGE;
307 }
308 
309 bool
in_thread_range()310 tid_range_parser::in_thread_range () const
311 {
312   return m_state == STATE_THREAD_RANGE;
313 }
314 
315 /* See tid-parse.h.  */
316 
317 int
tid_is_in_list(const char * list,int default_inferior,int inf_num,int thr_num)318 tid_is_in_list (const char *list, int default_inferior,
319                     int inf_num, int thr_num)
320 {
321   if (list == NULL || *list == '\0')
322     return 1;
323 
324   tid_range_parser parser (list, default_inferior);
325   if (parser.finished ())
326     invalid_thread_id_error (parser.cur_tok ());
327   while (!parser.finished ())
328     {
329       int tmp_inf, tmp_thr_start, tmp_thr_end;
330 
331       if (!parser.get_tid_range (&tmp_inf, &tmp_thr_start, &tmp_thr_end))
332           invalid_thread_id_error (parser.cur_tok ());
333       if (tmp_inf == inf_num
334             && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end)
335           return 1;
336     }
337   return 0;
338 }
339