xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/break-catch-syscall.c (revision 1580a27b92f58fcdcb23fdfbc04a7c2b54a0b7c8)
1 /* Everything about syscall catchpoints, for GDB.
2 
3    Copyright (C) 2009-2015 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 "observer.h"
31 #include "xml-syscall.h"
32 
33 /* An instance of this type is used to represent a syscall catchpoint.
34    It includes a "struct breakpoint" as a kind of base class; users
35    downcast to "struct breakpoint *" when needed.  A breakpoint is
36    really of this type iff its ops pointer points to
37    CATCH_SYSCALL_BREAKPOINT_OPS.  */
38 
39 struct syscall_catchpoint
40 {
41   /* The base class.  */
42   struct breakpoint base;
43 
44   /* Syscall numbers used for the 'catch syscall' feature.  If no
45      syscall has been specified for filtering, its value is NULL.
46      Otherwise, it holds a list of all syscalls to be caught.  The
47      list elements are allocated with xmalloc.  */
48   VEC(int) *syscalls_to_be_caught;
49 };
50 
51 /* Implement the "dtor" breakpoint_ops method for syscall
52    catchpoints.  */
53 
54 static void
55 dtor_catch_syscall (struct breakpoint *b)
56 {
57   struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
58 
59   VEC_free (int, c->syscalls_to_be_caught);
60 
61   base_breakpoint_ops.dtor (b);
62 }
63 
64 static const struct inferior_data *catch_syscall_inferior_data = NULL;
65 
66 struct catch_syscall_inferior_data
67 {
68   /* We keep a count of the number of times the user has requested a
69      particular syscall to be tracked, and pass this information to the
70      target.  This lets capable targets implement filtering directly.  */
71 
72   /* Number of times that "any" syscall is requested.  */
73   int any_syscall_count;
74 
75   /* Count of each system call.  */
76   VEC(int) *syscalls_counts;
77 
78   /* This counts all syscall catch requests, so we can readily determine
79      if any catching is necessary.  */
80   int total_syscalls_count;
81 };
82 
83 static struct catch_syscall_inferior_data*
84 get_catch_syscall_inferior_data (struct inferior *inf)
85 {
86   struct catch_syscall_inferior_data *inf_data;
87 
88   inf_data = inferior_data (inf, catch_syscall_inferior_data);
89   if (inf_data == NULL)
90     {
91       inf_data = XCNEW (struct catch_syscall_inferior_data);
92       set_inferior_data (inf, catch_syscall_inferior_data, inf_data);
93     }
94 
95   return inf_data;
96 }
97 
98 static void
99 catch_syscall_inferior_data_cleanup (struct inferior *inf, void *arg)
100 {
101   xfree (arg);
102 }
103 
104 
105 /* Implement the "insert" breakpoint_ops method for syscall
106    catchpoints.  */
107 
108 static int
109 insert_catch_syscall (struct bp_location *bl)
110 {
111   struct syscall_catchpoint *c = (struct syscall_catchpoint *) bl->owner;
112   struct inferior *inf = current_inferior ();
113   struct catch_syscall_inferior_data *inf_data
114     = get_catch_syscall_inferior_data (inf);
115 
116   ++inf_data->total_syscalls_count;
117   if (!c->syscalls_to_be_caught)
118     ++inf_data->any_syscall_count;
119   else
120     {
121       int i, iter;
122 
123       for (i = 0;
124            VEC_iterate (int, c->syscalls_to_be_caught, i, iter);
125            i++)
126 	{
127           int elem;
128 
129 	  if (iter >= VEC_length (int, inf_data->syscalls_counts))
130 	    {
131               int old_size = VEC_length (int, inf_data->syscalls_counts);
132               uintptr_t vec_addr_offset
133 		= old_size * ((uintptr_t) sizeof (int));
134               uintptr_t vec_addr;
135               VEC_safe_grow (int, inf_data->syscalls_counts, iter + 1);
136               vec_addr = ((uintptr_t) VEC_address (int,
137 						  inf_data->syscalls_counts)
138 			  + vec_addr_offset);
139               memset ((void *) vec_addr, 0,
140                       (iter + 1 - old_size) * sizeof (int));
141 	    }
142           elem = VEC_index (int, inf_data->syscalls_counts, iter);
143           VEC_replace (int, inf_data->syscalls_counts, iter, ++elem);
144 	}
145     }
146 
147   return target_set_syscall_catchpoint (ptid_get_pid (inferior_ptid),
148 					inf_data->total_syscalls_count != 0,
149 					inf_data->any_syscall_count,
150 					VEC_length (int,
151 						    inf_data->syscalls_counts),
152 					VEC_address (int,
153 						     inf_data->syscalls_counts));
154 }
155 
156 /* Implement the "remove" breakpoint_ops method for syscall
157    catchpoints.  */
158 
159 static int
160 remove_catch_syscall (struct bp_location *bl)
161 {
162   struct syscall_catchpoint *c = (struct syscall_catchpoint *) bl->owner;
163   struct inferior *inf = current_inferior ();
164   struct catch_syscall_inferior_data *inf_data
165     = get_catch_syscall_inferior_data (inf);
166 
167   --inf_data->total_syscalls_count;
168   if (!c->syscalls_to_be_caught)
169     --inf_data->any_syscall_count;
170   else
171     {
172       int i, iter;
173 
174       for (i = 0;
175            VEC_iterate (int, c->syscalls_to_be_caught, i, iter);
176            i++)
177 	{
178           int elem;
179 	  if (iter >= VEC_length (int, inf_data->syscalls_counts))
180 	    /* Shouldn't happen.  */
181 	    continue;
182           elem = VEC_index (int, inf_data->syscalls_counts, iter);
183           VEC_replace (int, inf_data->syscalls_counts, iter, --elem);
184         }
185     }
186 
187   return target_set_syscall_catchpoint (ptid_get_pid (inferior_ptid),
188 					inf_data->total_syscalls_count != 0,
189 					inf_data->any_syscall_count,
190 					VEC_length (int,
191 						    inf_data->syscalls_counts),
192 					VEC_address (int,
193 						     inf_data->syscalls_counts));
194 }
195 
196 /* Implement the "breakpoint_hit" breakpoint_ops method for syscall
197    catchpoints.  */
198 
199 static int
200 breakpoint_hit_catch_syscall (const struct bp_location *bl,
201 			      struct address_space *aspace, CORE_ADDR bp_addr,
202 			      const struct target_waitstatus *ws)
203 {
204   /* We must check if we are catching specific syscalls in this
205      breakpoint.  If we are, then we must guarantee that the called
206      syscall is the same syscall we are catching.  */
207   int syscall_number = 0;
208   const struct syscall_catchpoint *c
209     = (const struct syscall_catchpoint *) bl->owner;
210 
211   if (ws->kind != TARGET_WAITKIND_SYSCALL_ENTRY
212       && ws->kind != TARGET_WAITKIND_SYSCALL_RETURN)
213     return 0;
214 
215   syscall_number = ws->value.syscall_number;
216 
217   /* Now, checking if the syscall is the same.  */
218   if (c->syscalls_to_be_caught)
219     {
220       int i, iter;
221 
222       for (i = 0;
223            VEC_iterate (int, c->syscalls_to_be_caught, i, iter);
224            i++)
225 	if (syscall_number == iter)
226 	  return 1;
227 
228       return 0;
229     }
230 
231   return 1;
232 }
233 
234 /* Implement the "print_it" breakpoint_ops method for syscall
235    catchpoints.  */
236 
237 static enum print_stop_action
238 print_it_catch_syscall (bpstat bs)
239 {
240   struct ui_out *uiout = current_uiout;
241   struct breakpoint *b = bs->breakpoint_at;
242   /* These are needed because we want to know in which state a
243      syscall is.  It can be in the TARGET_WAITKIND_SYSCALL_ENTRY
244      or TARGET_WAITKIND_SYSCALL_RETURN, and depending on it we
245      must print "called syscall" or "returned from syscall".  */
246   ptid_t ptid;
247   struct target_waitstatus last;
248   struct syscall s;
249   struct gdbarch *gdbarch = bs->bp_location_at->gdbarch;
250 
251   get_last_target_status (&ptid, &last);
252 
253   get_syscall_by_number (gdbarch, last.value.syscall_number, &s);
254 
255   annotate_catchpoint (b->number);
256 
257   if (b->disposition == disp_del)
258     ui_out_text (uiout, "\nTemporary catchpoint ");
259   else
260     ui_out_text (uiout, "\nCatchpoint ");
261   if (ui_out_is_mi_like_p (uiout))
262     {
263       ui_out_field_string (uiout, "reason",
264 			   async_reason_lookup (last.kind == TARGET_WAITKIND_SYSCALL_ENTRY
265 						? EXEC_ASYNC_SYSCALL_ENTRY
266 						: EXEC_ASYNC_SYSCALL_RETURN));
267       ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition));
268     }
269   ui_out_field_int (uiout, "bkptno", b->number);
270 
271   if (last.kind == TARGET_WAITKIND_SYSCALL_ENTRY)
272     ui_out_text (uiout, " (call to syscall ");
273   else
274     ui_out_text (uiout, " (returned from syscall ");
275 
276   if (s.name == NULL || ui_out_is_mi_like_p (uiout))
277     ui_out_field_int (uiout, "syscall-number", last.value.syscall_number);
278   if (s.name != NULL)
279     ui_out_field_string (uiout, "syscall-name", s.name);
280 
281   ui_out_text (uiout, "), ");
282 
283   return PRINT_SRC_AND_LOC;
284 }
285 
286 /* Implement the "print_one" breakpoint_ops method for syscall
287    catchpoints.  */
288 
289 static void
290 print_one_catch_syscall (struct breakpoint *b,
291 			 struct bp_location **last_loc)
292 {
293   struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
294   struct value_print_options opts;
295   struct ui_out *uiout = current_uiout;
296   struct gdbarch *gdbarch = b->loc->gdbarch;
297 
298   get_user_print_options (&opts);
299   /* Field 4, the address, is omitted (which makes the columns not
300      line up too nicely with the headers, but the effect is relatively
301      readable).  */
302   if (opts.addressprint)
303     ui_out_field_skip (uiout, "addr");
304   annotate_field (5);
305 
306   if (c->syscalls_to_be_caught
307       && VEC_length (int, c->syscalls_to_be_caught) > 1)
308     ui_out_text (uiout, "syscalls \"");
309   else
310     ui_out_text (uiout, "syscall \"");
311 
312   if (c->syscalls_to_be_caught)
313     {
314       int i, iter;
315       char *text = xstrprintf ("%s", "");
316 
317       for (i = 0;
318            VEC_iterate (int, c->syscalls_to_be_caught, i, iter);
319            i++)
320         {
321           char *x = text;
322           struct syscall s;
323           get_syscall_by_number (gdbarch, iter, &s);
324 
325           if (s.name != NULL)
326             text = xstrprintf ("%s%s, ", text, s.name);
327           else
328             text = xstrprintf ("%s%d, ", text, iter);
329 
330           /* We have to xfree the last 'text' (now stored at 'x')
331              because xstrprintf dynamically allocates new space for it
332              on every call.  */
333 	  xfree (x);
334         }
335       /* Remove the last comma.  */
336       text[strlen (text) - 2] = '\0';
337       ui_out_field_string (uiout, "what", text);
338     }
339   else
340     ui_out_field_string (uiout, "what", "<any syscall>");
341   ui_out_text (uiout, "\" ");
342 
343   if (ui_out_is_mi_like_p (uiout))
344     ui_out_field_string (uiout, "catch-type", "syscall");
345 }
346 
347 /* Implement the "print_mention" breakpoint_ops method for syscall
348    catchpoints.  */
349 
350 static void
351 print_mention_catch_syscall (struct breakpoint *b)
352 {
353   struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
354   struct gdbarch *gdbarch = b->loc->gdbarch;
355 
356   if (c->syscalls_to_be_caught)
357     {
358       int i, iter;
359 
360       if (VEC_length (int, c->syscalls_to_be_caught) > 1)
361         printf_filtered (_("Catchpoint %d (syscalls"), b->number);
362       else
363         printf_filtered (_("Catchpoint %d (syscall"), b->number);
364 
365       for (i = 0;
366            VEC_iterate (int, c->syscalls_to_be_caught, i, iter);
367            i++)
368         {
369           struct syscall s;
370           get_syscall_by_number (gdbarch, iter, &s);
371 
372           if (s.name)
373             printf_filtered (" '%s' [%d]", s.name, s.number);
374           else
375             printf_filtered (" %d", s.number);
376         }
377       printf_filtered (")");
378     }
379   else
380     printf_filtered (_("Catchpoint %d (any syscall)"),
381                      b->number);
382 }
383 
384 /* Implement the "print_recreate" breakpoint_ops method for syscall
385    catchpoints.  */
386 
387 static void
388 print_recreate_catch_syscall (struct breakpoint *b, struct ui_file *fp)
389 {
390   struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
391   struct gdbarch *gdbarch = b->loc->gdbarch;
392 
393   fprintf_unfiltered (fp, "catch syscall");
394 
395   if (c->syscalls_to_be_caught)
396     {
397       int i, iter;
398 
399       for (i = 0;
400            VEC_iterate (int, c->syscalls_to_be_caught, i, iter);
401            i++)
402         {
403           struct syscall s;
404 
405           get_syscall_by_number (gdbarch, iter, &s);
406           if (s.name)
407             fprintf_unfiltered (fp, " %s", s.name);
408           else
409             fprintf_unfiltered (fp, " %d", s.number);
410         }
411     }
412   print_recreate_thread (b, fp);
413 }
414 
415 /* The breakpoint_ops structure to be used in syscall catchpoints.  */
416 
417 static struct breakpoint_ops catch_syscall_breakpoint_ops;
418 
419 /* Returns non-zero if 'b' is a syscall catchpoint.  */
420 
421 static int
422 syscall_catchpoint_p (struct breakpoint *b)
423 {
424   return (b->ops == &catch_syscall_breakpoint_ops);
425 }
426 
427 static void
428 create_syscall_event_catchpoint (int tempflag, VEC(int) *filter,
429                                  const struct breakpoint_ops *ops)
430 {
431   struct syscall_catchpoint *c;
432   struct gdbarch *gdbarch = get_current_arch ();
433 
434   c = XNEW (struct syscall_catchpoint);
435   init_catchpoint (&c->base, gdbarch, tempflag, NULL, ops);
436   c->syscalls_to_be_caught = filter;
437 
438   install_breakpoint (0, &c->base, 1);
439 }
440 
441 /* Splits the argument using space as delimiter.  Returns an xmalloc'd
442    filter list, or NULL if no filtering is required.  */
443 static VEC(int) *
444 catch_syscall_split_args (char *arg)
445 {
446   VEC(int) *result = NULL;
447   struct cleanup *cleanup = make_cleanup (VEC_cleanup (int), &result);
448   struct gdbarch *gdbarch = target_gdbarch ();
449 
450   while (*arg != '\0')
451     {
452       int i, syscall_number;
453       char *endptr;
454       char cur_name[128];
455       struct syscall s;
456 
457       /* Skip whitespace.  */
458       arg = skip_spaces (arg);
459 
460       for (i = 0; i < 127 && arg[i] && !isspace (arg[i]); ++i)
461 	cur_name[i] = arg[i];
462       cur_name[i] = '\0';
463       arg += i;
464 
465       /* Check if the user provided a syscall name or a number.  */
466       syscall_number = (int) strtol (cur_name, &endptr, 0);
467       if (*endptr == '\0')
468 	get_syscall_by_number (gdbarch, syscall_number, &s);
469       else
470 	{
471 	  /* We have a name.  Let's check if it's valid and convert it
472 	     to a number.  */
473 	  get_syscall_by_name (gdbarch, cur_name, &s);
474 
475 	  if (s.number == UNKNOWN_SYSCALL)
476 	    /* Here we have to issue an error instead of a warning,
477 	       because GDB cannot do anything useful if there's no
478 	       syscall number to be caught.  */
479 	    error (_("Unknown syscall name '%s'."), cur_name);
480 	}
481 
482       /* Ok, it's valid.  */
483       VEC_safe_push (int, result, s.number);
484     }
485 
486   discard_cleanups (cleanup);
487   return result;
488 }
489 
490 /* Implement the "catch syscall" command.  */
491 
492 static void
493 catch_syscall_command_1 (char *arg, int from_tty,
494 			 struct cmd_list_element *command)
495 {
496   int tempflag;
497   VEC(int) *filter;
498   struct syscall s;
499   struct gdbarch *gdbarch = get_current_arch ();
500 
501   /* Checking if the feature if supported.  */
502   if (gdbarch_get_syscall_number_p (gdbarch) == 0)
503     error (_("The feature 'catch syscall' is not supported on \
504 this architecture yet."));
505 
506   tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
507 
508   arg = skip_spaces (arg);
509 
510   /* We need to do this first "dummy" translation in order
511      to get the syscall XML file loaded or, most important,
512      to display a warning to the user if there's no XML file
513      for his/her architecture.  */
514   get_syscall_by_number (gdbarch, 0, &s);
515 
516   /* The allowed syntax is:
517      catch syscall
518      catch syscall <name | number> [<name | number> ... <name | number>]
519 
520      Let's check if there's a syscall name.  */
521 
522   if (arg != NULL)
523     filter = catch_syscall_split_args (arg);
524   else
525     filter = NULL;
526 
527   create_syscall_event_catchpoint (tempflag, filter,
528 				   &catch_syscall_breakpoint_ops);
529 }
530 
531 
532 /* Returns 0 if 'bp' is NOT a syscall catchpoint,
533    non-zero otherwise.  */
534 static int
535 is_syscall_catchpoint_enabled (struct breakpoint *bp)
536 {
537   if (syscall_catchpoint_p (bp)
538       && bp->enable_state != bp_disabled
539       && bp->enable_state != bp_call_disabled)
540     return 1;
541   else
542     return 0;
543 }
544 
545 int
546 catch_syscall_enabled (void)
547 {
548   struct catch_syscall_inferior_data *inf_data
549     = get_catch_syscall_inferior_data (current_inferior ());
550 
551   return inf_data->total_syscalls_count != 0;
552 }
553 
554 /* Helper function for catching_syscall_number.  If B is a syscall
555    catchpoint for SYSCALL_NUMBER, return 1 (which will make
556    'breakpoint_find_if' return).  Otherwise, return 0.  */
557 
558 static int
559 catching_syscall_number_1 (struct breakpoint *b,
560 			   void *data)
561 {
562   int syscall_number = (int) (uintptr_t) data;
563 
564   if (is_syscall_catchpoint_enabled (b))
565     {
566       struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
567 
568       if (c->syscalls_to_be_caught)
569 	{
570 	  int i, iter;
571 	  for (i = 0;
572 	       VEC_iterate (int, c->syscalls_to_be_caught, i, iter);
573 	       i++)
574 	    if (syscall_number == iter)
575 	      return 1;
576 	}
577       else
578 	return 1;
579     }
580 
581   return 0;
582 }
583 
584 int
585 catching_syscall_number (int syscall_number)
586 {
587   struct breakpoint *b = breakpoint_find_if (catching_syscall_number_1,
588 					 (void *) (uintptr_t) syscall_number);
589 
590   return b != NULL;
591 }
592 
593 /* Complete syscall names.  Used by "catch syscall".  */
594 static VEC (char_ptr) *
595 catch_syscall_completer (struct cmd_list_element *cmd,
596                          const char *text, const char *word)
597 {
598   const char **list = get_syscall_names (get_current_arch ());
599   VEC (char_ptr) *retlist
600     = (list == NULL) ? NULL : complete_on_enum (list, word, word);
601 
602   xfree (list);
603   return retlist;
604 }
605 
606 static void
607 clear_syscall_counts (struct inferior *inf)
608 {
609   struct catch_syscall_inferior_data *inf_data
610     = get_catch_syscall_inferior_data (inf);
611 
612   inf_data->total_syscalls_count = 0;
613   inf_data->any_syscall_count = 0;
614   VEC_free (int, inf_data->syscalls_counts);
615 }
616 
617 static void
618 initialize_syscall_catchpoint_ops (void)
619 {
620   struct breakpoint_ops *ops;
621 
622   initialize_breakpoint_ops ();
623 
624   /* Syscall catchpoints.  */
625   ops = &catch_syscall_breakpoint_ops;
626   *ops = base_breakpoint_ops;
627   ops->dtor = dtor_catch_syscall;
628   ops->insert_location = insert_catch_syscall;
629   ops->remove_location = remove_catch_syscall;
630   ops->breakpoint_hit = breakpoint_hit_catch_syscall;
631   ops->print_it = print_it_catch_syscall;
632   ops->print_one = print_one_catch_syscall;
633   ops->print_mention = print_mention_catch_syscall;
634   ops->print_recreate = print_recreate_catch_syscall;
635 }
636 
637 initialize_file_ftype _initialize_break_catch_syscall;
638 
639 void
640 _initialize_break_catch_syscall (void)
641 {
642   initialize_syscall_catchpoint_ops ();
643 
644   observer_attach_inferior_exit (clear_syscall_counts);
645   catch_syscall_inferior_data
646     = register_inferior_data_with_cleanup (NULL,
647 					   catch_syscall_inferior_data_cleanup);
648 
649   add_catch_command ("syscall", _("\
650 Catch system calls by their names and/or numbers.\n\
651 Arguments say which system calls to catch.  If no arguments\n\
652 are given, every system call will be caught.\n\
653 Arguments, if given, should be one or more system call names\n\
654 (if your system supports that), or system call numbers."),
655 		     catch_syscall_command_1,
656 		     catch_syscall_completer,
657 		     CATCH_PERMANENT,
658 		     CATCH_TEMPORARY);
659 }
660