Commit | Line | Data |
---|---|---|
5d5658a1 PA |
1 | /* TID parsing for GDB, the GNU debugger. |
2 | ||
3 | Copyright (C) 2015-2016 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 "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; | |
58 | struct thread_info *tp; | |
59 | struct inferior *inf; | |
60 | int thr_num; | |
61 | int explicit_inf_id = 0; | |
62 | ||
63 | dot = strchr (number, '.'); | |
64 | ||
65 | if (dot != NULL) | |
66 | { | |
67 | /* Parse number to the left of the dot. */ | |
68 | int inf_num; | |
69 | ||
70 | p1 = number; | |
3f5b7598 | 71 | inf_num = get_positive_number_trailer (&p1, '.', number); |
5d5658a1 PA |
72 | if (inf_num == 0) |
73 | invalid_thread_id_error (number); | |
74 | ||
75 | inf = find_inferior_id (inf_num); | |
76 | if (inf == NULL) | |
77 | error (_("No inferior number '%d'"), inf_num); | |
78 | ||
79 | explicit_inf_id = 1; | |
80 | p1 = dot + 1; | |
81 | } | |
82 | else | |
83 | { | |
84 | inf = current_inferior (); | |
85 | ||
86 | p1 = number; | |
87 | } | |
88 | ||
3f5b7598 | 89 | thr_num = get_positive_number_trailer (&p1, 0, number); |
5d5658a1 PA |
90 | if (thr_num == 0) |
91 | invalid_thread_id_error (number); | |
92 | ||
93 | ALL_THREADS (tp) | |
94 | { | |
95 | if (ptid_get_pid (tp->ptid) == inf->pid | |
96 | && tp->per_inf_num == thr_num) | |
97 | break; | |
98 | } | |
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 | ||
116 | void | |
117 | tid_range_parser_init (struct tid_range_parser *parser, const char *tidlist, | |
118 | int default_inferior) | |
119 | { | |
120 | parser->state = TID_RANGE_STATE_INFERIOR; | |
121 | parser->string = tidlist; | |
122 | parser->inf_num = 0; | |
123 | parser->qualified = 0; | |
124 | parser->default_inferior = default_inferior; | |
125 | } | |
126 | ||
127 | /* See tid-parse.h. */ | |
128 | ||
129 | int | |
130 | tid_range_parser_finished (struct tid_range_parser *parser) | |
131 | { | |
132 | switch (parser->state) | |
133 | { | |
134 | case TID_RANGE_STATE_INFERIOR: | |
135 | return *parser->string == '\0'; | |
136 | case TID_RANGE_STATE_THREAD_RANGE: | |
71ef29a8 | 137 | case TID_RANGE_STATE_STAR_RANGE: |
5d5658a1 PA |
138 | return parser->range_parser.finished; |
139 | } | |
140 | ||
141 | gdb_assert_not_reached (_("unhandled state")); | |
142 | } | |
143 | ||
144 | /* See tid-parse.h. */ | |
145 | ||
146 | const char * | |
147 | tid_range_parser_string (struct tid_range_parser *parser) | |
148 | { | |
149 | switch (parser->state) | |
150 | { | |
151 | case TID_RANGE_STATE_INFERIOR: | |
152 | return parser->string; | |
153 | case TID_RANGE_STATE_THREAD_RANGE: | |
71ef29a8 | 154 | case TID_RANGE_STATE_STAR_RANGE: |
5d5658a1 PA |
155 | return parser->range_parser.string; |
156 | } | |
157 | ||
158 | gdb_assert_not_reached (_("unhandled state")); | |
159 | } | |
160 | ||
161 | /* See tid-parse.h. */ | |
162 | ||
163 | void | |
164 | tid_range_parser_skip (struct tid_range_parser *parser) | |
165 | { | |
71ef29a8 PA |
166 | gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE |
167 | || parser->state == TID_RANGE_STATE_STAR_RANGE) | |
5d5658a1 PA |
168 | && parser->range_parser.in_range); |
169 | ||
170 | tid_range_parser_init (parser, parser->range_parser.end_ptr, | |
171 | parser->default_inferior); | |
172 | } | |
173 | ||
174 | /* See tid-parse.h. */ | |
175 | ||
176 | int | |
177 | tid_range_parser_qualified (struct tid_range_parser *parser) | |
178 | { | |
179 | return parser->qualified; | |
180 | } | |
181 | ||
182 | /* Helper for tid_range_parser_get_tid and | |
183 | tid_range_parser_get_tid_range. Return the next range if THR_END | |
184 | is non-NULL, return a single thread ID otherwise. */ | |
185 | ||
186 | static int | |
187 | get_tid_or_range (struct tid_range_parser *parser, int *inf_num, | |
188 | int *thr_start, int *thr_end) | |
189 | { | |
190 | if (parser->state == TID_RANGE_STATE_INFERIOR) | |
191 | { | |
192 | const char *p; | |
193 | const char *space; | |
194 | ||
195 | space = skip_to_space (parser->string); | |
196 | ||
197 | p = parser->string; | |
198 | while (p < space && *p != '.') | |
199 | p++; | |
200 | if (p < space) | |
201 | { | |
202 | const char *dot = p; | |
203 | ||
204 | /* Parse number to the left of the dot. */ | |
205 | p = parser->string; | |
3f5b7598 PA |
206 | parser->inf_num |
207 | = get_positive_number_trailer (&p, '.', parser->string); | |
5d5658a1 | 208 | if (parser->inf_num == 0) |
3f5b7598 | 209 | return 0; |
5d5658a1 PA |
210 | |
211 | parser->qualified = 1; | |
212 | p = dot + 1; | |
213 | ||
214 | if (isspace (*p)) | |
3f5b7598 | 215 | return 0; |
5d5658a1 PA |
216 | } |
217 | else | |
218 | { | |
219 | parser->inf_num = parser->default_inferior; | |
220 | parser->qualified = 0; | |
221 | p = parser->string; | |
222 | } | |
223 | ||
224 | init_number_or_range (&parser->range_parser, p); | |
71ef29a8 PA |
225 | if (p[0] == '*' && (p[1] == '\0' || isspace (p[1]))) |
226 | { | |
227 | /* Setup the number range parser to return numbers in the | |
228 | whole [1,INT_MAX] range. */ | |
229 | number_range_setup_range (&parser->range_parser, 1, INT_MAX, | |
230 | skip_spaces_const (p + 1)); | |
231 | parser->state = TID_RANGE_STATE_STAR_RANGE; | |
232 | } | |
233 | else | |
234 | parser->state = TID_RANGE_STATE_THREAD_RANGE; | |
5d5658a1 PA |
235 | } |
236 | ||
237 | *inf_num = parser->inf_num; | |
238 | *thr_start = get_number_or_range (&parser->range_parser); | |
3f5b7598 PA |
239 | if (*thr_start < 0) |
240 | error (_("negative value: %s"), parser->string); | |
5d5658a1 | 241 | if (*thr_start == 0) |
3f5b7598 PA |
242 | { |
243 | parser->state = TID_RANGE_STATE_INFERIOR; | |
244 | return 0; | |
245 | } | |
5d5658a1 PA |
246 | |
247 | /* If we successfully parsed a thread number or finished parsing a | |
248 | thread range, switch back to assuming the next TID is | |
249 | inferior-qualified. */ | |
250 | if (parser->range_parser.end_ptr == NULL | |
251 | || parser->range_parser.string == parser->range_parser.end_ptr) | |
252 | { | |
253 | parser->state = TID_RANGE_STATE_INFERIOR; | |
254 | parser->string = parser->range_parser.string; | |
255 | ||
256 | if (thr_end != NULL) | |
257 | *thr_end = *thr_start; | |
258 | } | |
259 | ||
260 | /* If we're midway through a range, and the caller wants the end | |
261 | value, return it and skip to the end of the range. */ | |
71ef29a8 PA |
262 | if (thr_end != NULL |
263 | && (parser->state == TID_RANGE_STATE_THREAD_RANGE | |
264 | || parser->state == TID_RANGE_STATE_STAR_RANGE)) | |
5d5658a1 PA |
265 | { |
266 | *thr_end = parser->range_parser.end_value; | |
267 | tid_range_parser_skip (parser); | |
268 | } | |
269 | ||
270 | return (*inf_num != 0 && *thr_start != 0); | |
271 | } | |
272 | ||
273 | /* See tid-parse.h. */ | |
274 | ||
275 | int | |
276 | tid_range_parser_get_tid_range (struct tid_range_parser *parser, int *inf_num, | |
277 | int *thr_start, int *thr_end) | |
278 | { | |
279 | gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL); | |
280 | ||
281 | return get_tid_or_range (parser, inf_num, thr_start, thr_end); | |
282 | } | |
283 | ||
284 | /* See tid-parse.h. */ | |
285 | ||
286 | int | |
287 | tid_range_parser_get_tid (struct tid_range_parser *parser, | |
288 | int *inf_num, int *thr_num) | |
289 | { | |
290 | gdb_assert (inf_num != NULL && thr_num != NULL); | |
291 | ||
292 | return get_tid_or_range (parser, inf_num, thr_num, NULL); | |
293 | } | |
294 | ||
295 | /* See tid-parse.h. */ | |
296 | ||
71ef29a8 PA |
297 | int |
298 | tid_range_parser_star_range (struct tid_range_parser *parser) | |
299 | { | |
300 | return parser->state == TID_RANGE_STATE_STAR_RANGE; | |
301 | } | |
302 | ||
303 | /* See gdbthread.h. */ | |
304 | ||
5d5658a1 PA |
305 | int |
306 | tid_is_in_list (const char *list, int default_inferior, | |
307 | int inf_num, int thr_num) | |
308 | { | |
309 | struct tid_range_parser parser; | |
310 | ||
311 | if (list == NULL || *list == '\0') | |
312 | return 1; | |
313 | ||
314 | tid_range_parser_init (&parser, list, default_inferior); | |
315 | while (!tid_range_parser_finished (&parser)) | |
316 | { | |
317 | int tmp_inf, tmp_thr_start, tmp_thr_end; | |
318 | ||
319 | if (!tid_range_parser_get_tid_range (&parser, &tmp_inf, | |
320 | &tmp_thr_start, &tmp_thr_end)) | |
321 | invalid_thread_id_error (parser.string); | |
322 | if (tmp_inf == inf_num | |
323 | && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end) | |
324 | return 1; | |
325 | } | |
326 | return 0; | |
327 | } |