Home | History | Annotate | Line # | Download | only in gdb
      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