1/* TID parsing for GDB, the GNU debugger. 2 3 Copyright (C) 2015-2020 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 28void ATTRIBUTE_NORETURN 29invalid_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 40static int 41get_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 53struct thread_info * 54parse_thread_id (const char *tidstr, const char **end) 55{ 56 const char *number = tidstr; 57 const char *dot, *p1; 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; 70 inf_num = get_positive_number_trailer (&p1, '.', number); 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 88 thr_num = get_positive_number_trailer (&p1, 0, number); 89 if (thr_num == 0) 90 invalid_thread_id_error (number); 91 92 thread_info *tp = nullptr; 93 for (thread_info *it : inf->threads ()) 94 if (it->per_inf_num == thr_num) 95 { 96 tp = it; 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 116tid_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 124void 125tid_range_parser::init (const char *tidlist, int default_inferior) 126{ 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; 132} 133 134/* See tid-parse.h. */ 135 136bool 137tid_range_parser::finished () const 138{ 139 switch (m_state) 140 { 141 case STATE_INFERIOR: 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 == '*')); 149 case STATE_THREAD_RANGE: 150 case STATE_STAR_RANGE: 151 return m_range_parser.finished (); 152 } 153 154 gdb_assert_not_reached (_("unhandled state")); 155} 156 157/* See tid-parse.h. */ 158 159const char * 160tid_range_parser::cur_tok () const 161{ 162 switch (m_state) 163 { 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 (); 169 } 170 171 gdb_assert_not_reached (_("unhandled state")); 172} 173 174void 175tid_range_parser::skip_range () 176{ 177 gdb_assert (m_state == STATE_THREAD_RANGE 178 || m_state == STATE_STAR_RANGE); 179 180 m_range_parser.skip_range (); 181 init (m_range_parser.cur_tok (), m_default_inferior); 182} 183 184/* See tid-parse.h. */ 185 186bool 187tid_range_parser::tid_is_qualified () const 188{ 189 return m_qualified; 190} 191 192/* Helper for tid_range_parser::get_tid and 193 tid_range_parser::get_tid_range. Return the next range if THR_END 194 is non-NULL, return a single thread ID otherwise. */ 195 196bool 197tid_range_parser::get_tid_or_range (int *inf_num, 198 int *thr_start, int *thr_end) 199{ 200 if (m_state == STATE_INFERIOR) 201 { 202 const char *p; 203 const char *space; 204 205 space = skip_to_space (m_cur_tok); 206 207 p = m_cur_tok; 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. */ 215 p = m_cur_tok; 216 m_inf_num = get_positive_number_trailer (&p, '.', m_cur_tok); 217 if (m_inf_num == 0) 218 return 0; 219 220 m_qualified = true; 221 p = dot + 1; 222 223 if (isspace (*p)) 224 return false; 225 } 226 else 227 { 228 m_inf_num = m_default_inferior; 229 m_qualified = false; 230 p = m_cur_tok; 231 } 232 233 m_range_parser.init (p); 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. */ 238 m_range_parser.setup_range (1, INT_MAX, skip_spaces (p + 1)); 239 m_state = STATE_STAR_RANGE; 240 } 241 else 242 m_state = STATE_THREAD_RANGE; 243 } 244 245 *inf_num = m_inf_num; 246 *thr_start = m_range_parser.get_number (); 247 if (*thr_start < 0) 248 error (_("negative value: %s"), m_cur_tok); 249 if (*thr_start == 0) 250 { 251 m_state = STATE_INFERIOR; 252 return false; 253 } 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. */ 258 if (!m_range_parser.in_range ()) 259 { 260 m_state = STATE_INFERIOR; 261 m_cur_tok = m_range_parser.cur_tok (); 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. */ 269 if (thr_end != NULL 270 && (m_state == STATE_THREAD_RANGE 271 || m_state == STATE_STAR_RANGE)) 272 { 273 *thr_end = m_range_parser.end_value (); 274 275 skip_range (); 276 } 277 278 return (*inf_num != 0 && *thr_start != 0); 279} 280 281/* See tid-parse.h. */ 282 283bool 284tid_range_parser::get_tid_range (int *inf_num, 285 int *thr_start, int *thr_end) 286{ 287 gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL); 288 289 return get_tid_or_range (inf_num, thr_start, thr_end); 290} 291 292/* See tid-parse.h. */ 293 294bool 295tid_range_parser::get_tid (int *inf_num, int *thr_num) 296{ 297 gdb_assert (inf_num != NULL && thr_num != NULL); 298 299 return get_tid_or_range (inf_num, thr_num, NULL); 300} 301 302/* See tid-parse.h. */ 303 304bool 305tid_range_parser::in_star_range () const 306{ 307 return m_state == STATE_STAR_RANGE; 308} 309 310bool 311tid_range_parser::in_thread_range () const 312{ 313 return m_state == STATE_THREAD_RANGE; 314} 315 316/* See tid-parse.h. */ 317 318int 319tid_is_in_list (const char *list, int default_inferior, 320 int inf_num, int thr_num) 321{ 322 if (list == NULL || *list == '\0') 323 return 1; 324 325 tid_range_parser parser (list, default_inferior); 326 if (parser.finished ()) 327 invalid_thread_id_error (parser.cur_tok ()); 328 while (!parser.finished ()) 329 { 330 int tmp_inf, tmp_thr_start, tmp_thr_end; 331 332 if (!parser.get_tid_range (&tmp_inf, &tmp_thr_start, &tmp_thr_end)) 333 invalid_thread_id_error (parser.cur_tok ()); 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} 340