Per-inferior/Inferior-qualified thread IDs
[external/binutils.git] / gdb / tid-parse.c
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 /* See tid-parse.h.  */
35
36 struct thread_info *
37 parse_thread_id (const char *tidstr, const char **end)
38 {
39   const char *number = tidstr;
40   const char *dot, *p1;
41   struct thread_info *tp;
42   struct inferior *inf;
43   int thr_num;
44   int explicit_inf_id = 0;
45
46   dot = strchr (number, '.');
47
48   if (dot != NULL)
49     {
50       /* Parse number to the left of the dot.  */
51       int inf_num;
52
53       p1 = number;
54       inf_num = get_number_trailer (&p1, '.');
55       if (inf_num == 0)
56         invalid_thread_id_error (number);
57
58       inf = find_inferior_id (inf_num);
59       if (inf == NULL)
60         error (_("No inferior number '%d'"), inf_num);
61
62       explicit_inf_id = 1;
63       p1 = dot + 1;
64     }
65   else
66     {
67       inf = current_inferior ();
68
69       p1 = number;
70     }
71
72   thr_num = get_number_const (&p1);
73   if (thr_num == 0)
74     invalid_thread_id_error (number);
75
76   ALL_THREADS (tp)
77     {
78       if (ptid_get_pid (tp->ptid) == inf->pid
79           && tp->per_inf_num == thr_num)
80         break;
81     }
82
83   if (tp == NULL)
84     {
85       if (show_inferior_qualified_tids () || explicit_inf_id)
86         error (_("Unknown thread %d.%d."), inf->num, thr_num);
87       else
88         error (_("Unknown thread %d."), thr_num);
89     }
90
91   if (end != NULL)
92     *end = p1;
93
94   return tp;
95 }
96
97 /* See tid-parse.h.  */
98
99 void
100 tid_range_parser_init (struct tid_range_parser *parser, const char *tidlist,
101                        int default_inferior)
102 {
103   parser->state = TID_RANGE_STATE_INFERIOR;
104   parser->string = tidlist;
105   parser->inf_num = 0;
106   parser->qualified = 0;
107   parser->default_inferior = default_inferior;
108 }
109
110 /* See tid-parse.h.  */
111
112 int
113 tid_range_parser_finished (struct tid_range_parser *parser)
114 {
115   switch (parser->state)
116     {
117     case TID_RANGE_STATE_INFERIOR:
118       return *parser->string == '\0';
119     case TID_RANGE_STATE_THREAD_RANGE:
120       return parser->range_parser.finished;
121     }
122
123   gdb_assert_not_reached (_("unhandled state"));
124 }
125
126 /* See tid-parse.h.  */
127
128 const char *
129 tid_range_parser_string (struct tid_range_parser *parser)
130 {
131   switch (parser->state)
132     {
133     case TID_RANGE_STATE_INFERIOR:
134       return parser->string;
135     case TID_RANGE_STATE_THREAD_RANGE:
136       return parser->range_parser.string;
137     }
138
139   gdb_assert_not_reached (_("unhandled state"));
140 }
141
142 /* See tid-parse.h.  */
143
144 void
145 tid_range_parser_skip (struct tid_range_parser *parser)
146 {
147   gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE)
148               && parser->range_parser.in_range);
149
150   tid_range_parser_init (parser, parser->range_parser.end_ptr,
151                          parser->default_inferior);
152 }
153
154 /* See tid-parse.h.  */
155
156 int
157 tid_range_parser_qualified (struct tid_range_parser *parser)
158 {
159   return parser->qualified;
160 }
161
162 /* Helper for tid_range_parser_get_tid and
163    tid_range_parser_get_tid_range.  Return the next range if THR_END
164    is non-NULL, return a single thread ID otherwise.  */
165
166 static int
167 get_tid_or_range (struct tid_range_parser *parser, int *inf_num,
168                   int *thr_start, int *thr_end)
169 {
170   if (parser->state == TID_RANGE_STATE_INFERIOR)
171     {
172       const char *p;
173       const char *space;
174
175       space = skip_to_space (parser->string);
176
177       p = parser->string;
178       while (p < space && *p != '.')
179         p++;
180       if (p < space)
181         {
182           const char *dot = p;
183
184           /* Parse number to the left of the dot.  */
185           p = parser->string;
186           parser->inf_num = get_number_trailer (&p, '.');
187           if (parser->inf_num == 0)
188             invalid_thread_id_error (parser->string);
189
190           parser->qualified = 1;
191           p = dot + 1;
192
193           if (isspace (*p))
194             invalid_thread_id_error (parser->string);
195         }
196       else
197         {
198           parser->inf_num = parser->default_inferior;
199           parser->qualified = 0;
200           p = parser->string;
201         }
202
203       init_number_or_range (&parser->range_parser, p);
204       parser->state = TID_RANGE_STATE_THREAD_RANGE;
205     }
206
207   *inf_num = parser->inf_num;
208   *thr_start = get_number_or_range (&parser->range_parser);
209   if (*thr_start == 0)
210     invalid_thread_id_error (parser->string);
211
212   /* If we successfully parsed a thread number or finished parsing a
213      thread range, switch back to assuming the next TID is
214      inferior-qualified.  */
215   if (parser->range_parser.end_ptr == NULL
216       || parser->range_parser.string == parser->range_parser.end_ptr)
217     {
218       parser->state = TID_RANGE_STATE_INFERIOR;
219       parser->string = parser->range_parser.string;
220
221       if (thr_end != NULL)
222         *thr_end = *thr_start;
223     }
224
225   /* If we're midway through a range, and the caller wants the end
226      value, return it and skip to the end of the range.  */
227   if (thr_end != NULL && parser->state == TID_RANGE_STATE_THREAD_RANGE)
228     {
229       *thr_end = parser->range_parser.end_value;
230       tid_range_parser_skip (parser);
231     }
232
233   return (*inf_num != 0 && *thr_start != 0);
234 }
235
236 /* See tid-parse.h.  */
237
238 int
239 tid_range_parser_get_tid_range (struct tid_range_parser *parser, int *inf_num,
240                                 int *thr_start, int *thr_end)
241 {
242   gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL);
243
244   return get_tid_or_range (parser, inf_num, thr_start, thr_end);
245 }
246
247 /* See tid-parse.h.  */
248
249 int
250 tid_range_parser_get_tid (struct tid_range_parser *parser,
251                           int *inf_num, int *thr_num)
252 {
253   gdb_assert (inf_num != NULL && thr_num != NULL);
254
255   return get_tid_or_range (parser, inf_num, thr_num, NULL);
256 }
257
258 /* See tid-parse.h.  */
259
260 int
261 tid_is_in_list (const char *list, int default_inferior,
262                 int inf_num, int thr_num)
263 {
264   struct tid_range_parser parser;
265
266   if (list == NULL || *list == '\0')
267     return 1;
268
269   tid_range_parser_init (&parser, list, default_inferior);
270   while (!tid_range_parser_finished (&parser))
271     {
272       int tmp_inf, tmp_thr_start, tmp_thr_end;
273
274       if (!tid_range_parser_get_tid_range (&parser, &tmp_inf,
275                                            &tmp_thr_start, &tmp_thr_end))
276         invalid_thread_id_error (parser.string);
277       if (tmp_inf == inf_num
278           && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end)
279         return 1;
280     }
281   return 0;
282 }