1 /* Everything about syscall catchpoints, for GDB.
3 Copyright (C) 2009-2021 Free Software Foundation, Inc.
5 This file is part of GDB.
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.
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.
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/>. */
22 #include "breakpoint.h"
25 #include "cli/cli-utils.h"
27 #include "mi/mi-common.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"
35 /* An instance of this type is used to represent a syscall catchpoint.
36 A breakpoint is really of this type iff its ops pointer points to
37 CATCH_SYSCALL_BREAKPOINT_OPS. */
39 struct syscall_catchpoint
: public breakpoint
41 /* Syscall numbers used for the 'catch syscall' feature. If no
42 syscall has been specified for filtering, it is empty.
43 Otherwise, it holds a list of all syscalls to be caught. */
44 std::vector
<int> syscalls_to_be_caught
;
47 struct catch_syscall_inferior_data
49 /* We keep a count of the number of times the user has requested a
50 particular syscall to be tracked, and pass this information to the
51 target. This lets capable targets implement filtering directly. */
53 /* Number of times that "any" syscall is requested. */
54 int any_syscall_count
;
56 /* Count of each system call. */
57 std::vector
<int> syscalls_counts
;
59 /* This counts all syscall catch requests, so we can readily determine
60 if any catching is necessary. */
61 int total_syscalls_count
;
64 static const struct inferior_key
<struct catch_syscall_inferior_data
>
65 catch_syscall_inferior_data
;
67 static struct catch_syscall_inferior_data
*
68 get_catch_syscall_inferior_data (struct inferior
*inf
)
70 struct catch_syscall_inferior_data
*inf_data
;
72 inf_data
= catch_syscall_inferior_data
.get (inf
);
74 inf_data
= catch_syscall_inferior_data
.emplace (inf
);
79 /* Implement the "insert" breakpoint_ops method for syscall
83 insert_catch_syscall (struct bp_location
*bl
)
85 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) bl
->owner
;
86 struct inferior
*inf
= current_inferior ();
87 struct catch_syscall_inferior_data
*inf_data
88 = get_catch_syscall_inferior_data (inf
);
90 ++inf_data
->total_syscalls_count
;
91 if (c
->syscalls_to_be_caught
.empty ())
92 ++inf_data
->any_syscall_count
;
95 for (int iter
: c
->syscalls_to_be_caught
)
97 if (iter
>= inf_data
->syscalls_counts
.size ())
98 inf_data
->syscalls_counts
.resize (iter
+ 1);
99 ++inf_data
->syscalls_counts
[iter
];
103 return target_set_syscall_catchpoint (inferior_ptid
.pid (),
104 inf_data
->total_syscalls_count
!= 0,
105 inf_data
->any_syscall_count
,
106 inf_data
->syscalls_counts
);
109 /* Implement the "remove" breakpoint_ops method for syscall
113 remove_catch_syscall (struct bp_location
*bl
, enum remove_bp_reason reason
)
115 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) bl
->owner
;
116 struct inferior
*inf
= current_inferior ();
117 struct catch_syscall_inferior_data
*inf_data
118 = get_catch_syscall_inferior_data (inf
);
120 --inf_data
->total_syscalls_count
;
121 if (c
->syscalls_to_be_caught
.empty ())
122 --inf_data
->any_syscall_count
;
125 for (int iter
: c
->syscalls_to_be_caught
)
127 if (iter
>= inf_data
->syscalls_counts
.size ())
128 /* Shouldn't happen. */
130 --inf_data
->syscalls_counts
[iter
];
134 return target_set_syscall_catchpoint (inferior_ptid
.pid (),
135 inf_data
->total_syscalls_count
!= 0,
136 inf_data
->any_syscall_count
,
137 inf_data
->syscalls_counts
);
140 /* Implement the "breakpoint_hit" breakpoint_ops method for syscall
144 breakpoint_hit_catch_syscall (const struct bp_location
*bl
,
145 const address_space
*aspace
, CORE_ADDR bp_addr
,
146 const struct target_waitstatus
*ws
)
148 /* We must check if we are catching specific syscalls in this
149 breakpoint. If we are, then we must guarantee that the called
150 syscall is the same syscall we are catching. */
151 int syscall_number
= 0;
152 const struct syscall_catchpoint
*c
153 = (const struct syscall_catchpoint
*) bl
->owner
;
155 if (ws
->kind
!= TARGET_WAITKIND_SYSCALL_ENTRY
156 && ws
->kind
!= TARGET_WAITKIND_SYSCALL_RETURN
)
159 syscall_number
= ws
->value
.syscall_number
;
161 /* Now, checking if the syscall is the same. */
162 if (!c
->syscalls_to_be_caught
.empty ())
164 for (int iter
: c
->syscalls_to_be_caught
)
165 if (syscall_number
== iter
)
174 /* Implement the "print_it" breakpoint_ops method for syscall
177 static enum print_stop_action
178 print_it_catch_syscall (bpstat bs
)
180 struct ui_out
*uiout
= current_uiout
;
181 struct breakpoint
*b
= bs
->breakpoint_at
;
182 /* These are needed because we want to know in which state a
183 syscall is. It can be in the TARGET_WAITKIND_SYSCALL_ENTRY
184 or TARGET_WAITKIND_SYSCALL_RETURN, and depending on it we
185 must print "called syscall" or "returned from syscall". */
186 struct target_waitstatus last
;
188 struct gdbarch
*gdbarch
= bs
->bp_location_at
->gdbarch
;
190 get_last_target_status (nullptr, nullptr, &last
);
192 get_syscall_by_number (gdbarch
, last
.value
.syscall_number
, &s
);
194 annotate_catchpoint (b
->number
);
195 maybe_print_thread_hit_breakpoint (uiout
);
197 if (b
->disposition
== disp_del
)
198 uiout
->text ("Temporary catchpoint ");
200 uiout
->text ("Catchpoint ");
201 if (uiout
->is_mi_like_p ())
203 uiout
->field_string ("reason",
204 async_reason_lookup (last
.kind
== TARGET_WAITKIND_SYSCALL_ENTRY
205 ? EXEC_ASYNC_SYSCALL_ENTRY
206 : EXEC_ASYNC_SYSCALL_RETURN
));
207 uiout
->field_string ("disp", bpdisp_text (b
->disposition
));
209 uiout
->field_signed ("bkptno", b
->number
);
211 if (last
.kind
== TARGET_WAITKIND_SYSCALL_ENTRY
)
212 uiout
->text (" (call to syscall ");
214 uiout
->text (" (returned from syscall ");
216 if (s
.name
== NULL
|| uiout
->is_mi_like_p ())
217 uiout
->field_signed ("syscall-number", last
.value
.syscall_number
);
219 uiout
->field_string ("syscall-name", s
.name
);
223 return PRINT_SRC_AND_LOC
;
226 /* Implement the "print_one" breakpoint_ops method for syscall
230 print_one_catch_syscall (struct breakpoint
*b
,
231 struct bp_location
**last_loc
)
233 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) b
;
234 struct value_print_options opts
;
235 struct ui_out
*uiout
= current_uiout
;
236 struct gdbarch
*gdbarch
= b
->loc
->gdbarch
;
238 get_user_print_options (&opts
);
239 /* Field 4, the address, is omitted (which makes the columns not
240 line up too nicely with the headers, but the effect is relatively
242 if (opts
.addressprint
)
243 uiout
->field_skip ("addr");
246 if (c
->syscalls_to_be_caught
.size () > 1)
247 uiout
->text ("syscalls \"");
249 uiout
->text ("syscall \"");
251 if (!c
->syscalls_to_be_caught
.empty ())
253 char *text
= xstrprintf ("%s", "");
255 for (int iter
: c
->syscalls_to_be_caught
)
257 char *previous_text
= text
;
259 get_syscall_by_number (gdbarch
, iter
, &s
);
262 text
= xstrprintf ("%s%s, ", text
, s
.name
);
264 text
= xstrprintf ("%s%d, ", text
, iter
);
266 /* We have to xfree previous_text because xstrprintf dynamically
267 allocates new space for text on every call. */
268 xfree (previous_text
);
270 /* Remove the last comma. */
271 text
[strlen (text
) - 2] = '\0';
272 uiout
->field_string ("what", text
);
273 /* xfree last text. */
277 uiout
->field_string ("what", "<any syscall>", metadata_style
.style ());
280 if (uiout
->is_mi_like_p ())
281 uiout
->field_string ("catch-type", "syscall");
284 /* Implement the "print_mention" breakpoint_ops method for syscall
288 print_mention_catch_syscall (struct breakpoint
*b
)
290 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) b
;
291 struct gdbarch
*gdbarch
= b
->loc
->gdbarch
;
293 if (!c
->syscalls_to_be_caught
.empty ())
295 if (c
->syscalls_to_be_caught
.size () > 1)
296 printf_filtered (_("Catchpoint %d (syscalls"), b
->number
);
298 printf_filtered (_("Catchpoint %d (syscall"), b
->number
);
300 for (int iter
: c
->syscalls_to_be_caught
)
303 get_syscall_by_number (gdbarch
, iter
, &s
);
306 printf_filtered (" '%s' [%d]", s
.name
, s
.number
);
308 printf_filtered (" %d", s
.number
);
310 printf_filtered (")");
313 printf_filtered (_("Catchpoint %d (any syscall)"),
317 /* Implement the "print_recreate" breakpoint_ops method for syscall
321 print_recreate_catch_syscall (struct breakpoint
*b
, struct ui_file
*fp
)
323 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) b
;
324 struct gdbarch
*gdbarch
= b
->loc
->gdbarch
;
326 fprintf_unfiltered (fp
, "catch syscall");
328 for (int iter
: c
->syscalls_to_be_caught
)
332 get_syscall_by_number (gdbarch
, iter
, &s
);
334 fprintf_unfiltered (fp
, " %s", s
.name
);
336 fprintf_unfiltered (fp
, " %d", s
.number
);
339 print_recreate_thread (b
, fp
);
342 /* The breakpoint_ops structure to be used in syscall catchpoints. */
344 static struct breakpoint_ops catch_syscall_breakpoint_ops
;
346 /* Returns non-zero if 'b' is a syscall catchpoint. */
349 syscall_catchpoint_p (struct breakpoint
*b
)
351 return (b
->ops
== &catch_syscall_breakpoint_ops
);
355 create_syscall_event_catchpoint (int tempflag
, std::vector
<int> &&filter
,
356 const struct breakpoint_ops
*ops
)
358 struct gdbarch
*gdbarch
= get_current_arch ();
360 std::unique_ptr
<syscall_catchpoint
> c (new syscall_catchpoint ());
361 init_catchpoint (c
.get (), gdbarch
, tempflag
, NULL
, ops
);
362 c
->syscalls_to_be_caught
= std::move (filter
);
364 install_breakpoint (0, std::move (c
), 1);
367 /* Splits the argument using space as delimiter. */
369 static std::vector
<int>
370 catch_syscall_split_args (const char *arg
)
372 std::vector
<int> result
;
373 struct gdbarch
*gdbarch
= target_gdbarch ();
377 int i
, syscall_number
;
382 /* Skip whitespace. */
383 arg
= skip_spaces (arg
);
385 for (i
= 0; i
< 127 && arg
[i
] && !isspace (arg
[i
]); ++i
)
386 cur_name
[i
] = arg
[i
];
390 /* Check if the user provided a syscall name, group, or a number. */
391 syscall_number
= (int) strtol (cur_name
, &endptr
, 0);
394 if (syscall_number
< 0)
395 error (_("Unknown syscall number '%d'."), syscall_number
);
396 get_syscall_by_number (gdbarch
, syscall_number
, &s
);
397 result
.push_back (s
.number
);
399 else if (startswith (cur_name
, "g:")
400 || startswith (cur_name
, "group:"))
402 /* We have a syscall group. Let's expand it into a syscall
403 list before inserting. */
404 const char *group_name
;
406 /* Skip over "g:" and "group:" prefix strings. */
407 group_name
= strchr (cur_name
, ':') + 1;
409 if (!get_syscalls_by_group (gdbarch
, group_name
, &result
))
410 error (_("Unknown syscall group '%s'."), group_name
);
414 /* We have a name. Let's check if it's valid and fetch a
415 list of matching numbers. */
416 if (!get_syscalls_by_name (gdbarch
, cur_name
, &result
))
417 /* Here we have to issue an error instead of a warning,
418 because GDB cannot do anything useful if there's no
419 syscall number to be caught. */
420 error (_("Unknown syscall name '%s'."), cur_name
);
427 /* Implement the "catch syscall" command. */
430 catch_syscall_command_1 (const char *arg
, int from_tty
,
431 struct cmd_list_element
*command
)
434 std::vector
<int> filter
;
436 struct gdbarch
*gdbarch
= get_current_arch ();
438 /* Checking if the feature if supported. */
439 if (gdbarch_get_syscall_number_p (gdbarch
) == 0)
440 error (_("The feature 'catch syscall' is not supported on \
441 this architecture yet."));
443 tempflag
= command
->context () == CATCH_TEMPORARY
;
445 arg
= skip_spaces (arg
);
447 /* We need to do this first "dummy" translation in order
448 to get the syscall XML file loaded or, most important,
449 to display a warning to the user if there's no XML file
450 for his/her architecture. */
451 get_syscall_by_number (gdbarch
, 0, &s
);
453 /* The allowed syntax is:
455 catch syscall <name | number> [<name | number> ... <name | number>]
457 Let's check if there's a syscall name. */
460 filter
= catch_syscall_split_args (arg
);
462 create_syscall_event_catchpoint (tempflag
, std::move (filter
),
463 &catch_syscall_breakpoint_ops
);
467 /* Returns 0 if 'bp' is NOT a syscall catchpoint,
468 non-zero otherwise. */
470 is_syscall_catchpoint_enabled (struct breakpoint
*bp
)
472 if (syscall_catchpoint_p (bp
)
473 && bp
->enable_state
!= bp_disabled
474 && bp
->enable_state
!= bp_call_disabled
)
481 catch_syscall_enabled (void)
483 struct catch_syscall_inferior_data
*inf_data
484 = get_catch_syscall_inferior_data (current_inferior ());
486 return inf_data
->total_syscalls_count
!= 0;
489 /* Helper function for catching_syscall_number. If B is a syscall
490 catchpoint for SYSCALL_NUMBER, return 1 (which will make
491 'breakpoint_find_if' return). Otherwise, return 0. */
494 catching_syscall_number_1 (struct breakpoint
*b
,
497 int syscall_number
= (int) (uintptr_t) data
;
499 if (is_syscall_catchpoint_enabled (b
))
501 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) b
;
503 if (!c
->syscalls_to_be_caught
.empty ())
505 for (int iter
: c
->syscalls_to_be_caught
)
506 if (syscall_number
== iter
)
517 catching_syscall_number (int syscall_number
)
519 struct breakpoint
*b
= breakpoint_find_if (catching_syscall_number_1
,
520 (void *) (uintptr_t) syscall_number
);
525 /* Complete syscall names. Used by "catch syscall". */
528 catch_syscall_completer (struct cmd_list_element
*cmd
,
529 completion_tracker
&tracker
,
530 const char *text
, const char *word
)
532 struct gdbarch
*gdbarch
= get_current_arch ();
533 gdb::unique_xmalloc_ptr
<const char *> group_list
;
536 /* Completion considers ':' to be a word separator, so we use this to
537 verify whether the previous word was a group prefix. If so, we
538 build the completion list using group names only. */
539 for (prefix
= word
; prefix
!= text
&& prefix
[-1] != ' '; prefix
--)
542 if (startswith (prefix
, "g:") || startswith (prefix
, "group:"))
544 /* Perform completion inside 'group:' namespace only. */
545 group_list
.reset (get_syscall_group_names (gdbarch
));
546 if (group_list
!= NULL
)
547 complete_on_enum (tracker
, group_list
.get (), word
, word
);
551 /* Complete with both, syscall names and groups. */
552 gdb::unique_xmalloc_ptr
<const char *> syscall_list
553 (get_syscall_names (gdbarch
));
554 group_list
.reset (get_syscall_group_names (gdbarch
));
556 const char **group_ptr
= group_list
.get ();
558 /* Hold on to strings while we're using them. */
559 std::vector
<std::string
> holders
;
561 /* Append "group:" prefix to syscall groups. */
562 for (int i
= 0; group_ptr
[i
] != NULL
; i
++)
563 holders
.push_back (string_printf ("group:%s", group_ptr
[i
]));
565 for (int i
= 0; group_ptr
[i
] != NULL
; i
++)
566 group_ptr
[i
] = holders
[i
].c_str ();
568 if (syscall_list
!= NULL
)
569 complete_on_enum (tracker
, syscall_list
.get (), word
, word
);
570 if (group_list
!= NULL
)
571 complete_on_enum (tracker
, group_ptr
, word
, word
);
576 clear_syscall_counts (struct inferior
*inf
)
578 struct catch_syscall_inferior_data
*inf_data
579 = get_catch_syscall_inferior_data (inf
);
581 inf_data
->total_syscalls_count
= 0;
582 inf_data
->any_syscall_count
= 0;
583 inf_data
->syscalls_counts
.clear ();
587 initialize_syscall_catchpoint_ops (void)
589 struct breakpoint_ops
*ops
;
591 initialize_breakpoint_ops ();
593 /* Syscall catchpoints. */
594 ops
= &catch_syscall_breakpoint_ops
;
595 *ops
= base_breakpoint_ops
;
596 ops
->insert_location
= insert_catch_syscall
;
597 ops
->remove_location
= remove_catch_syscall
;
598 ops
->breakpoint_hit
= breakpoint_hit_catch_syscall
;
599 ops
->print_it
= print_it_catch_syscall
;
600 ops
->print_one
= print_one_catch_syscall
;
601 ops
->print_mention
= print_mention_catch_syscall
;
602 ops
->print_recreate
= print_recreate_catch_syscall
;
605 void _initialize_break_catch_syscall ();
607 _initialize_break_catch_syscall ()
609 initialize_syscall_catchpoint_ops ();
611 gdb::observers::inferior_exit
.attach (clear_syscall_counts
,
612 "break-catch-syscall");
614 add_catch_command ("syscall", _("\
615 Catch system calls by their names, groups and/or numbers.\n\
616 Arguments say which system calls to catch. If no arguments are given,\n\
617 every system call will be caught. Arguments, if given, should be one\n\
618 or more system call names (if your system supports that), system call\n\
619 groups or system call numbers."),
620 catch_syscall_command_1
,
621 catch_syscall_completer
,