1 1.1 christos /* Copyright (C) 2023 Free Software Foundation, Inc. 2 1.1 christos 3 1.1 christos This file is part of GDB. 4 1.1 christos 5 1.1 christos This program is free software; you can redistribute it and/or modify 6 1.1 christos it under the terms of the GNU General Public License as published by 7 1.1 christos the Free Software Foundation; either version 3 of the License, or 8 1.1 christos (at your option) any later version. 9 1.1 christos 10 1.1 christos This program is distributed in the hope that it will be useful, 11 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 12 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 1.1 christos GNU General Public License for more details. 14 1.1 christos 15 1.1 christos You should have received a copy of the GNU General Public License 16 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 1.1 christos 18 1.1 christos #include "defs.h" 19 1.1 christos #include "gdbsupport/gdb_assert.h" 20 1.1 christos #include "gdbsupport/selftest.h" 21 1.1 christos #include "test-target.h" 22 1.1 christos #include "scoped-mock-context.h" 23 1.1 christos #include "break-cond-parse.h" 24 1.1 christos #include "tid-parse.h" 25 1.1 christos #include "ada-lang.h" 26 1.1 christos #include "exceptions.h" 27 1.1 christos 28 1.1 christos /* When parsing tokens from a string, which direction are we parsing? 29 1.1 christos 30 1.1 christos Given the following string and pointer 'ptr': 31 1.1 christos 32 1.1 christos ABC DEF GHI JKL 33 1.1 christos ^ 34 1.1 christos ptr 35 1.1 christos 36 1.1 christos Parsing 'forward' will return the token 'GHI' and update 'ptr' to point 37 1.1 christos between GHI and JKL. Parsing 'backward' will return the token 'DEF' and 38 1.1 christos update 'ptr' to point between ABC and DEF. 39 1.1 christos */ 40 1.1 christos 41 1.1 christos enum class parse_direction 42 1.1 christos { 43 1.1 christos /* Parse the next token forwards. */ 44 1.1 christos forward, 45 1.1 christos 46 1.1 christos /* Parse the previous token backwards. */ 47 1.1 christos backward 48 1.1 christos }; 49 1.1 christos 50 1.1 christos /* Find the next token in DIRECTION from *CURR. */ 51 1.1 christos 52 1.1 christos static std::string_view 53 1.1 christos find_next_token (const char **curr, parse_direction direction) 54 1.1 christos { 55 1.1 christos const char *tok_start, *tok_end; 56 1.1 christos 57 1.1 christos gdb_assert (**curr != '\0'); 58 1.1 christos 59 1.1 christos if (direction == parse_direction::forward) 60 1.1 christos { 61 1.1 christos *curr = skip_spaces (*curr); 62 1.1 christos tok_start = *curr; 63 1.1 christos *curr = skip_to_space (*curr); 64 1.1 christos tok_end = *curr - 1; 65 1.1 christos } 66 1.1 christos else 67 1.1 christos { 68 1.1 christos gdb_assert (direction == parse_direction::backward); 69 1.1 christos 70 1.1 christos while (isspace (**curr)) 71 1.1 christos --(*curr); 72 1.1 christos 73 1.1 christos tok_end = *curr; 74 1.1 christos 75 1.1 christos while (!isspace (**curr)) 76 1.1 christos --(*curr); 77 1.1 christos 78 1.1 christos tok_start = (*curr) + 1; 79 1.1 christos } 80 1.1 christos 81 1.1 christos return std::string_view (tok_start, tok_end - tok_start + 1); 82 1.1 christos } 83 1.1 christos 84 1.1 christos /* A class that represents a complete parsed token. Each token has a type 85 1.1 christos and a std::string_view into the original breakpoint condition string. */ 86 1.1 christos 87 1.1 christos struct token 88 1.1 christos { 89 1.1 christos /* The types a token might take. */ 90 1.1 christos enum class type 91 1.1 christos { 92 1.1 christos /* These are the token types for the 'if', 'thread', 'inferior', and 93 1.1 christos 'task' keywords. The m_content for these token types is the value 94 1.1 christos passed to the keyword, not the keyword itself. */ 95 1.1 christos CONDITION, 96 1.1 christos THREAD, 97 1.1 christos INFERIOR, 98 1.1 christos TASK, 99 1.1 christos 100 1.1 christos /* This is the token used when we find unknown content, the m_content 101 1.1 christos for this token is the rest of the input string. */ 102 1.1 christos REST, 103 1.1 christos 104 1.1 christos /* This is the token for the -force-condition token, the m_content for 105 1.1 christos this token contains the keyword itself. */ 106 1.1 christos FORCE 107 1.1 christos }; 108 1.1 christos 109 1.1 christos token (enum type type, std::string_view content) 110 1.1 christos : m_type (type), 111 1.1 christos m_content (std::move (content)) 112 1.1 christos { 113 1.1 christos /* Nothing. */ 114 1.1 christos } 115 1.1 christos 116 1.1 christos /* Return a string representing this token. Only used for debug. */ 117 1.1 christos std::string to_string () const 118 1.1 christos { 119 1.1 christos switch (m_type) 120 1.1 christos { 121 1.1 christos case type::CONDITION: 122 1.1 christos return string_printf ("{ CONDITION: \"%s\" }", 123 1.1 christos std::string (m_content).c_str ()); 124 1.1 christos case type::THREAD: 125 1.1 christos return string_printf ("{ THREAD: \"%s\" }", 126 1.1 christos std::string (m_content).c_str ()); 127 1.1 christos case type::INFERIOR: 128 1.1 christos return string_printf ("{ INFERIOR: \"%s\" }", 129 1.1 christos std::string (m_content).c_str ()); 130 1.1 christos case type::TASK: 131 1.1 christos return string_printf ("{ TASK: \"%s\" }", 132 1.1 christos std::string (m_content).c_str ()); 133 1.1 christos case type::REST: 134 1.1 christos return string_printf ("{ REST: \"%s\" }", 135 1.1 christos std::string (m_content).c_str ()); 136 1.1 christos case type::FORCE: 137 1.1 christos return string_printf ("{ FORCE }"); 138 1.1 christos default: 139 1.1 christos return "** unknown **"; 140 1.1 christos } 141 1.1 christos } 142 1.1 christos 143 1.1 christos /* The type of this token. */ 144 1.1 christos const type &get_type () const 145 1.1 christos { 146 1.1 christos return m_type; 147 1.1 christos } 148 1.1 christos 149 1.1 christos /* Return the value of this token. */ 150 1.1 christos const std::string_view &get_value () const 151 1.1 christos { 152 1.1 christos gdb_assert (m_content.size () > 0); 153 1.1 christos return m_content; 154 1.1 christos } 155 1.1 christos 156 1.1 christos /* Extend this token with the contents of OTHER. This only makes sense 157 1.1 christos if OTHER is the next token after this one in the original string, 158 1.1 christos however, enforcing that restriction is left to the caller of this 159 1.1 christos function. 160 1.1 christos 161 1.1 christos When OTHER is a keyword/value token, e.g. 'thread 1', the m_content 162 1.1 christos for OTHER will only point to the '1'. However, as the m_content is a 163 1.1 christos std::string_view, then when we merge the m_content of OTHER into this 164 1.1 christos token we automatically merge in the 'thread' part too, as it 165 1.1 christos naturally sits between this token and OTHER. */ 166 1.1 christos 167 1.1 christos void 168 1.1 christos extend (const token &other) 169 1.1 christos { 170 1.1 christos m_content = std::string_view (this->m_content.data (), 171 1.1 christos (other.m_content.data () 172 1.1 christos - this->m_content.data () 173 1.1 christos + other.m_content.size ())); 174 1.1 christos } 175 1.1 christos 176 1.1 christos private: 177 1.1 christos /* The type of this token. */ 178 1.1 christos type m_type; 179 1.1 christos 180 1.1 christos /* The important content part of this token. The extend member function 181 1.1 christos depends on this being a std::string_view. */ 182 1.1 christos std::string_view m_content; 183 1.1 christos }; 184 1.1 christos 185 1.1 christos /* Split STR, a breakpoint condition string, into a vector of tokens where 186 1.1 christos each token represents a component of the condition. Tokens are first 187 1.1 christos parsed from the front of STR until we encounter an 'if' token. At this 188 1.1 christos point tokens are parsed from the end of STR until we encounter an 189 1.1 christos unknown token, which we assume is the other end of the 'if' condition. 190 1.1 christos If when scanning forward we encounter an unknown token then the 191 1.1 christos remainder of STR is placed into a 'rest' token (the rest of the 192 1.1 christos string), and no backward scan is performed. */ 193 1.1 christos 194 1.1 christos static std::vector<token> 195 1.1 christos parse_all_tokens (const char *str) 196 1.1 christos { 197 1.1 christos gdb_assert (str != nullptr); 198 1.1 christos 199 1.1 christos std::vector<token> forward_results; 200 1.1 christos std::vector<token> backward_results; 201 1.1 christos 202 1.1 christos const char *cond_start = nullptr; 203 1.1 christos const char *cond_end = nullptr; 204 1.1 christos parse_direction direction = parse_direction::forward; 205 1.1 christos std::vector<token> *curr_results = &forward_results; 206 1.1 christos while (*str != '\0') 207 1.1 christos { 208 1.1 christos /* Find the next token. If moving backward and this token starts at 209 1.1 christos the same location as the condition then we must have found the 210 1.1 christos other end of the condition string -- we're done. */ 211 1.1 christos std::string_view t = find_next_token (&str, direction); 212 1.1 christos if (direction == parse_direction::backward && t.data () <= cond_start) 213 1.1 christos { 214 1.1 christos cond_end = &t.back (); 215 1.1 christos break; 216 1.1 christos } 217 1.1 christos 218 1.1 christos /* We only have a single flag option to check for. All the other 219 1.1 christos options take a value so require an additional token to be found. 220 1.1 christos Additionally, we require that this flag be at least '-f', we 221 1.1 christos don't allow it to be abbreviated to '-'. */ 222 1.1 christos if (t.length () > 1 && startswith ("-force-condition", t)) 223 1.1 christos { 224 1.1 christos curr_results->emplace_back (token::type::FORCE, t); 225 1.1 christos continue; 226 1.1 christos } 227 1.1 christos 228 1.1 christos /* Maybe the first token was the last token in the string. If this 229 1.1 christos is the case then we definitely can't try to extract a value 230 1.1 christos token. This also means that the token T is meaningless. Reset 231 1.1 christos TOK to point at the start of the unknown content and break out of 232 1.1 christos the loop. We'll record the unknown part of the string outside of 233 1.1 christos the scanning loop (below). */ 234 1.1 christos if (direction == parse_direction::forward && *str == '\0') 235 1.1 christos { 236 1.1 christos str = t.data (); 237 1.1 christos break; 238 1.1 christos } 239 1.1 christos 240 1.1 christos /* As before, find the next token and, if we are scanning backwards, 241 1.1 christos check that we have not reached the start of the condition string. */ 242 1.1 christos std::string_view v = find_next_token (&str, direction); 243 1.1 christos if (direction == parse_direction::backward && v.data () <= cond_start) 244 1.1 christos { 245 1.1 christos /* Use token T here as that must also be part of the condition 246 1.1 christos string. */ 247 1.1 christos cond_end = &t.back (); 248 1.1 christos break; 249 1.1 christos } 250 1.1 christos 251 1.1 christos /* When moving backward we will first parse the value token then the 252 1.1 christos keyword token, so swap them now. */ 253 1.1 christos if (direction == parse_direction::backward) 254 1.1 christos std::swap (t, v); 255 1.1 christos 256 1.1 christos /* Check for valid option in token T. If we find a valid option then 257 1.1 christos parse the value from the token V. Except for 'if', that's handled 258 1.1 christos differently. 259 1.1 christos 260 1.1 christos For the 'if' token we need to capture the entire condition 261 1.1 christos string, so record the start of the condition string and then 262 1.1 christos start scanning backwards looking for the end of the condition 263 1.1 christos string. 264 1.1 christos 265 1.1 christos The order of these checks is important, at least the check for 266 1.1 christos 'thread' must occur before the check for 'task'. We accept 267 1.1 christos abbreviations of these token names, and 't' should resolve to 268 1.1 christos 'thread', which will only happen if we check 'thread' first. */ 269 1.1 christos if (direction == parse_direction::forward && startswith ("if", t)) 270 1.1 christos { 271 1.1 christos cond_start = v.data (); 272 1.1 christos str = str + strlen (str); 273 1.1 christos gdb_assert (*str == '\0'); 274 1.1 christos --str; 275 1.1 christos direction = parse_direction::backward; 276 1.1 christos curr_results = &backward_results; 277 1.1 christos continue; 278 1.1 christos } 279 1.1 christos else if (startswith ("thread", t)) 280 1.1 christos curr_results->emplace_back (token::type::THREAD, v); 281 1.1 christos else if (startswith ("inferior", t)) 282 1.1 christos curr_results->emplace_back (token::type::INFERIOR, v); 283 1.1 christos else if (startswith ("task", t)) 284 1.1 christos curr_results->emplace_back (token::type::TASK, v); 285 1.1 christos else 286 1.1 christos { 287 1.1 christos /* An unknown token. If we are scanning forward then reset TOK 288 1.1 christos to point at the start of the unknown content, we record this 289 1.1 christos outside of the scanning loop (below). 290 1.1 christos 291 1.1 christos If we are scanning backward then unknown content is assumed to 292 1.1 christos be the other end of the condition string, obviously, this is 293 1.1 christos just a heuristic, we could be looking at a mistyped command 294 1.1 christos line, but this will be spotted when the condition is 295 1.1 christos eventually evaluated. 296 1.1 christos 297 1.1 christos Either way, no more scanning is required after this. */ 298 1.1 christos if (direction == parse_direction::forward) 299 1.1 christos str = t.data (); 300 1.1 christos else 301 1.1 christos { 302 1.1 christos gdb_assert (direction == parse_direction::backward); 303 1.1 christos cond_end = &v.back (); 304 1.1 christos } 305 1.1 christos break; 306 1.1 christos } 307 1.1 christos } 308 1.1 christos 309 1.1 christos if (cond_start != nullptr) 310 1.1 christos { 311 1.1 christos /* If we found the start of a condition string then we should have 312 1.1 christos switched to backward scan mode, and found the end of the condition 313 1.1 christos string. Capture the whole condition string into COND_STRING 314 1.1 christos now. */ 315 1.1 christos gdb_assert (direction == parse_direction::backward); 316 1.1 christos gdb_assert (cond_end != nullptr); 317 1.1 christos 318 1.1 christos std::string_view v (cond_start, cond_end - cond_start + 1); 319 1.1 christos 320 1.1 christos forward_results.emplace_back (token::type::CONDITION, v); 321 1.1 christos } 322 1.1 christos else if (*str != '\0') 323 1.1 christos { 324 1.1 christos /* If we didn't have a condition start pointer then we should still 325 1.1 christos be in forward scanning mode. If we didn't reach the end of the 326 1.1 christos input string (TOK is not at the null character) then the rest of 327 1.1 christos the input string is garbage that we didn't understand. 328 1.1 christos 329 1.1 christos Record the unknown content into REST. The caller of this function 330 1.1 christos will report this as an error later on. We could report the error 331 1.1 christos here, but we prefer to allow the caller to run other checks, and 332 1.1 christos prioritise other errors before reporting this problem. */ 333 1.1 christos gdb_assert (direction == parse_direction::forward); 334 1.1 christos gdb_assert (cond_end == nullptr); 335 1.1 christos 336 1.1 christos std::string_view v (str, strlen (str)); 337 1.1 christos 338 1.1 christos forward_results.emplace_back (token::type::REST, v); 339 1.1 christos } 340 1.1 christos 341 1.1 christos /* If we have tokens in the BACKWARD_RESULTS vector then this means that 342 1.1 christos we found an 'if' condition (which will be the last thing in the 343 1.1 christos FORWARD_RESULTS vector), and then we started a backward scan. 344 1.1 christos 345 1.1 christos The last tokens from the input string (those after the 'if' condition) 346 1.1 christos will be the first tokens added to the BACKWARD_RESULTS vector, so the 347 1.1 christos last items in the BACKWARD_RESULTS vector are those next to the 'if' 348 1.1 christos condition. 349 1.1 christos 350 1.1 christos Check the tokens in the BACKWARD_RESULTS vector from back to front. 351 1.1 christos If the tokens look invalid then we assume that they are actually part 352 1.1 christos of the 'if' condition, and merge the token with the 'if' condition. 353 1.1 christos If it turns out that this was incorrect and that instead the user just 354 1.1 christos messed up entering the token value, then this will show as an error 355 1.1 christos when parsing the 'if' condition. 356 1.1 christos 357 1.1 christos Doing this allows us to handle things like: 358 1.1 christos 359 1.1 christos break function if ( variable == thread ) 360 1.1 christos 361 1.1 christos Where 'thread' is a local variable within 'function'. When parsing 362 1.1 christos this we will initially see 'thread )' as a thread token with ')' as 363 1.1 christos the value. However, the following code will spot that ')' is not a 364 1.1 christos valid thread-id, and so we merge 'thread )' into the 'if' condition 365 1.1 christos string. 366 1.1 christos 367 1.1 christos This code also handles the special treatment for '-force-condition', 368 1.1 christos which exists for backwards compatibility reasons. Traditionally this 369 1.1 christos flag, if it occurred immediately after the 'if' condition, would be 370 1.1 christos treated as part of the 'if' condition. When the breakpoint condition 371 1.1 christos parsing code was rewritten, this behavior was retained. */ 372 1.1 christos gdb_assert (backward_results.empty () 373 1.1 christos || (forward_results.back ().get_type () 374 1.1 christos == token::type::CONDITION)); 375 1.1 christos while (!backward_results.empty ()) 376 1.1 christos { 377 1.1 christos token &t = backward_results.back (); 378 1.1 christos 379 1.1 christos if (t.get_type () == token::type::FORCE) 380 1.1 christos forward_results.back ().extend (std::move (t)); 381 1.1 christos else if (t.get_type () == token::type::THREAD) 382 1.1 christos { 383 1.1 christos const char *end; 384 1.1 christos std::string v (t.get_value ()); 385 1.1 christos if (is_thread_id (v.c_str (), &end) && *end == '\0') 386 1.1 christos break; 387 1.1 christos forward_results.back ().extend (std::move (t)); 388 1.1 christos } 389 1.1 christos else if (t.get_type () == token::type::INFERIOR 390 1.1 christos || t.get_type () == token::type::TASK) 391 1.1 christos { 392 1.1 christos /* Place the token's value into a null-terminated string, parse 393 1.1 christos the string as a number and check that the entire string was 394 1.1 christos parsed. If this is true then this looks like a valid inferior 395 1.1 christos or task number, otherwise, assume an invalid id, and merge 396 1.1 christos this token with the 'if' token. */ 397 1.1 christos char *end; 398 1.1 christos std::string v (t.get_value ()); 399 1.1 christos (void) strtol (v.c_str (), &end, 0); 400 1.1 christos if (end > v.c_str () && *end == '\0') 401 1.1 christos break; 402 1.1 christos forward_results.back ().extend (std::move (t)); 403 1.1 christos } 404 1.1 christos else 405 1.1 christos gdb_assert_not_reached ("unexpected token type"); 406 1.1 christos 407 1.1 christos /* If we found an actual valid token above then we will have broken 408 1.1 christos out of the loop. We only get here if the token was merged with 409 1.1 christos the 'if' condition, in which case we can discard the last token 410 1.1 christos and then check the token before that. */ 411 1.1 christos backward_results.pop_back (); 412 1.1 christos } 413 1.1 christos 414 1.1 christos /* If after the above checks we still have some tokens in the 415 1.1 christos BACKWARD_RESULTS vector, then these need to be appended to the 416 1.1 christos FORWARD_RESULTS vector. However, we first reverse the order so that 417 1.1 christos FORWARD_RESULTS retains the tokens in the order they appeared in the 418 1.1 christos input string. */ 419 1.1 christos if (!backward_results.empty ()) 420 1.1 christos forward_results.insert (forward_results.end (), 421 1.1 christos backward_results.rbegin (), 422 1.1 christos backward_results.rend ()); 423 1.1 christos 424 1.1 christos return forward_results; 425 1.1 christos } 426 1.1 christos 427 1.1 christos /* Called when the global debug_breakpoint is true. Prints VEC to the 428 1.1 christos debug output stream. */ 429 1.1 christos 430 1.1 christos static void 431 1.1 christos dump_condition_tokens (const std::vector<token> &vec) 432 1.1 christos { 433 1.1 christos gdb_assert (debug_breakpoint); 434 1.1 christos 435 1.1 christos bool first = true; 436 1.1 christos std::string str = "Tokens: "; 437 1.1 christos for (const token &t : vec) 438 1.1 christos { 439 1.1 christos if (!first) 440 1.1 christos str += " "; 441 1.1 christos first = false; 442 1.1 christos str += t.to_string (); 443 1.1 christos } 444 1.1 christos breakpoint_debug_printf ("%s", str.c_str ()); 445 1.1 christos } 446 1.1 christos 447 1.1 christos /* See break-cond-parse.h. */ 448 1.1 christos 449 1.1 christos void 450 1.1 christos create_breakpoint_parse_arg_string 451 1.1 christos (const char *str, gdb::unique_xmalloc_ptr<char> *cond_string_ptr, 452 1.1 christos int *thread_ptr, int *inferior_ptr, int *task_ptr, 453 1.1 christos gdb::unique_xmalloc_ptr<char> *rest_ptr, bool *force_ptr) 454 1.1 christos { 455 1.1 christos /* Set up the defaults. */ 456 1.1 christos cond_string_ptr->reset (); 457 1.1 christos rest_ptr->reset (); 458 1.1 christos *thread_ptr = -1; 459 1.1 christos *inferior_ptr = -1; 460 1.1 christos *task_ptr = -1; 461 1.1 christos *force_ptr = false; 462 1.1 christos 463 1.1 christos if (str == nullptr) 464 1.1 christos return; 465 1.1 christos 466 1.1 christos /* Split STR into a series of tokens. */ 467 1.1 christos std::vector<token> tokens = parse_all_tokens (str); 468 1.1 christos if (debug_breakpoint) 469 1.1 christos dump_condition_tokens (tokens); 470 1.1 christos 471 1.1 christos /* Temporary variables. Initialised to the default state, then updated 472 1.1 christos as we parse TOKENS. If all of TOKENS is parsed successfully then the 473 1.1 christos state from these variables is copied into the output arguments before 474 1.1 christos the function returns. */ 475 1.1 christos int thread = -1, inferior = -1, task = -1; 476 1.1 christos bool force = false; 477 1.1 christos gdb::unique_xmalloc_ptr<char> cond_string, rest; 478 1.1 christos 479 1.1 christos for (const token &t : tokens) 480 1.1 christos { 481 1.1 christos std::string tok_value (t.get_value ()); 482 1.1 christos switch (t.get_type ()) 483 1.1 christos { 484 1.1 christos case token::type::FORCE: 485 1.1 christos force = true; 486 1.1 christos break; 487 1.1 christos case token::type::THREAD: 488 1.1 christos { 489 1.1 christos if (thread != -1) 490 1.1 christos error ("You can specify only one thread."); 491 1.1 christos if (task != -1 || inferior != -1) 492 1.1 christos error ("You can specify only one of thread, inferior, or task."); 493 1.1 christos const char *tmptok; 494 1.1 christos thread_info *thr = parse_thread_id (tok_value.c_str (), &tmptok); 495 1.1 christos gdb_assert (*tmptok == '\0'); 496 1.1 christos thread = thr->global_num; 497 1.1 christos } 498 1.1 christos break; 499 1.1 christos case token::type::INFERIOR: 500 1.1 christos { 501 1.1 christos if (inferior != -1) 502 1.1 christos error ("You can specify only one inferior."); 503 1.1 christos if (task != -1 || thread != -1) 504 1.1 christos error ("You can specify only one of thread, inferior, or task."); 505 1.1 christos char *tmptok; 506 1.1 christos long inferior_id = strtol (tok_value.c_str (), &tmptok, 0); 507 1.1 christos if (*tmptok != '\0') 508 1.1 christos error (_("Junk '%s' after inferior keyword."), tmptok); 509 1.1 christos if (inferior_id > INT_MAX) 510 1.1 christos error (_("No inferior number '%ld'"), inferior_id); 511 1.1 christos inferior = static_cast<int> (inferior_id); 512 1.1 christos struct inferior *inf = find_inferior_id (inferior); 513 1.1 christos if (inf == nullptr) 514 1.1 christos error (_("No inferior number '%d'"), inferior); 515 1.1 christos } 516 1.1 christos break; 517 1.1 christos case token::type::TASK: 518 1.1 christos { 519 1.1 christos if (task != -1) 520 1.1 christos error ("You can specify only one task."); 521 1.1 christos if (inferior != -1 || thread != -1) 522 1.1 christos error ("You can specify only one of thread, inferior, or task."); 523 1.1 christos char *tmptok; 524 1.1 christos long task_id = strtol (tok_value.c_str (), &tmptok, 0); 525 1.1 christos if (*tmptok != '\0') 526 1.1 christos error (_("Junk '%s' after task keyword."), tmptok); 527 1.1 christos if (task_id > INT_MAX) 528 1.1 christos error (_("Unknown task %ld"), task_id); 529 1.1 christos task = static_cast<int> (task_id); 530 1.1 christos if (!valid_task_id (task)) 531 1.1 christos error (_("Unknown task %d."), task); 532 1.1 christos } 533 1.1 christos break; 534 1.1 christos case token::type::CONDITION: 535 1.1 christos cond_string.reset (savestring (t.get_value ().data (), 536 1.1 christos t.get_value ().size ())); 537 1.1 christos break; 538 1.1 christos case token::type::REST: 539 1.1 christos rest.reset (savestring (t.get_value ().data (), 540 1.1 christos t.get_value ().size ())); 541 1.1 christos break; 542 1.1 christos } 543 1.1 christos } 544 1.1 christos 545 1.1 christos /* Move results into the output locations. */ 546 1.1 christos *force_ptr = force; 547 1.1 christos *thread_ptr = thread; 548 1.1 christos *inferior_ptr = inferior; 549 1.1 christos *task_ptr = task; 550 1.1 christos rest_ptr->reset (rest.release ()); 551 1.1 christos cond_string_ptr->reset (cond_string.release ()); 552 1.1 christos } 553 1.1 christos 554 1.1 christos #if GDB_SELF_TEST 555 1.1 christos 556 1.1 christos namespace selftests { 557 1.1 christos 558 1.1 christos /* Run a single test of the create_breakpoint_parse_arg_string function. 559 1.1 christos INPUT is passed to create_breakpoint_parse_arg_string while all other 560 1.1 christos arguments are the expected output from 561 1.1 christos create_breakpoint_parse_arg_string. */ 562 1.1 christos 563 1.1 christos static void 564 1.1 christos test (const char *input, const char *condition, int thread = -1, 565 1.1 christos int inferior = -1, int task = -1, bool force = false, 566 1.1 christos const char *rest = nullptr, const char *error_msg = nullptr) 567 1.1 christos { 568 1.1 christos gdb::unique_xmalloc_ptr<char> extracted_condition; 569 1.1 christos gdb::unique_xmalloc_ptr<char> extracted_rest; 570 1.1 christos int extracted_thread, extracted_inferior, extracted_task; 571 1.1 christos bool extracted_force_condition; 572 1.1 christos std::string exception_msg, error_str; 573 1.1 christos 574 1.1 christos if (error_msg != nullptr) 575 1.1 christos error_str = std::string (error_msg) + "\n"; 576 1.1 christos 577 1.1 christos try 578 1.1 christos { 579 1.1 christos create_breakpoint_parse_arg_string (input, &extracted_condition, 580 1.1 christos &extracted_thread, 581 1.1 christos &extracted_inferior, 582 1.1 christos &extracted_task, &extracted_rest, 583 1.1 christos &extracted_force_condition); 584 1.1 christos } 585 1.1 christos catch (const gdb_exception_error &ex) 586 1.1 christos { 587 1.1 christos string_file buf; 588 1.1 christos 589 1.1 christos exception_print (&buf, ex); 590 1.1 christos exception_msg = buf.release (); 591 1.1 christos } 592 1.1 christos 593 1.1 christos if ((condition == nullptr) != (extracted_condition.get () == nullptr) 594 1.1 christos || (condition != nullptr 595 1.1 christos && strcmp (condition, extracted_condition.get ()) != 0) 596 1.1 christos || (rest == nullptr) != (extracted_rest.get () == nullptr) 597 1.1 christos || (rest != nullptr && strcmp (rest, extracted_rest.get ()) != 0) 598 1.1 christos || thread != extracted_thread 599 1.1 christos || inferior != extracted_inferior 600 1.1 christos || task != extracted_task 601 1.1 christos || force != extracted_force_condition 602 1.1 christos || exception_msg != error_str) 603 1.1 christos { 604 1.1 christos if (run_verbose ()) 605 1.1 christos { 606 1.1 christos debug_printf ("input: '%s'\n", input); 607 1.1 christos debug_printf ("condition: '%s'\n", extracted_condition.get ()); 608 1.1 christos debug_printf ("rest: '%s'\n", extracted_rest.get ()); 609 1.1 christos debug_printf ("thread: %d\n", extracted_thread); 610 1.1 christos debug_printf ("inferior: %d\n", extracted_inferior); 611 1.1 christos debug_printf ("task: %d\n", extracted_task); 612 1.1 christos debug_printf ("forced: %s\n", 613 1.1 christos extracted_force_condition ? "true" : "false"); 614 1.1 christos debug_printf ("exception: '%s'\n", exception_msg.c_str ()); 615 1.1 christos } 616 1.1 christos 617 1.1 christos /* Report the failure. */ 618 1.1 christos SELF_CHECK (false); 619 1.1 christos } 620 1.1 christos } 621 1.1 christos 622 1.1 christos /* Wrapper for test function. Pass through the default values for all 623 1.1 christos parameters, except the last parameter, which indicates that we expect 624 1.1 christos INPUT to trigger an error. */ 625 1.1 christos 626 1.1 christos static void 627 1.1 christos test_error (const char *input, const char *error_msg) 628 1.1 christos { 629 1.1 christos test (input, nullptr, -1, -1, -1, false, nullptr, error_msg); 630 1.1 christos } 631 1.1 christos 632 1.1 christos /* Test the create_breakpoint_parse_arg_string function. Just wraps 633 1.1 christos multiple calls to the test function above. */ 634 1.1 christos 635 1.1 christos static void 636 1.1 christos create_breakpoint_parse_arg_string_tests () 637 1.1 christos { 638 1.1 christos gdbarch *arch = current_inferior ()->arch (); 639 1.1 christos scoped_restore_current_pspace_and_thread restore; 640 1.1 christos scoped_mock_context<test_target_ops> mock_target (arch); 641 1.1 christos 642 1.1 christos int global_thread_num = mock_target.mock_thread.global_num; 643 1.1 christos 644 1.1 christos /* Test parsing valid breakpoint condition strings. */ 645 1.1 christos test (" if blah ", "blah"); 646 1.1 christos test (" if blah thread 1", "blah", global_thread_num); 647 1.1 christos test (" if blah inferior 1", "blah", -1, 1); 648 1.1 christos test (" if blah thread 1 ", "blah", global_thread_num); 649 1.1 christos test ("thread 1 woof", nullptr, global_thread_num, -1, -1, false, "woof"); 650 1.1 christos test ("thread 1 X", nullptr, global_thread_num, -1, -1, false, "X"); 651 1.1 christos test (" if blah thread 1 -force-condition", "blah", global_thread_num, 652 1.1 christos -1, -1, true); 653 1.1 christos test (" -force-condition if blah thread 1", "blah", global_thread_num, 654 1.1 christos -1, -1, true); 655 1.1 christos test (" -force-condition if blah thread 1 ", "blah", global_thread_num, 656 1.1 christos -1, -1, true); 657 1.1 christos test ("thread 1 -force-condition if blah", "blah", global_thread_num, 658 1.1 christos -1, -1, true); 659 1.1 christos test ("if (A::outer::func ())", "(A::outer::func ())"); 660 1.1 christos test ("if ( foo == thread )", "( foo == thread )"); 661 1.1 christos test ("if ( foo == thread ) inferior 1", "( foo == thread )", -1, 1); 662 1.1 christos test ("if ( foo == thread ) thread 1", "( foo == thread )", 663 1.1 christos global_thread_num); 664 1.1 christos test ("if foo == thread", "foo == thread"); 665 1.1 christos test ("if foo == thread 1", "foo ==", global_thread_num); 666 1.1 christos 667 1.1 christos /* Test parsing some invalid breakpoint condition strings. */ 668 1.1 christos test_error ("thread 1 if foo == 123 thread 1", 669 1.1 christos "You can specify only one thread."); 670 1.1 christos test_error ("thread 1 if foo == 123 inferior 1", 671 1.1 christos "You can specify only one of thread, inferior, or task."); 672 1.1 christos test_error ("thread 1 if foo == 123 task 1", 673 1.1 christos "You can specify only one of thread, inferior, or task."); 674 1.1 christos test_error ("inferior 1 if foo == 123 inferior 1", 675 1.1 christos "You can specify only one inferior."); 676 1.1 christos test_error ("inferior 1 if foo == 123 thread 1", 677 1.1 christos "You can specify only one of thread, inferior, or task."); 678 1.1 christos test_error ("inferior 1 if foo == 123 task 1", 679 1.1 christos "You can specify only one of thread, inferior, or task."); 680 1.1 christos test_error ("thread 1.2.3", "Invalid thread ID: 1.2.3"); 681 1.1 christos test_error ("thread 1/2", "Invalid thread ID: 1/2"); 682 1.1 christos test_error ("thread 1xxx", "Invalid thread ID: 1xxx"); 683 1.1 christos test_error ("inferior 1xxx", "Junk 'xxx' after inferior keyword."); 684 1.1 christos test_error ("task 1xxx", "Junk 'xxx' after task keyword."); 685 1.1 christos } 686 1.1 christos 687 1.1 christos } // namespace selftests 688 1.1 christos #endif /* GDB_SELF_TEST */ 689 1.1 christos 690 1.1 christos void _initialize_break_cond_parse (); 691 1.1 christos void 692 1.1 christos _initialize_break_cond_parse () 693 1.1 christos { 694 1.1 christos #if GDB_SELF_TEST 695 1.1 christos selftests::register_test 696 1.1 christos ("create_breakpoint_parse_arg_string", 697 1.1 christos selftests::create_breakpoint_parse_arg_string_tests); 698 1.1 christos #endif 699 1.1 christos } 700