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: | |
137 | return parser->range_parser.finished; | |
138 | } | |
139 | ||
140 | gdb_assert_not_reached (_("unhandled state")); | |
141 | } | |
142 | ||
143 | /* See tid-parse.h. */ | |
144 | ||
145 | const char * | |
146 | tid_range_parser_string (struct tid_range_parser *parser) | |
147 | { | |
148 | switch (parser->state) | |
149 | { | |
150 | case TID_RANGE_STATE_INFERIOR: | |
151 | return parser->string; | |
152 | case TID_RANGE_STATE_THREAD_RANGE: | |
153 | return parser->range_parser.string; | |
154 | } | |
155 | ||
156 | gdb_assert_not_reached (_("unhandled state")); | |
157 | } | |
158 | ||
159 | /* See tid-parse.h. */ | |
160 | ||
161 | void | |
162 | tid_range_parser_skip (struct tid_range_parser *parser) | |
163 | { | |
164 | gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE) | |
165 | && parser->range_parser.in_range); | |
166 | ||
167 | tid_range_parser_init (parser, parser->range_parser.end_ptr, | |
168 | parser->default_inferior); | |
169 | } | |
170 | ||
171 | /* See tid-parse.h. */ | |
172 | ||
173 | int | |
174 | tid_range_parser_qualified (struct tid_range_parser *parser) | |
175 | { | |
176 | return parser->qualified; | |
177 | } | |
178 | ||
179 | /* Helper for tid_range_parser_get_tid and | |
180 | tid_range_parser_get_tid_range. Return the next range if THR_END | |
181 | is non-NULL, return a single thread ID otherwise. */ | |
182 | ||
183 | static int | |
184 | get_tid_or_range (struct tid_range_parser *parser, int *inf_num, | |
185 | int *thr_start, int *thr_end) | |
186 | { | |
187 | if (parser->state == TID_RANGE_STATE_INFERIOR) | |
188 | { | |
189 | const char *p; | |
190 | const char *space; | |
191 | ||
192 | space = skip_to_space (parser->string); | |
193 | ||
194 | p = parser->string; | |
195 | while (p < space && *p != '.') | |
196 | p++; | |
197 | if (p < space) | |
198 | { | |
199 | const char *dot = p; | |
200 | ||
201 | /* Parse number to the left of the dot. */ | |
202 | p = parser->string; | |
3f5b7598 PA |
203 | parser->inf_num |
204 | = get_positive_number_trailer (&p, '.', parser->string); | |
5d5658a1 | 205 | if (parser->inf_num == 0) |
3f5b7598 | 206 | return 0; |
5d5658a1 PA |
207 | |
208 | parser->qualified = 1; | |
209 | p = dot + 1; | |
210 | ||
211 | if (isspace (*p)) | |
3f5b7598 | 212 | return 0; |
5d5658a1 PA |
213 | } |
214 | else | |
215 | { | |
216 | parser->inf_num = parser->default_inferior; | |
217 | parser->qualified = 0; | |
218 | p = parser->string; | |
219 | } | |
220 | ||
221 | init_number_or_range (&parser->range_parser, p); | |
222 | parser->state = TID_RANGE_STATE_THREAD_RANGE; | |
223 | } | |
224 | ||
225 | *inf_num = parser->inf_num; | |
226 | *thr_start = get_number_or_range (&parser->range_parser); | |
3f5b7598 PA |
227 | if (*thr_start < 0) |
228 | error (_("negative value: %s"), parser->string); | |
5d5658a1 | 229 | if (*thr_start == 0) |
3f5b7598 PA |
230 | { |
231 | parser->state = TID_RANGE_STATE_INFERIOR; | |
232 | return 0; | |
233 | } | |
5d5658a1 PA |
234 | |
235 | /* If we successfully parsed a thread number or finished parsing a | |
236 | thread range, switch back to assuming the next TID is | |
237 | inferior-qualified. */ | |
238 | if (parser->range_parser.end_ptr == NULL | |
239 | || parser->range_parser.string == parser->range_parser.end_ptr) | |
240 | { | |
241 | parser->state = TID_RANGE_STATE_INFERIOR; | |
242 | parser->string = parser->range_parser.string; | |
243 | ||
244 | if (thr_end != NULL) | |
245 | *thr_end = *thr_start; | |
246 | } | |
247 | ||
248 | /* If we're midway through a range, and the caller wants the end | |
249 | value, return it and skip to the end of the range. */ | |
250 | if (thr_end != NULL && parser->state == TID_RANGE_STATE_THREAD_RANGE) | |
251 | { | |
252 | *thr_end = parser->range_parser.end_value; | |
253 | tid_range_parser_skip (parser); | |
254 | } | |
255 | ||
256 | return (*inf_num != 0 && *thr_start != 0); | |
257 | } | |
258 | ||
259 | /* See tid-parse.h. */ | |
260 | ||
261 | int | |
262 | tid_range_parser_get_tid_range (struct tid_range_parser *parser, int *inf_num, | |
263 | int *thr_start, int *thr_end) | |
264 | { | |
265 | gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL); | |
266 | ||
267 | return get_tid_or_range (parser, inf_num, thr_start, thr_end); | |
268 | } | |
269 | ||
270 | /* See tid-parse.h. */ | |
271 | ||
272 | int | |
273 | tid_range_parser_get_tid (struct tid_range_parser *parser, | |
274 | int *inf_num, int *thr_num) | |
275 | { | |
276 | gdb_assert (inf_num != NULL && thr_num != NULL); | |
277 | ||
278 | return get_tid_or_range (parser, inf_num, thr_num, NULL); | |
279 | } | |
280 | ||
281 | /* See tid-parse.h. */ | |
282 | ||
283 | int | |
284 | tid_is_in_list (const char *list, int default_inferior, | |
285 | int inf_num, int thr_num) | |
286 | { | |
287 | struct tid_range_parser parser; | |
288 | ||
289 | if (list == NULL || *list == '\0') | |
290 | return 1; | |
291 | ||
292 | tid_range_parser_init (&parser, list, default_inferior); | |
293 | while (!tid_range_parser_finished (&parser)) | |
294 | { | |
295 | int tmp_inf, tmp_thr_start, tmp_thr_end; | |
296 | ||
297 | if (!tid_range_parser_get_tid_range (&parser, &tmp_inf, | |
298 | &tmp_thr_start, &tmp_thr_end)) | |
299 | invalid_thread_id_error (parser.string); | |
300 | if (tmp_inf == inf_num | |
301 | && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end) | |
302 | return 1; | |
303 | } | |
304 | return 0; | |
305 | } |