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