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 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 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; 71 inf_num = get_positive_number_trailer (&p1, '.', number); 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 89 thr_num = get_positive_number_trailer (&p1, 0, number); 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 case TID_RANGE_STATE_STAR_RANGE: 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: 154 case TID_RANGE_STATE_STAR_RANGE: 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 { 166 gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE 167 || parser->state == TID_RANGE_STATE_STAR_RANGE) 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; 206 parser->inf_num 207 = get_positive_number_trailer (&p, '.', parser->string); 208 if (parser->inf_num == 0) 209 return 0; 210 211 parser->qualified = 1; 212 p = dot + 1; 213 214 if (isspace (*p)) 215 return 0; 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); 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; 235 } 236 237 *inf_num = parser->inf_num; 238 *thr_start = get_number_or_range (&parser->range_parser); 239 if (*thr_start < 0) 240 error (_("negative value: %s"), parser->string); 241 if (*thr_start == 0) 242 { 243 parser->state = TID_RANGE_STATE_INFERIOR; 244 return 0; 245 } 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. */ 262 if (thr_end != NULL 263 && (parser->state == TID_RANGE_STATE_THREAD_RANGE 264 || parser->state == TID_RANGE_STATE_STAR_RANGE)) 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 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 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 } 328