1 /* Everything about syscall catchpoints, for GDB.
3 Copyright (C) 2009-2022 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 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
.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
.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
.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 ())
256 for (int iter
: c
->syscalls_to_be_caught
)
259 get_syscall_by_number (gdbarch
, iter
, &s
);
268 text
+= std::to_string (iter
);
270 uiout
->field_string ("what", text
.c_str ());
273 uiout
->field_string ("what", "<any syscall>", metadata_style
.style ());
276 if (uiout
->is_mi_like_p ())
277 uiout
->field_string ("catch-type", "syscall");
280 /* Implement the "print_mention" breakpoint_ops method for syscall
284 print_mention_catch_syscall (struct breakpoint
*b
)
286 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) b
;
287 struct gdbarch
*gdbarch
= b
->loc
->gdbarch
;
289 if (!c
->syscalls_to_be_caught
.empty ())
291 if (c
->syscalls_to_be_caught
.size () > 1)
292 printf_filtered (_("Catchpoint %d (syscalls"), b
->number
);
294 printf_filtered (_("Catchpoint %d (syscall"), b
->number
);
296 for (int iter
: c
->syscalls_to_be_caught
)
299 get_syscall_by_number (gdbarch
, iter
, &s
);
302 printf_filtered (" '%s' [%d]", s
.name
, s
.number
);
304 printf_filtered (" %d", s
.number
);
306 printf_filtered (")");
309 printf_filtered (_("Catchpoint %d (any syscall)"),
313 /* Implement the "print_recreate" breakpoint_ops method for syscall
317 print_recreate_catch_syscall (struct breakpoint
*b
, struct ui_file
*fp
)
319 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) b
;
320 struct gdbarch
*gdbarch
= b
->loc
->gdbarch
;
322 fprintf_unfiltered (fp
, "catch syscall");
324 for (int iter
: c
->syscalls_to_be_caught
)
328 get_syscall_by_number (gdbarch
, iter
, &s
);
330 fprintf_unfiltered (fp
, " %s", s
.name
);
332 fprintf_unfiltered (fp
, " %d", s
.number
);
335 print_recreate_thread (b
, fp
);
338 /* The breakpoint_ops structure to be used in syscall catchpoints. */
340 static struct breakpoint_ops catch_syscall_breakpoint_ops
;
342 /* Returns non-zero if 'b' is a syscall catchpoint. */
345 syscall_catchpoint_p (struct breakpoint
*b
)
347 return (b
->ops
== &catch_syscall_breakpoint_ops
);
351 create_syscall_event_catchpoint (int tempflag
, std::vector
<int> &&filter
,
352 const struct breakpoint_ops
*ops
)
354 struct gdbarch
*gdbarch
= get_current_arch ();
356 std::unique_ptr
<syscall_catchpoint
> c (new syscall_catchpoint ());
357 init_catchpoint (c
.get (), gdbarch
, tempflag
, NULL
, ops
);
358 c
->syscalls_to_be_caught
= std::move (filter
);
360 install_breakpoint (0, std::move (c
), 1);
363 /* Splits the argument using space as delimiter. */
365 static std::vector
<int>
366 catch_syscall_split_args (const char *arg
)
368 std::vector
<int> result
;
369 struct gdbarch
*gdbarch
= target_gdbarch ();
373 int i
, syscall_number
;
378 /* Skip whitespace. */
379 arg
= skip_spaces (arg
);
381 for (i
= 0; i
< 127 && arg
[i
] && !isspace (arg
[i
]); ++i
)
382 cur_name
[i
] = arg
[i
];
386 /* Check if the user provided a syscall name, group, or a number. */
387 syscall_number
= (int) strtol (cur_name
, &endptr
, 0);
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
);
395 else if (startswith (cur_name
, "g:")
396 || startswith (cur_name
, "group:"))
398 /* We have a syscall group. Let's expand it into a syscall
399 list before inserting. */
400 const char *group_name
;
402 /* Skip over "g:" and "group:" prefix strings. */
403 group_name
= strchr (cur_name
, ':') + 1;
405 if (!get_syscalls_by_group (gdbarch
, group_name
, &result
))
406 error (_("Unknown syscall group '%s'."), group_name
);
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
);
423 /* Implement the "catch syscall" command. */
426 catch_syscall_command_1 (const char *arg
, int from_tty
,
427 struct cmd_list_element
*command
)
430 std::vector
<int> filter
;
432 struct gdbarch
*gdbarch
= get_current_arch ();
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."));
439 tempflag
= command
->context () == CATCH_TEMPORARY
;
441 arg
= skip_spaces (arg
);
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
);
449 /* The allowed syntax is:
451 catch syscall <name | number> [<name | number> ... <name | number>]
453 Let's check if there's a syscall name. */
456 filter
= catch_syscall_split_args (arg
);
458 create_syscall_event_catchpoint (tempflag
, std::move (filter
),
459 &catch_syscall_breakpoint_ops
);
463 /* Returns 0 if 'bp' is NOT a syscall catchpoint,
464 non-zero otherwise. */
466 is_syscall_catchpoint_enabled (struct breakpoint
*bp
)
468 if (syscall_catchpoint_p (bp
)
469 && bp
->enable_state
!= bp_disabled
470 && bp
->enable_state
!= bp_call_disabled
)
477 catch_syscall_enabled (void)
479 struct catch_syscall_inferior_data
*inf_data
480 = get_catch_syscall_inferior_data (current_inferior ());
482 return inf_data
->total_syscalls_count
!= 0;
485 /* Helper function for catching_syscall_number. return true if B is a syscall
486 catchpoint for SYSCALL_NUMBER, else false. */
489 catching_syscall_number_1 (struct breakpoint
*b
, int syscall_number
)
492 if (is_syscall_catchpoint_enabled (b
))
494 struct syscall_catchpoint
*c
= (struct syscall_catchpoint
*) b
;
496 if (!c
->syscalls_to_be_caught
.empty ())
498 for (int iter
: c
->syscalls_to_be_caught
)
499 if (syscall_number
== iter
)
510 catching_syscall_number (int syscall_number
)
512 for (breakpoint
*b
: all_breakpoints ())
513 if (catching_syscall_number_1 (b
, syscall_number
))
519 /* Complete syscall names. Used by "catch syscall". */
522 catch_syscall_completer (struct cmd_list_element
*cmd
,
523 completion_tracker
&tracker
,
524 const char *text
, const char *word
)
526 struct gdbarch
*gdbarch
= get_current_arch ();
527 gdb::unique_xmalloc_ptr
<const char *> group_list
;
530 /* Completion considers ':' to be a word separator, so we use this to
531 verify whether the previous word was a group prefix. If so, we
532 build the completion list using group names only. */
533 for (prefix
= word
; prefix
!= text
&& prefix
[-1] != ' '; prefix
--)
536 if (startswith (prefix
, "g:") || startswith (prefix
, "group:"))
538 /* Perform completion inside 'group:' namespace only. */
539 group_list
.reset (get_syscall_group_names (gdbarch
));
540 if (group_list
!= NULL
)
541 complete_on_enum (tracker
, group_list
.get (), word
, word
);
545 /* Complete with both, syscall names and groups. */
546 gdb::unique_xmalloc_ptr
<const char *> syscall_list
547 (get_syscall_names (gdbarch
));
548 group_list
.reset (get_syscall_group_names (gdbarch
));
550 const char **group_ptr
= group_list
.get ();
552 /* Hold on to strings while we're using them. */
553 std::vector
<std::string
> holders
;
555 /* Append "group:" prefix to syscall groups. */
556 for (int i
= 0; group_ptr
[i
] != NULL
; i
++)
557 holders
.push_back (string_printf ("group:%s", group_ptr
[i
]));
559 for (int i
= 0; group_ptr
[i
] != NULL
; i
++)
560 group_ptr
[i
] = holders
[i
].c_str ();
562 if (syscall_list
!= NULL
)
563 complete_on_enum (tracker
, syscall_list
.get (), word
, word
);
564 if (group_list
!= NULL
)
565 complete_on_enum (tracker
, group_ptr
, word
, word
);
570 clear_syscall_counts (struct inferior
*inf
)
572 struct catch_syscall_inferior_data
*inf_data
573 = get_catch_syscall_inferior_data (inf
);
575 inf_data
->total_syscalls_count
= 0;
576 inf_data
->any_syscall_count
= 0;
577 inf_data
->syscalls_counts
.clear ();
581 initialize_syscall_catchpoint_ops (void)
583 struct breakpoint_ops
*ops
;
585 initialize_breakpoint_ops ();
587 /* Syscall catchpoints. */
588 ops
= &catch_syscall_breakpoint_ops
;
589 *ops
= base_breakpoint_ops
;
590 ops
->insert_location
= insert_catch_syscall
;
591 ops
->remove_location
= remove_catch_syscall
;
592 ops
->breakpoint_hit
= breakpoint_hit_catch_syscall
;
593 ops
->print_it
= print_it_catch_syscall
;
594 ops
->print_one
= print_one_catch_syscall
;
595 ops
->print_mention
= print_mention_catch_syscall
;
596 ops
->print_recreate
= print_recreate_catch_syscall
;
599 void _initialize_break_catch_syscall ();
601 _initialize_break_catch_syscall ()
603 initialize_syscall_catchpoint_ops ();
605 gdb::observers::inferior_exit
.attach (clear_syscall_counts
,
606 "break-catch-syscall");
608 add_catch_command ("syscall", _("\
609 Catch system calls by their names, groups and/or numbers.\n\
610 Arguments say which system calls to catch. If no arguments are given,\n\
611 every system call will be caught. Arguments, if given, should be one\n\
612 or more system call names (if your system supports that), system call\n\
613 groups or system call numbers."),
614 catch_syscall_command_1
,
615 catch_syscall_completer
,