xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/break-catch-syscall.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
1 /* Everything about syscall catchpoints, for GDB.
2 
3    Copyright (C) 2009-2023 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 <ctype.h>
22 #include "breakpoint.h"
23 #include "gdbcmd.h"
24 #include "inferior.h"
25 #include "cli/cli-utils.h"
26 #include "annotate.h"
27 #include "mi/mi-common.h"
28 #include "valprint.h"
29 #include "arch-utils.h"
30 #include "observable.h"
31 #include "xml-syscall.h"
32 #include "cli/cli-style.h"
33 #include "cli/cli-decode.h"
34 
35 /* An instance of this type is used to represent a syscall
36    catchpoint.  */
37 
38 struct syscall_catchpoint : public catchpoint
39 {
40   syscall_catchpoint (struct gdbarch *gdbarch, bool tempflag,
41 		      std::vector<int> &&calls)
42     : catchpoint (gdbarch, tempflag, nullptr),
43       syscalls_to_be_caught (std::move (calls))
44   {
45   }
46 
47   int insert_location (struct bp_location *) override;
48   int remove_location (struct bp_location *,
49 		       enum remove_bp_reason reason) override;
50   int breakpoint_hit (const struct bp_location *bl,
51 		      const address_space *aspace,
52 		      CORE_ADDR bp_addr,
53 		      const target_waitstatus &ws) override;
54   enum print_stop_action print_it (const bpstat *bs) const override;
55   bool print_one (bp_location **) const override;
56   void print_mention () const override;
57   void print_recreate (struct ui_file *fp) const override;
58 
59   /* Syscall numbers used for the 'catch syscall' feature.  If no
60      syscall has been specified for filtering, it is empty.
61      Otherwise, it holds a list of all syscalls to be caught.  */
62   std::vector<int> syscalls_to_be_caught;
63 };
64 
65 struct catch_syscall_inferior_data
66 {
67   /* We keep a count of the number of times the user has requested a
68      particular syscall to be tracked, and pass this information to the
69      target.  This lets capable targets implement filtering directly.  */
70 
71   /* Number of times that "any" syscall is requested.  */
72   int any_syscall_count;
73 
74   /* Count of each system call.  */
75   std::vector<int> syscalls_counts;
76 
77   /* This counts all syscall catch requests, so we can readily determine
78      if any catching is necessary.  */
79   int total_syscalls_count;
80 };
81 
82 static const registry<inferior>::key<catch_syscall_inferior_data>
83   catch_syscall_inferior_data;
84 
85 static struct catch_syscall_inferior_data *
86 get_catch_syscall_inferior_data (struct inferior *inf)
87 {
88   struct catch_syscall_inferior_data *inf_data;
89 
90   inf_data = catch_syscall_inferior_data.get (inf);
91   if (inf_data == NULL)
92     inf_data = catch_syscall_inferior_data.emplace (inf);
93 
94   return inf_data;
95 }
96 
97 /* Implement the "insert" method for syscall catchpoints.  */
98 
99 int
100 syscall_catchpoint::insert_location (struct bp_location *bl)
101 {
102   struct inferior *inf = current_inferior ();
103   struct catch_syscall_inferior_data *inf_data
104     = get_catch_syscall_inferior_data (inf);
105 
106   ++inf_data->total_syscalls_count;
107   if (syscalls_to_be_caught.empty ())
108     ++inf_data->any_syscall_count;
109   else
110     {
111       for (int iter : syscalls_to_be_caught)
112 	{
113 	  if (iter >= inf_data->syscalls_counts.size ())
114 	    inf_data->syscalls_counts.resize (iter + 1);
115 	  ++inf_data->syscalls_counts[iter];
116 	}
117     }
118 
119   return target_set_syscall_catchpoint (inferior_ptid.pid (),
120 					inf_data->total_syscalls_count != 0,
121 					inf_data->any_syscall_count,
122 					inf_data->syscalls_counts);
123 }
124 
125 /* Implement the "remove" method for syscall catchpoints.  */
126 
127 int
128 syscall_catchpoint::remove_location (struct bp_location *bl,
129 				     enum remove_bp_reason reason)
130 {
131   struct inferior *inf = current_inferior ();
132   struct catch_syscall_inferior_data *inf_data
133     = get_catch_syscall_inferior_data (inf);
134 
135   --inf_data->total_syscalls_count;
136   if (syscalls_to_be_caught.empty ())
137     --inf_data->any_syscall_count;
138   else
139     {
140       for (int iter : syscalls_to_be_caught)
141 	{
142 	  if (iter >= inf_data->syscalls_counts.size ())
143 	    /* Shouldn't happen.  */
144 	    continue;
145 	  --inf_data->syscalls_counts[iter];
146 	}
147     }
148 
149   return target_set_syscall_catchpoint (inferior_ptid.pid (),
150 					inf_data->total_syscalls_count != 0,
151 					inf_data->any_syscall_count,
152 					inf_data->syscalls_counts);
153 }
154 
155 /* Implement the "breakpoint_hit" method for syscall catchpoints.  */
156 
157 int
158 syscall_catchpoint::breakpoint_hit (const struct bp_location *bl,
159 				    const address_space *aspace,
160 				    CORE_ADDR bp_addr,
161 				    const target_waitstatus &ws)
162 {
163   /* We must check if we are catching specific syscalls in this
164      breakpoint.  If we are, then we must guarantee that the called
165      syscall is the same syscall we are catching.  */
166   int syscall_number = 0;
167 
168   if (ws.kind () != TARGET_WAITKIND_SYSCALL_ENTRY
169       && ws.kind () != TARGET_WAITKIND_SYSCALL_RETURN)
170     return 0;
171 
172   syscall_number = ws.syscall_number ();
173 
174   /* Now, checking if the syscall is the same.  */
175   if (!syscalls_to_be_caught.empty ())
176     {
177       for (int iter : syscalls_to_be_caught)
178 	if (syscall_number == iter)
179 	  return 1;
180 
181       return 0;
182     }
183 
184   return 1;
185 }
186 
187 /* Implement the "print_it" method for syscall catchpoints.  */
188 
189 enum print_stop_action
190 syscall_catchpoint::print_it (const bpstat *bs) const
191 {
192   struct ui_out *uiout = current_uiout;
193   struct breakpoint *b = bs->breakpoint_at;
194   /* These are needed because we want to know in which state a
195      syscall is.  It can be in the TARGET_WAITKIND_SYSCALL_ENTRY
196      or TARGET_WAITKIND_SYSCALL_RETURN, and depending on it we
197      must print "called syscall" or "returned from syscall".  */
198   struct target_waitstatus last;
199   struct syscall s;
200   struct gdbarch *gdbarch = b->gdbarch;
201 
202   get_last_target_status (nullptr, nullptr, &last);
203 
204   get_syscall_by_number (gdbarch, last.syscall_number (), &s);
205 
206   annotate_catchpoint (b->number);
207   maybe_print_thread_hit_breakpoint (uiout);
208 
209   if (b->disposition == disp_del)
210     uiout->text ("Temporary catchpoint ");
211   else
212     uiout->text ("Catchpoint ");
213   if (uiout->is_mi_like_p ())
214     {
215       uiout->field_string ("reason",
216 			   async_reason_lookup (last.kind () == TARGET_WAITKIND_SYSCALL_ENTRY
217 						? EXEC_ASYNC_SYSCALL_ENTRY
218 						: EXEC_ASYNC_SYSCALL_RETURN));
219       uiout->field_string ("disp", bpdisp_text (b->disposition));
220     }
221   print_num_locno (bs, uiout);
222 
223   if (last.kind () == TARGET_WAITKIND_SYSCALL_ENTRY)
224     uiout->text (" (call to syscall ");
225   else
226     uiout->text (" (returned from syscall ");
227 
228   if (s.name == NULL || uiout->is_mi_like_p ())
229     uiout->field_signed ("syscall-number", last.syscall_number ());
230   if (s.name != NULL)
231     uiout->field_string ("syscall-name", s.name);
232 
233   uiout->text ("), ");
234 
235   return PRINT_SRC_AND_LOC;
236 }
237 
238 /* Implement the "print_one" method for syscall catchpoints.  */
239 
240 bool
241 syscall_catchpoint::print_one (bp_location **last_loc) const
242 {
243   struct value_print_options opts;
244   struct ui_out *uiout = current_uiout;
245   struct gdbarch *gdbarch = loc->owner->gdbarch;
246 
247   get_user_print_options (&opts);
248   /* Field 4, the address, is omitted (which makes the columns not
249      line up too nicely with the headers, but the effect is relatively
250      readable).  */
251   if (opts.addressprint)
252     uiout->field_skip ("addr");
253   annotate_field (5);
254 
255   if (syscalls_to_be_caught.size () > 1)
256     uiout->text ("syscalls \"");
257   else
258     uiout->text ("syscall \"");
259 
260   if (!syscalls_to_be_caught.empty ())
261     {
262       std::string text;
263 
264       bool first = true;
265       for (int iter : syscalls_to_be_caught)
266 	{
267 	  struct syscall s;
268 	  get_syscall_by_number (gdbarch, iter, &s);
269 
270 	  if (!first)
271 	    text += ", ";
272 	  first = false;
273 
274 	  if (s.name != NULL)
275 	    text += s.name;
276 	  else
277 	    text += std::to_string (iter);
278 	}
279       uiout->field_string ("what", text.c_str ());
280     }
281   else
282     uiout->field_string ("what", "<any syscall>", metadata_style.style ());
283   uiout->text ("\" ");
284 
285   if (uiout->is_mi_like_p ())
286     uiout->field_string ("catch-type", "syscall");
287 
288   return true;
289 }
290 
291 /* Implement the "print_mention" method for syscall catchpoints.  */
292 
293 void
294 syscall_catchpoint::print_mention () const
295 {
296   struct gdbarch *gdbarch = loc->owner->gdbarch;
297 
298   if (!syscalls_to_be_caught.empty ())
299     {
300       if (syscalls_to_be_caught.size () > 1)
301 	gdb_printf (_("Catchpoint %d (syscalls"), number);
302       else
303 	gdb_printf (_("Catchpoint %d (syscall"), number);
304 
305       for (int iter : syscalls_to_be_caught)
306 	{
307 	  struct syscall s;
308 	  get_syscall_by_number (gdbarch, iter, &s);
309 
310 	  if (s.name != NULL)
311 	    gdb_printf (" '%s' [%d]", s.name, s.number);
312 	  else
313 	    gdb_printf (" %d", s.number);
314 	}
315       gdb_printf (")");
316     }
317   else
318     gdb_printf (_("Catchpoint %d (any syscall)"), number);
319 }
320 
321 /* Implement the "print_recreate" method for syscall catchpoints.  */
322 
323 void
324 syscall_catchpoint::print_recreate (struct ui_file *fp) const
325 {
326   struct gdbarch *gdbarch = loc->gdbarch;
327 
328   gdb_printf (fp, "catch syscall");
329 
330   for (int iter : syscalls_to_be_caught)
331     {
332       struct syscall s;
333 
334       get_syscall_by_number (gdbarch, iter, &s);
335       if (s.name != NULL)
336 	gdb_printf (fp, " %s", s.name);
337       else
338 	gdb_printf (fp, " %d", s.number);
339     }
340 
341   print_recreate_thread (fp);
342 }
343 
344 /* Returns non-zero if 'b' is a syscall catchpoint.  */
345 
346 static int
347 syscall_catchpoint_p (struct breakpoint *b)
348 {
349   return dynamic_cast<syscall_catchpoint *> (b) != nullptr;
350 }
351 
352 static void
353 create_syscall_event_catchpoint (int tempflag, std::vector<int> &&filter)
354 {
355   struct gdbarch *gdbarch = get_current_arch ();
356 
357   std::unique_ptr<syscall_catchpoint> c
358     (new syscall_catchpoint (gdbarch, tempflag, std::move (filter)));
359 
360   install_breakpoint (0, std::move (c), 1);
361 }
362 
363 /* Splits the argument using space as delimiter.  */
364 
365 static std::vector<int>
366 catch_syscall_split_args (const char *arg)
367 {
368   std::vector<int> result;
369   struct gdbarch *gdbarch = target_gdbarch ();
370 
371   while (*arg != '\0')
372     {
373       int i, syscall_number;
374       char *endptr;
375       char cur_name[128];
376       struct syscall s;
377 
378       /* Skip whitespace.  */
379       arg = skip_spaces (arg);
380 
381       for (i = 0; i < 127 && arg[i] && !isspace (arg[i]); ++i)
382 	cur_name[i] = arg[i];
383       cur_name[i] = '\0';
384       arg += i;
385 
386       /* Check if the user provided a syscall name, group, or a number.  */
387       syscall_number = (int) strtol (cur_name, &endptr, 0);
388       if (*endptr == '\0')
389 	{
390 	  if (syscall_number < 0)
391 	    error (_("Unknown syscall number '%d'."), syscall_number);
392 	  get_syscall_by_number (gdbarch, syscall_number, &s);
393 	  result.push_back (s.number);
394 	}
395       else if (startswith (cur_name, "g:")
396 	       || startswith (cur_name, "group:"))
397 	{
398 	  /* We have a syscall group.  Let's expand it into a syscall
399 	     list before inserting.  */
400 	  const char *group_name;
401 
402 	  /* Skip over "g:" and "group:" prefix strings.  */
403 	  group_name = strchr (cur_name, ':') + 1;
404 
405 	  if (!get_syscalls_by_group (gdbarch, group_name, &result))
406 	    error (_("Unknown syscall group '%s'."), group_name);
407 	}
408       else
409 	{
410 	  /* We have a name.  Let's check if it's valid and fetch a
411 	     list of matching numbers.  */
412 	  if (!get_syscalls_by_name (gdbarch, cur_name, &result))
413 	    /* Here we have to issue an error instead of a warning,
414 	       because GDB cannot do anything useful if there's no
415 	       syscall number to be caught.  */
416 	    error (_("Unknown syscall name '%s'."), cur_name);
417 	}
418     }
419 
420   return result;
421 }
422 
423 /* Implement the "catch syscall" command.  */
424 
425 static void
426 catch_syscall_command_1 (const char *arg, int from_tty,
427 			 struct cmd_list_element *command)
428 {
429   int tempflag;
430   std::vector<int> filter;
431   struct syscall s;
432   struct gdbarch *gdbarch = get_current_arch ();
433 
434   /* Checking if the feature if supported.  */
435   if (gdbarch_get_syscall_number_p (gdbarch) == 0)
436     error (_("The feature 'catch syscall' is not supported on \
437 this architecture yet."));
438 
439   tempflag = command->context () == CATCH_TEMPORARY;
440 
441   arg = skip_spaces (arg);
442 
443   /* We need to do this first "dummy" translation in order
444      to get the syscall XML file loaded or, most important,
445      to display a warning to the user if there's no XML file
446      for his/her architecture.  */
447   get_syscall_by_number (gdbarch, 0, &s);
448 
449   /* The allowed syntax is:
450      catch syscall
451      catch syscall <name | number> [<name | number> ... <name | number>]
452 
453      Let's check if there's a syscall name.  */
454 
455   if (arg != NULL)
456     filter = catch_syscall_split_args (arg);
457 
458   create_syscall_event_catchpoint (tempflag, std::move (filter));
459 }
460 
461 
462 /* Returns 0 if 'bp' is NOT a syscall catchpoint,
463    non-zero otherwise.  */
464 static int
465 is_syscall_catchpoint_enabled (struct breakpoint *bp)
466 {
467   if (syscall_catchpoint_p (bp)
468       && bp->enable_state != bp_disabled
469       && bp->enable_state != bp_call_disabled)
470     return 1;
471   else
472     return 0;
473 }
474 
475 int
476 catch_syscall_enabled (void)
477 {
478   struct catch_syscall_inferior_data *inf_data
479     = get_catch_syscall_inferior_data (current_inferior ());
480 
481   return inf_data->total_syscalls_count != 0;
482 }
483 
484 /* Helper function for catching_syscall_number.  return true if B is a syscall
485    catchpoint for SYSCALL_NUMBER, else false.  */
486 
487 static bool
488 catching_syscall_number_1 (struct breakpoint *b, int syscall_number)
489 {
490 
491   if (is_syscall_catchpoint_enabled (b))
492     {
493       struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
494 
495       if (!c->syscalls_to_be_caught.empty ())
496 	{
497 	  for (int iter : c->syscalls_to_be_caught)
498 	    if (syscall_number == iter)
499 	      return true;
500 	}
501       else
502 	return true;
503     }
504 
505   return false;
506 }
507 
508 bool
509 catching_syscall_number (int syscall_number)
510 {
511   for (breakpoint *b : all_breakpoints ())
512     if (catching_syscall_number_1 (b, syscall_number))
513       return true;
514 
515   return false;
516 }
517 
518 /* Complete syscall names.  Used by "catch syscall".  */
519 
520 static void
521 catch_syscall_completer (struct cmd_list_element *cmd,
522 			 completion_tracker &tracker,
523 			 const char *text, const char *word)
524 {
525   struct gdbarch *gdbarch = get_current_arch ();
526   gdb::unique_xmalloc_ptr<const char *> group_list;
527   const char *prefix;
528 
529   /* Completion considers ':' to be a word separator, so we use this to
530      verify whether the previous word was a group prefix.  If so, we
531      build the completion list using group names only.  */
532   for (prefix = word; prefix != text && prefix[-1] != ' '; prefix--)
533     ;
534 
535   if (startswith (prefix, "g:") || startswith (prefix, "group:"))
536     {
537       /* Perform completion inside 'group:' namespace only.  */
538       group_list.reset (get_syscall_group_names (gdbarch));
539       if (group_list != NULL)
540 	complete_on_enum (tracker, group_list.get (), word, word);
541     }
542   else
543     {
544       /* Complete with both, syscall names and groups.  */
545       gdb::unique_xmalloc_ptr<const char *> syscall_list
546 	(get_syscall_names (gdbarch));
547       group_list.reset (get_syscall_group_names (gdbarch));
548 
549       const char **group_ptr = group_list.get ();
550 
551       /* Hold on to strings while we're using them.  */
552       std::vector<std::string> holders;
553 
554       /* Append "group:" prefix to syscall groups.  */
555       for (int i = 0; group_ptr[i] != NULL; i++)
556 	holders.push_back (string_printf ("group:%s", group_ptr[i]));
557 
558       for (int i = 0; group_ptr[i] != NULL; i++)
559 	group_ptr[i] = holders[i].c_str ();
560 
561       if (syscall_list != NULL)
562 	complete_on_enum (tracker, syscall_list.get (), word, word);
563       if (group_list != NULL)
564 	complete_on_enum (tracker, group_ptr, word, word);
565     }
566 }
567 
568 static void
569 clear_syscall_counts (struct inferior *inf)
570 {
571   struct catch_syscall_inferior_data *inf_data
572     = get_catch_syscall_inferior_data (inf);
573 
574   inf_data->total_syscalls_count = 0;
575   inf_data->any_syscall_count = 0;
576   inf_data->syscalls_counts.clear ();
577 }
578 
579 void _initialize_break_catch_syscall ();
580 void
581 _initialize_break_catch_syscall ()
582 {
583   gdb::observers::inferior_exit.attach (clear_syscall_counts,
584 					"break-catch-syscall");
585 
586   add_catch_command ("syscall", _("\
587 Catch system calls by their names, groups and/or numbers.\n\
588 Arguments say which system calls to catch.  If no arguments are given,\n\
589 every system call will be caught.  Arguments, if given, should be one\n\
590 or more system call names (if your system supports that), system call\n\
591 groups or system call numbers."),
592 		     catch_syscall_command_1,
593 		     catch_syscall_completer,
594 		     CATCH_PERMANENT,
595 		     CATCH_TEMPORARY);
596 }
597