* RFC: handle new NT_SIGINFO note in gdb @ 2012-10-30 17:18 Tom Tromey 2012-10-30 18:31 ` Pedro Alves 2012-11-01 1:12 ` Alan Modra 0 siblings, 2 replies; 9+ messages in thread From: Tom Tromey @ 2012-10-30 17:18 UTC (permalink / raw) To: gdb-patches; +Cc: Binutils Development This patch adds support for the new Linux NT_SIGINFO core note to gdb. First, I've CCd the binutils list because this includes a BFD change. In particular, in order to access the note data, I changed BFD to make a pseudosection for this new note's data. This seemed ok based on surrounding code; but if some other method is preferred, just let me know and I will implement that instead. On the gdb side the support is relatively straightforward. I chose to implement the support directly in corelow, since adding a new hook just for this functionality seemed like overkill. A new test case is included. It uses "gcore" and then re-reads the siginfo data from the generated core. Built and regtested on x86-64 Fedora 16. On the binutils side, I remembered this time to run the test suite in binutils, gas, and ld. Tom 2012-10-30 Tom Tromey <tromey@redhat.com> * elf.c (elfcore_grok_note) <NT_SIGINFO>: New case; make pseudosection. 2012-10-30 Tom Tromey <tromey@redhat.com> * linux-tdep.c (linux_make_siginfo_note): New function. (linux_make_corefile_notes): Use it. * corelow.c (get_core_siginfo): New function. (core_xfer_partial) <TARGET_OBJECT_SIGNAL_INFO>: New case. 2012-10-30 Tom Tromey <tromey@redhat.com> * gdb.base/siginfo-obj.exp: Create core file. Test siginfo from core files, if possible. --- bfd/ChangeLog | 5 ++++ bfd/elf.c | 4 +++ gdb/ChangeLog | 7 ++++++ gdb/corelow.c | 25 +++++++++++++++++++++ gdb/linux-tdep.c | 38 ++++++++++++++++++++++++++++++++ gdb/testsuite/ChangeLog | 5 ++++ gdb/testsuite/gdb.base/siginfo-obj.exp | 17 ++++++++++++++ 7 files changed, 101 insertions(+), 0 deletions(-) diff --git a/bfd/elf.c b/bfd/elf.c index cab1cc7..4465f48 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -8604,6 +8604,10 @@ elfcore_grok_note (bfd *abfd, Elf_Internal_Note *note) return TRUE; } + + case NT_SIGINFO: + return elfcore_make_note_pseudosection (abfd, ".note.linuxcore.siginfo", + note); } } diff --git a/gdb/corelow.c b/gdb/corelow.c index 340b149..d2f87cb 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -662,6 +662,26 @@ add_to_spuid_list (bfd *abfd, asection *asect, void *list_p) list->pos += 4; } +/* Read siginfo data from the core, if possible. Returns -1 on + failure. Otherwise, returns the number of bytes read. ABFD is the + core file's BFD; READBUF, OFFSET, and LEN are all as specified by + the to_xfer_partial interface. */ + +static LONGEST +get_core_siginfo (bfd *abfd, gdb_byte *readbuf, ULONGEST offset, LONGEST len) +{ + asection *section; + + section = bfd_get_section_by_name (abfd, ".note.linuxcore.siginfo"); + if (section == NULL) + return -1; + + if (!bfd_get_section_contents (abfd, section, readbuf, offset, len)) + return -1; + + return len; +} + static LONGEST core_xfer_partial (struct target_ops *ops, enum target_object object, const char *annex, gdb_byte *readbuf, @@ -800,6 +820,11 @@ core_xfer_partial (struct target_ops *ops, enum target_object object, } return -1; + case TARGET_OBJECT_SIGNAL_INFO: + if (readbuf) + return get_core_siginfo (core_bfd, readbuf, offset, len); + return -1; + default: if (ops->beneath != NULL) return ops->beneath->to_xfer_partial (ops->beneath, object, diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 65f5f97..f6408b6 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -712,6 +712,41 @@ linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size) return note_data; } +/* Write the core note for siginfo data, if it exists. + GDBARCH is the architecture to use. + OBFD is the output BFD. + NOTE_DATA is the current note data. + NOTE_SIZE is an in-out parameter holding the current note size. + This returns the new note data pointer. */ + +static char * +linux_make_siginfo_note (struct gdbarch *gdbarch, bfd *obfd, + char *note_data, int *note_size) +{ + struct type *siginfo_type; + gdb_byte *buf; + LONGEST bytes_read; + struct cleanup *cleanups; + + if (!gdbarch_get_siginfo_type_p (gdbarch)) + return note_data; + + siginfo_type = gdbarch_get_siginfo_type (gdbarch); + + buf = xmalloc (TYPE_LENGTH (siginfo_type)); + cleanups = make_cleanup (xfree, buf); + + bytes_read = target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL, + buf, 0, TYPE_LENGTH (siginfo_type)); + if (bytes_read == TYPE_LENGTH (siginfo_type)) + note_data = elfcore_write_note (obfd, note_data, note_size, + "CORE", NT_SIGINFO, + buf, bytes_read); + + do_cleanups (cleanups); + return note_data; +} + /* Records the thread's register state for the corefile note section. */ @@ -867,6 +902,9 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size, if (!note_data) return NULL; + /* Siginfo. */ + note_data = linux_make_siginfo_note (gdbarch, obfd, note_data, note_size); + make_cleanup (xfree, note_data); return note_data; } diff --git a/gdb/testsuite/gdb.base/siginfo-obj.exp b/gdb/testsuite/gdb.base/siginfo-obj.exp index 9ca649d..75e76ff 100644 --- a/gdb/testsuite/gdb.base/siginfo-obj.exp +++ b/gdb/testsuite/gdb.base/siginfo-obj.exp @@ -56,6 +56,10 @@ if { ![runto_main] } then { # Run to the signal. gdb_test "continue" ".*Program received signal SIGSEGV.*" "continue to signal" +# Try to generate a core file, for a later test. +set gcorefile [standard_output_file $testfile.gcore] +set gcore_created [gdb_gcore_cmd $gcorefile "save a core file"] + set ssi_addr "" set test "Extract si_addr" gdb_test_multiple "p \$_siginfo" "$test" { @@ -123,3 +127,16 @@ gdb_test "p ssi_addr" " = \\(void \\*\\) 0x666" gdb_test "p ssi_errno" " = 666" gdb_test "p ssi_code" " = 999" gdb_test "p ssi_signo" " = 11" + +# Test siginfo preservation in core files. +if {$gcore_created} { + clean_restart $binfile + + gdb_test "core $gcorefile" "Core was generated by.*" + + gdb_test "p \$_siginfo.si_signo" " = $ssi_signo" + gdb_test "p \$_siginfo.si_errno" " = $ssi_errno" + gdb_test "p \$_siginfo.si_code" " = $ssi_code" + gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ + " = \\(void \\*\\) $ssi_addr" +} -- 1.7.7.6 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: RFC: handle new NT_SIGINFO note in gdb 2012-10-30 17:18 RFC: handle new NT_SIGINFO note in gdb Tom Tromey @ 2012-10-30 18:31 ` Pedro Alves 2012-11-01 1:12 ` Alan Modra 1 sibling, 0 replies; 9+ messages in thread From: Pedro Alves @ 2012-10-30 18:31 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches, Binutils Development On 10/30/2012 05:18 PM, Tom Tromey wrote: > This patch adds support for the new Linux NT_SIGINFO core note to gdb. > > First, I've CCd the binutils list because this includes a BFD change. > In particular, in order to access the note data, I changed BFD to make a > pseudosection for this new note's data. This seemed ok based on > surrounding code; but if some other method is preferred, just let me > know and I will implement that instead. > > On the gdb side the support is relatively straightforward. > All looks great to me. Thanks Tom. > I chose to implement the support directly in corelow, since adding a new > hook just for this functionality seemed like overkill. > > A new test case is included. It uses "gcore" and then re-reads the > siginfo data from the generated core. > > Built and regtested on x86-64 Fedora 16. > On the binutils side, I remembered this time to run the test suite in > binutils, gas, and ld. -- Pedro Alves ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: RFC: handle new NT_SIGINFO note in gdb 2012-10-30 17:18 RFC: handle new NT_SIGINFO note in gdb Tom Tromey 2012-10-30 18:31 ` Pedro Alves @ 2012-11-01 1:12 ` Alan Modra 1 sibling, 0 replies; 9+ messages in thread From: Alan Modra @ 2012-11-01 1:12 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches, Binutils Development On Tue, Oct 30, 2012 at 11:18:07AM -0600, Tom Tromey wrote: > * elf.c (elfcore_grok_note) <NT_SIGINFO>: New case; make > pseudosection. OK. -- Alan Modra Australia Development Lab, IBM ^ permalink raw reply [flat|nested] 9+ messages in thread
[parent not found: <878vanyj3k.fsf__16012.8015945249$1351617533$gmane$org@fleche.redhat.com>]
* Re: RFC: handle new NT_SIGINFO note in gdb [not found] <878vanyj3k.fsf__16012.8015945249$1351617533$gmane$org@fleche.redhat.com> @ 2012-11-01 19:01 ` Tom Tromey 2012-11-02 18:21 ` Tom Tromey 0 siblings, 1 reply; 9+ messages in thread From: Tom Tromey @ 2012-11-01 19:01 UTC (permalink / raw) To: gdb-patches >>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes: Tom> This patch adds support for the new Linux NT_SIGINFO core note to gdb. Pedro pointed out that this patch doesn't ensure that $_siginfo is attached to the correct thread. I have a fix for this but I am looking into writing a multi-threaded test case before resubmitting. Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: RFC: handle new NT_SIGINFO note in gdb 2012-11-01 19:01 ` Tom Tromey @ 2012-11-02 18:21 ` Tom Tromey 2012-11-02 19:02 ` Pedro Alves 0 siblings, 1 reply; 9+ messages in thread From: Tom Tromey @ 2012-11-02 18:21 UTC (permalink / raw) To: gdb-patches >>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes: Tom> Pedro pointed out that this patch doesn't ensure that $_siginfo is Tom> attached to the correct thread. Tom> I have a fix for this but I am looking into writing a multi-threaded Tom> test case before resubmitting. Here is an updated patch. It differs from the original patch in that it associates each NT_SIGINFO note with the relevant thread. There is a new multi-threaded test case included as well. Note that when a core is generated by gdb, each thread has a $_siginfo -- threads stopped by gdb will show SIGSTOP. Built and regtested on x86-64 Fedora 16. I also tried this manually against a kernel made by a relatively recent Linux kernel. Tom 2012-11-02 Tom Tromey <tromey@redhat.com> * elf.c (elfcore_grok_note) <NT_SIGINFO>: New case; make pseudosection. 2012-11-02 Tom Tromey <tromey@redhat.com> * linux-tdep.c (linux_make_siginfo_note): New function. (linux_make_corefile_notes): Use it. * corelow.c (get_core_siginfo): New function. (core_xfer_partial) <TARGET_OBJECT_SIGNAL_INFO>: New case. 2012-11-02 Tom Tromey <tromey@redhat.com> * gdb.base/siginfo-obj.exp: Create core file. Test siginfo from core files, if possible. * gdb.base/siginfo-thread.c: New file * gdb.base/siginfo-thread.exp: New file --- bfd/ChangeLog | 5 + bfd/elf.c | 4 + gdb/ChangeLog | 7 ++ gdb/corelow.c | 35 ++++++++ gdb/linux-tdep.c | 56 +++++++++++++ gdb/testsuite/ChangeLog | 7 ++ gdb/testsuite/gdb.base/siginfo-obj.exp | 22 +++++ gdb/testsuite/gdb.base/siginfo-thread.c | 83 +++++++++++++++++++ gdb/testsuite/gdb.base/siginfo-thread.exp | 122 +++++++++++++++++++++++++++++ 9 files changed, 341 insertions(+), 0 deletions(-) create mode 100644 gdb/testsuite/gdb.base/siginfo-thread.c create mode 100644 gdb/testsuite/gdb.base/siginfo-thread.exp diff --git a/bfd/elf.c b/bfd/elf.c index cab1cc7..4465f48 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -8604,6 +8604,10 @@ elfcore_grok_note (bfd *abfd, Elf_Internal_Note *note) return TRUE; } + + case NT_SIGINFO: + return elfcore_make_note_pseudosection (abfd, ".note.linuxcore.siginfo", + note); } } diff --git a/gdb/corelow.c b/gdb/corelow.c index 2080068..99611ba 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -656,6 +656,36 @@ add_to_spuid_list (bfd *abfd, asection *asect, void *list_p) list->pos += 4; } +/* Read siginfo data from the core, if possible. Returns -1 on + failure. Otherwise, returns the number of bytes read. ABFD is the + core file's BFD; READBUF, OFFSET, and LEN are all as specified by + the to_xfer_partial interface. */ + +static LONGEST +get_core_siginfo (bfd *abfd, gdb_byte *readbuf, ULONGEST offset, LONGEST len) +{ + asection *section; + long pid; + char *section_name; + const char *name = ".note.linuxcore.siginfo"; + + if (ptid_get_lwp (inferior_ptid)) + section_name = xstrprintf ("%s/%ld", name, + ptid_get_lwp (inferior_ptid)); + else + section_name = xstrdup (name); + + section = bfd_get_section_by_name (abfd, section_name); + xfree (section_name); + if (section == NULL) + return -1; + + if (!bfd_get_section_contents (abfd, section, readbuf, offset, len)) + return -1; + + return len; +} + static LONGEST core_xfer_partial (struct target_ops *ops, enum target_object object, const char *annex, gdb_byte *readbuf, @@ -794,6 +824,11 @@ core_xfer_partial (struct target_ops *ops, enum target_object object, } return -1; + case TARGET_OBJECT_SIGNAL_INFO: + if (readbuf) + return get_core_siginfo (core_bfd, readbuf, offset, len); + return -1; + default: if (ops->beneath != NULL) return ops->beneath->to_xfer_partial (ops->beneath, object, diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 65f5f97..f02d510 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -765,6 +765,44 @@ linux_collect_thread_registers (const struct regcache *regcache, return note_data; } +/* Fetch the siginfo data for the current thread, if it exists. If + there is no data, or we could not read it, return NULL. Otherwise, + return a newly malloc'd buffer holding the data and fill in *SIZE + with the size of the data. The caller is responsible for freeing + the data. */ + +static gdb_byte * +linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size) +{ + struct type *siginfo_type; + gdb_byte *buf; + LONGEST bytes_read; + struct cleanup *cleanups; + + if (!gdbarch_get_siginfo_type_p (gdbarch)) + return NULL; + + siginfo_type = gdbarch_get_siginfo_type (gdbarch); + + buf = xmalloc (TYPE_LENGTH (siginfo_type)); + cleanups = make_cleanup (xfree, buf); + + bytes_read = target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL, + buf, 0, TYPE_LENGTH (siginfo_type)); + if (bytes_read == TYPE_LENGTH (siginfo_type)) + { + discard_cleanups (cleanups); + *size = bytes_read; + } + else + { + do_cleanups (cleanups); + buf = NULL; + } + + return buf; +} + struct linux_corefile_thread_data { struct gdbarch *gdbarch; @@ -789,17 +827,35 @@ linux_corefile_thread_callback (struct thread_info *info, void *data) { struct cleanup *old_chain; struct regcache *regcache; + gdb_byte *siginfo_data; + LONGEST siginfo_size; + regcache = get_thread_arch_regcache (info->ptid, args->gdbarch); old_chain = save_inferior_ptid (); inferior_ptid = info->ptid; target_fetch_registers (regcache, -1); + siginfo_data = linux_get_siginfo_data (args->gdbarch, &siginfo_size); do_cleanups (old_chain); + old_chain = make_cleanup (xfree, siginfo_data); + args->note_data = args->collect (regcache, info->ptid, args->obfd, args->note_data, args->note_size, args->stop_signal); args->num_notes++; + + if (siginfo_data != NULL) + { + args->note_data = elfcore_write_note (args->obfd, + args->note_data, + args->note_size, + "CORE", NT_SIGINFO, + siginfo_data, siginfo_size); + args->num_notes++; + } + + do_cleanups (old_chain); } return !args->note_data; diff --git a/gdb/testsuite/gdb.base/siginfo-obj.exp b/gdb/testsuite/gdb.base/siginfo-obj.exp index 9ca649d..c6ab6ff 100644 --- a/gdb/testsuite/gdb.base/siginfo-obj.exp +++ b/gdb/testsuite/gdb.base/siginfo-obj.exp @@ -56,6 +56,10 @@ if { ![runto_main] } then { # Run to the signal. gdb_test "continue" ".*Program received signal SIGSEGV.*" "continue to signal" +# Try to generate a core file, for a later test. +set gcorefile [standard_output_file $testfile.gcore] +set gcore_created [gdb_gcore_cmd $gcorefile "save a core file"] + set ssi_addr "" set test "Extract si_addr" gdb_test_multiple "p \$_siginfo" "$test" { @@ -123,3 +127,21 @@ gdb_test "p ssi_addr" " = \\(void \\*\\) 0x666" gdb_test "p ssi_errno" " = 666" gdb_test "p ssi_code" " = 999" gdb_test "p ssi_signo" " = 11" + +# Test siginfo preservation in core files. +if {$gcore_created} { + clean_restart $binfile + + gdb_test "core $gcorefile" "Core was generated by.*" \ + "core [file tail $gcorefile]" + + gdb_test "p \$_siginfo.si_signo" " = $ssi_signo" \ + "p \$_siginfo.si_signo from core file" + gdb_test "p \$_siginfo.si_errno" " = $ssi_errno" \ + "p \$_siginfo.si_errno from core file" + gdb_test "p \$_siginfo.si_code" " = $ssi_code" \ + "p \$_siginfo.si_code from core file" + gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ + " = \\(void \\*\\) $ssi_addr" \ + "p \$_siginfo._sifields._sigfault.si_addr from core file" +} diff --git a/gdb/testsuite/gdb.base/siginfo-thread.c b/gdb/testsuite/gdb.base/siginfo-thread.c new file mode 100644 index 0000000..a47bcb3 --- /dev/null +++ b/gdb/testsuite/gdb.base/siginfo-thread.c @@ -0,0 +1,83 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2004, 2007-2008, 2010-2012 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 <http://www.gnu.org/licenses/>. + +*/ + +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> +#include <pthread.h> + +static void *p; + +static void +handler (int sig, siginfo_t *info, void *context) +{ + /* Copy to local vars, as the test wants to read them, and si_addr, + etc. may be preprocessor defines. */ + int ssi_errno = info->si_errno; + int ssi_signo = info->si_signo; + int ssi_code = info->si_code; + void *ssi_addr = info->si_addr; + + _exit (0); /* set breakpoint here */ +} + +static void * +segv_thread (void *ptr) +{ + *(int *)ptr = 0; +} + +int +main (void) +{ + pthread_t thr; + + /* Set up unwritable memory. */ + { + size_t len; + len = sysconf(_SC_PAGESIZE); + p = mmap (0, len, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + { + perror ("mmap"); + return 1; + } + } + /* Set up the signal handler. */ + { + struct sigaction action; + memset (&action, 0, sizeof (action)); + action.sa_sigaction = handler; + action.sa_flags |= SA_SIGINFO; + if (sigaction (SIGSEGV, &action, NULL)) + { + perror ("sigaction"); + return 1; + } + } + + /* Create a thread that will trigger SIGSEGV. */ + pthread_create (&thr, NULL, segv_thread, p); + + pthread_join (thr, NULL); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/siginfo-thread.exp b/gdb/testsuite/gdb.base/siginfo-thread.exp new file mode 100644 index 0000000..194d856 --- /dev/null +++ b/gdb/testsuite/gdb.base/siginfo-thread.exp @@ -0,0 +1,122 @@ +# Copyright 2004, 2007-2008, 2010-2012 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 <http://www.gnu.org/licenses/>. + + +# Multi-threaded siginfo test. + +if [target_info exists gdb,nosignals] { + verbose "Skipping siginfo-thread.exp because of nosignals." + continue +} + +if { ! [istarget "i?86-*-linux*"] + && ! [istarget "x86_64-*-linux*"] + && ! [istarget "arm*-*-linux*"] } { + verbose "Skipping siginfo-thread.exp because of lack of support." + return +} + +standard_testfile .c + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" \ + "${binfile}" executable {debug}] != "" } { + return -1 +} + +clean_restart $binfile + +# Advance to main +if { ![runto_main] } then { + gdb_suppress_tests +} + +# Run to the signal. +gdb_test "continue" ".*Program received signal SIGSEGV.*" "continue to signal" + +# Try to generate a core file, for a later test. +set gcorefile [standard_output_file $testfile.gcore] +set gcore_created [gdb_gcore_cmd $gcorefile "save a core file"] + +set ssi_addr "" +set test "Extract si_addr" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_addr = ($hex).*$gdb_prompt $" { + set ssi_addr $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_errno" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_errno = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_errno $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_code" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_code = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_code $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_signo" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_signo = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_signo $expect_out(1,string) + pass "$test" + } +} + +set bp_location [gdb_get_line_number "set breakpoint here"] + +gdb_test "break $bp_location" +gdb_test "continue" ".* handler .*" "continue to handler" + +gdb_test "p ssi_addr" " = \\(void \\*\\) $ssi_addr" +gdb_test "p ssi_errno" " = $ssi_errno" +gdb_test "p ssi_code" " = $ssi_code" +gdb_test "p ssi_signo" " = $ssi_signo" + +gdb_test "thread 1" ".*" +# siginfo is available here -- it shows SIGSTOP -- so we test to make +# sure that we have a different signal from the SEGV thread. +gdb_test "p \$_siginfo.si_signo == $ssi_errno" " = 0" \ + "test signal in main thread" + +# Test siginfo preservation in core files. +if {$gcore_created} { + clean_restart $binfile + + gdb_test "core $gcorefile" "Core was generated by.*" \ + "core [file tail $gcorefile]" + + gdb_test "p \$_siginfo.si_signo" " = $ssi_signo" \ + "p \$_siginfo.si_signo from core file" + gdb_test "p \$_siginfo.si_errno" " = $ssi_errno" \ + "p \$_siginfo.si_errno from core file" + gdb_test "p \$_siginfo.si_code" " = $ssi_code" \ + "p \$_siginfo.si_code from core file" + gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ + " = \\(void \\*\\) $ssi_addr" \ + "p \$_siginfo._sifields._sigfault.si_addr from core file" + + # The signalled thread always shows up as thread 1. + gdb_test "thread 2" ".*" "switch threads with core file" + gdb_test "p \$_siginfo.si_signo == $ssi_errno" " = 0" \ + "test signal in main thread with core file" +} -- 1.7.7.6 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: RFC: handle new NT_SIGINFO note in gdb 2012-11-02 18:21 ` Tom Tromey @ 2012-11-02 19:02 ` Pedro Alves 2012-11-02 19:32 ` Tom Tromey 0 siblings, 1 reply; 9+ messages in thread From: Pedro Alves @ 2012-11-02 19:02 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches Looks mostly good to me. Thanks. On 11/02/2012 06:21 PM, Tom Tromey wrote:> +gdb_test "thread 1" ".*" > +# siginfo is available here -- it shows SIGSTOP -- so we test to make > +# sure that we have a different signal from the SEGV thread. > +gdb_test "p \$_siginfo.si_signo == $ssi_errno" " = 0" \ > + "test signal in main thread" I think here should be $ssi_signo, not $ssi_errno. > + # The signalled thread always shows up as thread 1. > + gdb_test "thread 2" ".*" "switch threads with core file" > + gdb_test "p \$_siginfo.si_signo == $ssi_errno" " = 0" \ > + "test signal in main thread with core file" Ditto. > + # The signalled thread always shows up as thread 1. > + gdb_test "thread 2" ".*" "switch threads with core file" Note this is actually more of an accident than GDB making sure that always happens. GDB makes no effort to make sure the the signaled thread is the first dumped thread. GDB's internal thread list is ordered newer->older, and since thread 2 is the one that crashes, and is the newest, that thread ends up appearing first in the core, and hence when gdb loads the core, it ends up as selected thread. If it was thread 1 that crashed, thread 2 in the running program would still end up as thread 1 in the core. (It just tried that, with a pristine mainline). -- Pedro Alves ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: RFC: handle new NT_SIGINFO note in gdb 2012-11-02 19:02 ` Pedro Alves @ 2012-11-02 19:32 ` Tom Tromey 2012-11-05 18:11 ` Pedro Alves 0 siblings, 1 reply; 9+ messages in thread From: Tom Tromey @ 2012-11-02 19:32 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches >>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes: Pedro> I think here should be $ssi_signo, not $ssi_errno. Sad oversight on my part. Thanks. Pedro> GDB makes no effort to make sure the the signaled thread Pedro> is the first dumped thread. Thanks. Here's an update. This takes a bit of a brute force approach to testing that just one thread got a SEGV. Tom 2012-11-02 Tom Tromey <tromey@redhat.com> * elf.c (elfcore_grok_note) <NT_SIGINFO>: New case; make pseudosection. 2012-11-02 Tom Tromey <tromey@redhat.com> * linux-tdep.c (linux_make_siginfo_note): New function. (linux_make_corefile_notes): Use it. * corelow.c (get_core_siginfo): New function. (core_xfer_partial) <TARGET_OBJECT_SIGNAL_INFO>: New case. 2012-11-02 Tom Tromey <tromey@redhat.com> * gdb.base/siginfo-obj.exp: Create core file. Test siginfo from core files, if possible. * gdb.base/siginfo-thread.c: New file * gdb.base/siginfo-thread.exp: New file --- bfd/ChangeLog | 5 + bfd/elf.c | 4 + gdb/ChangeLog | 7 ++ gdb/corelow.c | 35 ++++++++ gdb/linux-tdep.c | 56 +++++++++++++ gdb/testsuite/ChangeLog | 7 ++ gdb/testsuite/gdb.base/siginfo-obj.exp | 22 +++++ gdb/testsuite/gdb.base/siginfo-thread.c | 83 +++++++++++++++++++ gdb/testsuite/gdb.base/siginfo-thread.exp | 128 +++++++++++++++++++++++++++++ 9 files changed, 347 insertions(+), 0 deletions(-) create mode 100644 gdb/testsuite/gdb.base/siginfo-thread.c create mode 100644 gdb/testsuite/gdb.base/siginfo-thread.exp diff --git a/bfd/elf.c b/bfd/elf.c index cab1cc7..4465f48 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -8604,6 +8604,10 @@ elfcore_grok_note (bfd *abfd, Elf_Internal_Note *note) return TRUE; } + + case NT_SIGINFO: + return elfcore_make_note_pseudosection (abfd, ".note.linuxcore.siginfo", + note); } } diff --git a/gdb/corelow.c b/gdb/corelow.c index 2080068..99611ba 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -656,6 +656,36 @@ add_to_spuid_list (bfd *abfd, asection *asect, void *list_p) list->pos += 4; } +/* Read siginfo data from the core, if possible. Returns -1 on + failure. Otherwise, returns the number of bytes read. ABFD is the + core file's BFD; READBUF, OFFSET, and LEN are all as specified by + the to_xfer_partial interface. */ + +static LONGEST +get_core_siginfo (bfd *abfd, gdb_byte *readbuf, ULONGEST offset, LONGEST len) +{ + asection *section; + long pid; + char *section_name; + const char *name = ".note.linuxcore.siginfo"; + + if (ptid_get_lwp (inferior_ptid)) + section_name = xstrprintf ("%s/%ld", name, + ptid_get_lwp (inferior_ptid)); + else + section_name = xstrdup (name); + + section = bfd_get_section_by_name (abfd, section_name); + xfree (section_name); + if (section == NULL) + return -1; + + if (!bfd_get_section_contents (abfd, section, readbuf, offset, len)) + return -1; + + return len; +} + static LONGEST core_xfer_partial (struct target_ops *ops, enum target_object object, const char *annex, gdb_byte *readbuf, @@ -794,6 +824,11 @@ core_xfer_partial (struct target_ops *ops, enum target_object object, } return -1; + case TARGET_OBJECT_SIGNAL_INFO: + if (readbuf) + return get_core_siginfo (core_bfd, readbuf, offset, len); + return -1; + default: if (ops->beneath != NULL) return ops->beneath->to_xfer_partial (ops->beneath, object, diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 65f5f97..f02d510 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -765,6 +765,44 @@ linux_collect_thread_registers (const struct regcache *regcache, return note_data; } +/* Fetch the siginfo data for the current thread, if it exists. If + there is no data, or we could not read it, return NULL. Otherwise, + return a newly malloc'd buffer holding the data and fill in *SIZE + with the size of the data. The caller is responsible for freeing + the data. */ + +static gdb_byte * +linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size) +{ + struct type *siginfo_type; + gdb_byte *buf; + LONGEST bytes_read; + struct cleanup *cleanups; + + if (!gdbarch_get_siginfo_type_p (gdbarch)) + return NULL; + + siginfo_type = gdbarch_get_siginfo_type (gdbarch); + + buf = xmalloc (TYPE_LENGTH (siginfo_type)); + cleanups = make_cleanup (xfree, buf); + + bytes_read = target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL, + buf, 0, TYPE_LENGTH (siginfo_type)); + if (bytes_read == TYPE_LENGTH (siginfo_type)) + { + discard_cleanups (cleanups); + *size = bytes_read; + } + else + { + do_cleanups (cleanups); + buf = NULL; + } + + return buf; +} + struct linux_corefile_thread_data { struct gdbarch *gdbarch; @@ -789,17 +827,35 @@ linux_corefile_thread_callback (struct thread_info *info, void *data) { struct cleanup *old_chain; struct regcache *regcache; + gdb_byte *siginfo_data; + LONGEST siginfo_size; + regcache = get_thread_arch_regcache (info->ptid, args->gdbarch); old_chain = save_inferior_ptid (); inferior_ptid = info->ptid; target_fetch_registers (regcache, -1); + siginfo_data = linux_get_siginfo_data (args->gdbarch, &siginfo_size); do_cleanups (old_chain); + old_chain = make_cleanup (xfree, siginfo_data); + args->note_data = args->collect (regcache, info->ptid, args->obfd, args->note_data, args->note_size, args->stop_signal); args->num_notes++; + + if (siginfo_data != NULL) + { + args->note_data = elfcore_write_note (args->obfd, + args->note_data, + args->note_size, + "CORE", NT_SIGINFO, + siginfo_data, siginfo_size); + args->num_notes++; + } + + do_cleanups (old_chain); } return !args->note_data; diff --git a/gdb/testsuite/gdb.base/siginfo-obj.exp b/gdb/testsuite/gdb.base/siginfo-obj.exp index 9ca649d..c6ab6ff 100644 --- a/gdb/testsuite/gdb.base/siginfo-obj.exp +++ b/gdb/testsuite/gdb.base/siginfo-obj.exp @@ -56,6 +56,10 @@ if { ![runto_main] } then { # Run to the signal. gdb_test "continue" ".*Program received signal SIGSEGV.*" "continue to signal" +# Try to generate a core file, for a later test. +set gcorefile [standard_output_file $testfile.gcore] +set gcore_created [gdb_gcore_cmd $gcorefile "save a core file"] + set ssi_addr "" set test "Extract si_addr" gdb_test_multiple "p \$_siginfo" "$test" { @@ -123,3 +127,21 @@ gdb_test "p ssi_addr" " = \\(void \\*\\) 0x666" gdb_test "p ssi_errno" " = 666" gdb_test "p ssi_code" " = 999" gdb_test "p ssi_signo" " = 11" + +# Test siginfo preservation in core files. +if {$gcore_created} { + clean_restart $binfile + + gdb_test "core $gcorefile" "Core was generated by.*" \ + "core [file tail $gcorefile]" + + gdb_test "p \$_siginfo.si_signo" " = $ssi_signo" \ + "p \$_siginfo.si_signo from core file" + gdb_test "p \$_siginfo.si_errno" " = $ssi_errno" \ + "p \$_siginfo.si_errno from core file" + gdb_test "p \$_siginfo.si_code" " = $ssi_code" \ + "p \$_siginfo.si_code from core file" + gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ + " = \\(void \\*\\) $ssi_addr" \ + "p \$_siginfo._sifields._sigfault.si_addr from core file" +} diff --git a/gdb/testsuite/gdb.base/siginfo-thread.c b/gdb/testsuite/gdb.base/siginfo-thread.c new file mode 100644 index 0000000..a47bcb3 --- /dev/null +++ b/gdb/testsuite/gdb.base/siginfo-thread.c @@ -0,0 +1,83 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2004, 2007-2008, 2010-2012 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 <http://www.gnu.org/licenses/>. + +*/ + +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> +#include <pthread.h> + +static void *p; + +static void +handler (int sig, siginfo_t *info, void *context) +{ + /* Copy to local vars, as the test wants to read them, and si_addr, + etc. may be preprocessor defines. */ + int ssi_errno = info->si_errno; + int ssi_signo = info->si_signo; + int ssi_code = info->si_code; + void *ssi_addr = info->si_addr; + + _exit (0); /* set breakpoint here */ +} + +static void * +segv_thread (void *ptr) +{ + *(int *)ptr = 0; +} + +int +main (void) +{ + pthread_t thr; + + /* Set up unwritable memory. */ + { + size_t len; + len = sysconf(_SC_PAGESIZE); + p = mmap (0, len, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + { + perror ("mmap"); + return 1; + } + } + /* Set up the signal handler. */ + { + struct sigaction action; + memset (&action, 0, sizeof (action)); + action.sa_sigaction = handler; + action.sa_flags |= SA_SIGINFO; + if (sigaction (SIGSEGV, &action, NULL)) + { + perror ("sigaction"); + return 1; + } + } + + /* Create a thread that will trigger SIGSEGV. */ + pthread_create (&thr, NULL, segv_thread, p); + + pthread_join (thr, NULL); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/siginfo-thread.exp b/gdb/testsuite/gdb.base/siginfo-thread.exp new file mode 100644 index 0000000..315ac01 --- /dev/null +++ b/gdb/testsuite/gdb.base/siginfo-thread.exp @@ -0,0 +1,128 @@ +# Copyright 2004, 2007-2008, 2010-2012 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 <http://www.gnu.org/licenses/>. + + +# Multi-threaded siginfo test. + +if [target_info exists gdb,nosignals] { + verbose "Skipping siginfo-thread.exp because of nosignals." + continue +} + +if { ! [istarget "i?86-*-linux*"] + && ! [istarget "x86_64-*-linux*"] + && ! [istarget "arm*-*-linux*"] } { + verbose "Skipping siginfo-thread.exp because of lack of support." + return +} + +standard_testfile .c + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" \ + "${binfile}" executable {debug}] != "" } { + return -1 +} + +clean_restart $binfile + +# Advance to main +if { ![runto_main] } then { + gdb_suppress_tests +} + +# Run to the signal. +gdb_test "continue" ".*Program received signal SIGSEGV.*" "continue to signal" + +# Try to generate a core file, for a later test. +set gcorefile [standard_output_file $testfile.gcore] +set gcore_created [gdb_gcore_cmd $gcorefile "save a core file"] + +set ssi_addr "" +set test "Extract si_addr" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_addr = ($hex).*$gdb_prompt $" { + set ssi_addr $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_errno" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_errno = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_errno $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_code" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_code = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_code $expect_out(1,string) + pass "$test" + } +} + +set test "Extract si_signo" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_signo = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_signo $expect_out(1,string) + pass "$test" + } +} + +set bp_location [gdb_get_line_number "set breakpoint here"] + +gdb_test "break $bp_location" +gdb_test "continue" ".* handler .*" "continue to handler" + +gdb_test "p ssi_addr" " = \\(void \\*\\) $ssi_addr" +gdb_test "p ssi_errno" " = $ssi_errno" +gdb_test "p ssi_code" " = $ssi_code" +gdb_test "p ssi_signo" " = $ssi_signo" + +gdb_test "thread 1" ".*" +# siginfo is available here -- it shows SIGSTOP -- so we test to make +# sure that we have a different signal from the SEGV thread. +gdb_test "p \$_siginfo.si_signo == $ssi_signo" " = 0" \ + "test signal in main thread" + +# Test siginfo preservation in core files. +if {$gcore_created} { + clean_restart $binfile + + gdb_test "core $gcorefile" "Core was generated by.*" \ + "core [file tail $gcorefile]" + + gdb_test "p \$_siginfo.si_signo" " = $ssi_signo" \ + "p \$_siginfo.si_signo from core file" + gdb_test "p \$_siginfo.si_errno" " = $ssi_errno" \ + "p \$_siginfo.si_errno from core file" + gdb_test "p \$_siginfo.si_code" " = $ssi_code" \ + "p \$_siginfo.si_code from core file" + gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ + " = \\(void \\*\\) $ssi_addr" \ + "p \$_siginfo._sifields._sigfault.si_addr from core file" + + # We don't know which thread will be signalled, so we simply + # ensure that only one thread got a SEGV. + gdb_test_no_output "set variable \$count = 0" + foreach thread {1 2} { + gdb_test "thread $thread" ".*" "select thread $thread with core file" + gdb_test_no_output \ + "set variable \$count += (\$_siginfo.si_signo == $ssi_signo)" \ + "update counter in thread $thread" + } + gdb_test "print \$count" " = 1" +} -- 1.7.7.6 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: RFC: handle new NT_SIGINFO note in gdb 2012-11-02 19:32 ` Tom Tromey @ 2012-11-05 18:11 ` Pedro Alves 2012-11-08 21:10 ` Tom Tromey 0 siblings, 1 reply; 9+ messages in thread From: Pedro Alves @ 2012-11-05 18:11 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 11/02/2012 07:32 PM, Tom Tromey wrote: > Here's an update. This takes a bit of a brute force approach to testing > that just one thread got a SEGV. Thanks. Looks fine to me. -- Pedro Alves ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: RFC: handle new NT_SIGINFO note in gdb 2012-11-05 18:11 ` Pedro Alves @ 2012-11-08 21:10 ` Tom Tromey 0 siblings, 0 replies; 9+ messages in thread From: Tom Tromey @ 2012-11-08 21:10 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches >>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes: Pedro> On 11/02/2012 07:32 PM, Tom Tromey wrote: >> Here's an update. This takes a bit of a brute force approach to testing >> that just one thread got a SEGV. Pedro> Thanks. Looks fine to me. Thanks. I am checking it in now. Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-11-08 21:10 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-30 17:18 RFC: handle new NT_SIGINFO note in gdb Tom Tromey
2012-10-30 18:31 ` Pedro Alves
2012-11-01 1:12 ` Alan Modra
[not found] <878vanyj3k.fsf__16012.8015945249$1351617533$gmane$org@fleche.redhat.com>
2012-11-01 19:01 ` Tom Tromey
2012-11-02 18:21 ` Tom Tromey
2012-11-02 19:02 ` Pedro Alves
2012-11-02 19:32 ` Tom Tromey
2012-11-05 18:11 ` Pedro Alves
2012-11-08 21:10 ` Tom Tromey
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox