From 0c5571793a8e3dbd0f99b8e4759bd201f5fe75b7 Mon Sep 17 00:00:00 2001 From: Sergio Durigan Junior Date: Mon, 7 Oct 2013 05:34:11 +0000 Subject: [PATCH] This patch adds a new convenience variable called "$_exitsignal", which will hold the signal number when the inferior terminates due to the uncaught signal. I've made modifications on infrun.c:handle_inferior_event such that $_exitcode gets cleared when the inferior signalled, and vice-versa. This assumption was made because the variables are mutually exclusive, i.e., when the inferior terminates because of an uncaught signal it is not possible for it to return. I have also made modifications such that when a corefile is loaded, $_exitsignal gets set to the uncaught signal that "killed" the inferior, and $_exitcode is cleared. The patch also adds a NEWS entry, documentation bits, and a testcase. The documentation entry explains how to use $_exitsignal and $_exitcode in a GDB script, by making use of the new $_isvoid convenience function. gdb/ 2013-10-06 Sergio Durigan Junior * NEWS: Mention new convenience variable $_exitsignal. * corelow.c (core_open): Reset exit convenience variables. Set $_exitsignal to the uncaught signal which generated the corefile. * infrun.c (handle_inferior_event): Reset exit convenience variables. Set $_exitsignal for TARGET_WAITKIND_SIGNALLED. (clear_exit_convenience_vars): New function. * inferior.h (clear_exit_convenience_vars): New prototype. gdb/testsuite/ 2013-10-06 Sergio Durigan Junior * gdb.base/corefile.exp: Test whether $_exitsignal is set and $_exitcode is void when opening a corefile. * gdb.base/exitsignal.exp: New file. * gdb.base/segv.c: Likewise. * gdb.base/normal.c: Likewise. gdb/doc/ 2013-10-06 Sergio Durigan Junior * gdb.texinfo (Convenience Variables): Document $_exitsignal. Update entry for $_exitcode. --- gdb/ChangeLog | 10 +++ gdb/NEWS | 4 + gdb/corelow.c | 8 ++ gdb/doc/ChangeLog | 5 ++ gdb/doc/gdb.texinfo | 60 ++++++++++++++- gdb/inferior.h | 6 ++ gdb/infrun.c | 41 +++++++++- gdb/testsuite/ChangeLog | 8 ++ gdb/testsuite/gdb.base/corefile.exp | 10 +++ gdb/testsuite/gdb.base/exitsignal.exp | 106 ++++++++++++++++++++++++++ gdb/testsuite/gdb.base/normal.c | 24 ++++++ gdb/testsuite/gdb.base/segv.c | 29 +++++++ 12 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 gdb/testsuite/gdb.base/exitsignal.exp create mode 100644 gdb/testsuite/gdb.base/normal.c create mode 100644 gdb/testsuite/gdb.base/segv.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 3d8d9364bdf..a433b466939 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2013-10-06 Sergio Durigan Junior + + * NEWS: Mention new convenience variable $_exitsignal. + * corelow.c (core_open): Reset exit convenience variables. Set + $_exitsignal to the uncaught signal which generated the corefile. + * infrun.c (handle_inferior_event): Reset exit convenience + variables. Set $_exitsignal for TARGET_WAITKIND_SIGNALLED. + (clear_exit_convenience_vars): New function. + * inferior.h (clear_exit_convenience_vars): New prototype. + 2013-10-06 Yao Qi * varobj.h: Add comments to enum varobj_languages. diff --git a/gdb/NEWS b/gdb/NEWS index 737fa7ee111..8114fb13c88 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -122,6 +122,10 @@ show range-stepping * The exception-related catchpoints, like "catch throw", now accept a regular expression which can be used to filter exceptions by type. +* The new convenience variable $_exitsignal is automatically set to + the terminating signal number when the program being debugged dies + due to an uncaught signal. + * MI changes ** The -trace-save MI command can optionally save trace buffer in Common diff --git a/gdb/corelow.c b/gdb/corelow.c index 7a5aaabf7c6..d1e7f6ae7df 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -429,6 +429,9 @@ core_open (char *filename, int from_tty) if (p) printf_filtered (_("Core was generated by `%s'.\n"), p); + /* Clearing any previous state of convenience variables. */ + clear_exit_convenience_vars (); + siggy = bfd_core_file_failing_signal (core_bfd); if (siggy > 0) { @@ -446,6 +449,11 @@ core_open (char *filename, int from_tty) printf_filtered (_("Program terminated with signal %s, %s.\n"), gdb_signal_to_name (sig), gdb_signal_to_string (sig)); + + /* Set the value of the internal variable $_exitsignal, + which holds the signal uncaught by the inferior. */ + set_internalvar_integer (lookup_internalvar ("_exitsignal"), + siggy); } /* Fetch all registers from core file. */ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 04e291bce35..89254a99394 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2013-10-06 Sergio Durigan Junior + + * gdb.texinfo (Convenience Variables): Document $_exitsignal. + Update entry for $_exitcode. + 2013-10-04 Joel Brobecker * gdb.texinfo (GDB/MI Program Execution): Document "-exec-run"'s diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index a68556b0872..7ec91d85925 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -9751,8 +9751,64 @@ to match the format in which the data was printed. @item $_exitcode @vindex $_exitcode@r{, convenience variable} -The variable @code{$_exitcode} is automatically set to the exit code when -the program being debugged terminates. +When the program being debugged terminates normally, @value{GDBN} +automatically sets this variable to the exit code of the program, and +resets @code{$_exitsignal} to @code{void}. + +@item $_exitsignal +@vindex $_exitsignal@r{, convenience variable} +When the program being debugged dies due to an uncaught signal, +@value{GDBN} automatically sets this variable to that signal's number, +and resets @code{$_exitcode} to @code{void}. + +To distinguish between whether the program being debugged has exited +(i.e., @code{$_exitcode} is not @code{void}) or signalled (i.e., +@code{$_exitsignal} is not @code{void}), the convenience function +@code{$_isvoid} can be used (@pxref{Convenience Funs,, Convenience +Functions}). For example, considering the following source code: + +@smallexample +#include + +int +main (int argc, char *argv[]) +@{ + raise (SIGALRM); + return 0; +@} +@end smallexample + +A valid way of telling whether the program being debugged has exited +or signalled would be: + +@smallexample +(@value{GDBP}) define has_exited_or_signalled +Type commands for definition of ``has_exited_or_signalled''. +End with a line saying just ``end''. +>if $_isvoid ($_exitsignal) + >echo The program has exited\n + >else + >echo The program has signalled\n + >end +>end +(@value{GDBP}) run +Starting program: + +Program terminated with signal SIGALRM, Alarm clock. +The program no longer exists. +(@value{GDBP}) has_exited_or_signalled +The program has signalled +@end smallexample + +As can be seen, @value{GDBN} correctly informs that the program being +debugged has signalled, since it calls @code{raise} and raises a +@code{SIGALRM} signal. If the program being debugged had not called +@code{raise}, then @value{GDBN} would report a normal exit: + +@smallexample +(@value{GDBP}) has_exited_or_signalled +The program has exited +@end smallexample @item $_exception The variable @code{$_exception} is set to the exception object being diff --git a/gdb/inferior.h b/gdb/inferior.h index 922a0f2e0cb..fff072bbb70 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -224,6 +224,12 @@ extern void follow_inferior_reset_breakpoints (void); void set_step_info (struct frame_info *frame, struct symtab_and_line sal); +/* Clear the convenience variables associated with the exit of the + inferior. Currently, those variables are $_exitcode and + $_exitsignal. */ + +extern void clear_exit_convenience_vars (void); + /* From infcmd.c */ extern void post_create_inferior (struct target_ops *, int); diff --git a/gdb/infrun.c b/gdb/infrun.c index db0ad8d2c65..39c9cf3bb1c 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3426,6 +3426,9 @@ handle_inferior_event (struct execution_control_state *ecs) handle_vfork_child_exec_or_exit (0); target_terminal_ours (); /* Must do this before mourn anyway. */ + /* Clearing any previous state of convenience variables. */ + clear_exit_convenience_vars (); + if (ecs->ws.kind == TARGET_WAITKIND_EXITED) { /* Record the exit code in the convenience variable $_exitcode, so @@ -3440,7 +3443,34 @@ handle_inferior_event (struct execution_control_state *ecs) print_exited_reason (ecs->ws.value.integer); } else - print_signal_exited_reason (ecs->ws.value.sig); + { + struct regcache *regcache = get_thread_regcache (ecs->ptid); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + + if (gdbarch_gdb_signal_to_target_p (gdbarch)) + { + /* Set the value of the internal variable $_exitsignal, + which holds the signal uncaught by the inferior. */ + set_internalvar_integer (lookup_internalvar ("_exitsignal"), + gdbarch_gdb_signal_to_target (gdbarch, + ecs->ws.value.sig)); + } + else + { + /* We don't have access to the target's method used for + converting between signal numbers (GDB's internal + representation <-> target's representation). + Therefore, we cannot do a good job at displaying this + information to the user. It's better to just warn + her about it (if infrun debugging is enabled), and + give up. */ + if (debug_infrun) + fprintf_filtered (gdb_stdlog, _("\ +Cannot fill $_exitsignal with the correct signal number.\n")); + } + + print_signal_exited_reason (ecs->ws.value.sig); + } gdb_flush (gdb_stdout); target_mourn_inferior (); @@ -7061,6 +7091,15 @@ save_inferior_ptid (void) *saved_ptid_ptr = inferior_ptid; return make_cleanup (restore_inferior_ptid, saved_ptid_ptr); } + +/* See inferior.h. */ + +void +clear_exit_convenience_vars (void) +{ + clear_internalvar (lookup_internalvar ("_exitsignal")); + clear_internalvar (lookup_internalvar ("_exitcode")); +} /* User interface for reverse debugging: diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 4f077770653..3a5a4e0ce2f 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2013-10-06 Sergio Durigan Junior + + * gdb.base/corefile.exp: Test whether $_exitsignal is set and + $_exitcode is void when opening a corefile. + * gdb.base/exitsignal.exp: New file. + * gdb.base/segv.c: Likewise. + * gdb.base/normal.c: Likewise. + 2013-10-04 Joel Brobecker * gdb.mi/mi-start.c, gdb.mi/mi-start.exp: New files. diff --git a/gdb/testsuite/gdb.base/corefile.exp b/gdb/testsuite/gdb.base/corefile.exp index 04ae9691df6..72c7a4a1c4c 100644 --- a/gdb/testsuite/gdb.base/corefile.exp +++ b/gdb/testsuite/gdb.base/corefile.exp @@ -142,6 +142,16 @@ gdb_test "print coremaker_ro" "\\\$$decimal = 201" gdb_test "print func2::coremaker_local" "\\\$$decimal = \\{0, 1, 2, 3, 4\\}" +# Test the presence and the correct values of $_exitsignal and +# $_exitcode variables. The corefile is generated with a SIGABRT, +# which is "6" in the Linux kernel. + +gdb_test "print \$_exitsignal" " = 6" \ + "\$_exitsignal prints SIGABRT (6)" + +gdb_test "print \$_exitcode" " = void" \ + "\$_exitcode is void" + # Somehow we better test the ability to read the registers out of the core # file correctly. I don't think the other tests do this. diff --git a/gdb/testsuite/gdb.base/exitsignal.exp b/gdb/testsuite/gdb.base/exitsignal.exp new file mode 100644 index 00000000000..868781201c7 --- /dev/null +++ b/gdb/testsuite/gdb.base/exitsignal.exp @@ -0,0 +1,106 @@ +# Copyright 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This test checks both $_exitcode and $_exitsignal variables. The +# purpose of this checking is to ensure that the variables are +# mutually exclusive, i.e., that when $_exitsignal is set, $_exitcode +# is not, and vice-versa. This mutual exclusion happens because if an +# inferior exited (either successfuly or not), it certainly was not +# killed by a signal. However, if it was killed by an uncaught +# signal, then there is no way for it to have exited. + +if { [target_info exists gdb,nosignals] } { + verbose "Skipping exitsignal.exp because of nosignals." + continue +} + +standard_testfile segv.c + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +# Run to main +if { ![runto_main] } { + return -1 +} + +# Print $_exitsignal. It should be void now, because nothing +# happened. +gdb_test "print \$_exitsignal" " = void" \ + "\$_exitsignal is void before running" + +# Just to guarantee, making sure that $_exitcode is also void. +gdb_test "print \$_exitcode" " = void" \ + "\$_exitcode is void before running" + +# Trigger SIGSEGV. +gdb_test "continue" "Program received signal SIGSEGV.*" "trigger SIGSEGV" + +# Continue until the end. +gdb_test "continue" "Program terminated with signal SIGSEGV.*" \ + "program terminated with SIGSEGV" + +# Now, print $_exitsignal again. It should be 11 (SIGSEGV). +gdb_test "print \$_exitsignal" " = 11" \ + "\$_exitsignal is 11 (SIGSEGV) after SIGSEGV." + +# And $_exitcode should still be void, since the inferior died because +# of a signal, and did not return. +gdb_test "print \$_exitcode" " = void" \ + "\$_exitcode is still void after SIGSEGV" + +# Re-run to main, i.e., restart the executable. +rerun_to_main + +# Print the $_exitsignal again. Even in this normal scenario, it +# should still contain the signal triggered in the other run. +gdb_test "print \$_exitsignal" " = 11" \ + "\$_exitsignal is 11 (SIGSEGV) after restarting the inferior" + +# And, again, print $_exitcode. +gdb_test "print \$_exitcode" " = void" \ + "\$_exitcode is still void after restarting the inferior" + +# Now we test the behaviour of $_exit{code,signal} during a normal +# inferior execution. +standard_testfile normal.c + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +# Checking $_exitsignal and $_exitcode, both should be void before the +# inferior is executed. +gdb_test "print \$_exitsignal" " = void" \ + "\$_exitsignal is void before normal inferior is executed" +gdb_test "print \$_exitcode" " = void" \ + "\$_exitcode is void before normal inferior is executed" + +# Run the inferior until the end. +if { ![runto_main] } { + return -1 +} + +gdb_continue_to_end + +# Checking $_exitcode. It should be 0. +gdb_test "print \$_exitcode" " = 0" \ + "\$_exitcode is zero after normal inferior is executed" + +# Checking $_exitsignal. It should still be void, since the inferior +# has not received any signal. +gdb_test "print \$_exitsignal" " = void" \ + "\$_exitsignal is still void after normal inferior is executed" diff --git a/gdb/testsuite/gdb.base/normal.c b/gdb/testsuite/gdb.base/normal.c new file mode 100644 index 00000000000..4aa7c45555c --- /dev/null +++ b/gdb/testsuite/gdb.base/normal.c @@ -0,0 +1,24 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This test is just a normal return 0. */ + +int +main (int argc, char *argv[]) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.base/segv.c b/gdb/testsuite/gdb.base/segv.c new file mode 100644 index 00000000000..8991f4d1f22 --- /dev/null +++ b/gdb/testsuite/gdb.base/segv.c @@ -0,0 +1,29 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This test can be used just to generate a SIGSEGV. */ + +#include + +int +main (int argc, char *argv[]) +{ + /* Generating a SIGSEGV. */ + raise (SIGSEGV); + + return 0; +} -- 2.30.2