You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1130 lines
41 KiB
1130 lines
41 KiB
commit df8411da087dc05481926f4c4a82deabc5bc3859 |
|
Author: Sergio Durigan Junior <sergiodj@redhat.com> |
|
Date: Tue Mar 31 19:32:34 2015 -0400 |
|
|
|
Implement support for checking /proc/PID/coredump_filter |
|
|
|
This patch, as the subject says, extends GDB so that it is able to use |
|
the contents of the file /proc/PID/coredump_filter when generating a |
|
corefile. This file contains a bit mask that is a representation of |
|
the different types of memory mappings in the Linux kernel; the user |
|
can choose to dump or not dump a certain type of memory mapping by |
|
enabling/disabling the respective bit in the bit mask. Currently, |
|
here is what is supported: |
|
|
|
bit 0 Dump anonymous private mappings. |
|
bit 1 Dump anonymous shared mappings. |
|
bit 2 Dump file-backed private mappings. |
|
bit 3 Dump file-backed shared mappings. |
|
bit 4 (since Linux 2.6.24) |
|
Dump ELF headers. |
|
bit 5 (since Linux 2.6.28) |
|
Dump private huge pages. |
|
bit 6 (since Linux 2.6.28) |
|
Dump shared huge pages. |
|
|
|
(This table has been taken from core(5), but you can also read about it |
|
on Documentation/filesystems/proc.txt inside the Linux kernel source |
|
tree). |
|
|
|
The default value for this file, used by the Linux kernel, is 0x33, |
|
which means that bits 0, 1, 4 and 5 are enabled. This is also the |
|
default for GDB implemented in this patch, FWIW. |
|
|
|
Well, reading the file is obviously trivial. The hard part, mind you, |
|
is how to determine the types of the memory mappings. For that, I |
|
extended the code of gdb/linux-tdep.c:linux_find_memory_regions_full and |
|
made it rely *much more* on the information gathered from |
|
/proc/<PID>/smaps. This file contains a "verbose dump" of the |
|
inferior's memory mappings, and we were not using as much information as |
|
we could from it. If you want to read more about this file, take a look |
|
at the proc(5) manpage (I will also write a blog post soon about |
|
everything I had to learn to get this patch done, and when I it is ready |
|
I will post it here). |
|
|
|
With Oleg Nesterov's help, we could improve the current algorithm for |
|
determining whether a memory mapping is anonymous/file-backed, |
|
private/shared. GDB now also respects the MADV_DONTDUMP flag and does |
|
not dump the memory mapping marked as so, and will always dump |
|
"[vsyscall]" or "[vdso]" mappings (just like the Linux kernel). |
|
|
|
In a nutshell, what the new code is doing is: |
|
|
|
- If the mapping is associated to a file whose name ends with |
|
" (deleted)", or if the file is "/dev/zero", or if it is "/SYSV%08x" |
|
(shared memory), or if there is no file associated with it, or if |
|
the AnonHugePages: or the Anonymous: fields in the /proc/PID/smaps |
|
have contents, then GDB considers this mapping to be anonymous. |
|
There is a special case in this, though: if the memory mapping is a |
|
file-backed one, but *also* contains "Anonymous:" or |
|
"AnonHugePages:" pages, then GDB considers this mapping to be *both* |
|
anonymous and file-backed, just like the Linux kernel does. What |
|
that means is simple: this mapping will be dumped if the user |
|
requested anonymous mappings *or* if the user requested file-backed |
|
mappings to be present in the corefile. |
|
|
|
It is worth mentioning that, from all those checks described above, |
|
the most fragile is the one to see if the file name ends with |
|
" (deleted)". This does not necessarily mean that the mapping is |
|
anonymous, because the deleted file associated with the mapping may |
|
have been a hard link to another file, for example. The Linux |
|
kernel checks to see if "i_nlink == 0", but GDB cannot easily do |
|
this check (as it has been discussed, GDB would need to run as root, |
|
and would need to check the contents of the /proc/PID/map_files/ |
|
directory in order to determine whether the deleted was a hardlink |
|
or not). Therefore, we made a compromise here, and we assume that |
|
if the file name ends with " (deleted)", then the mapping is indeed |
|
anonymous. FWIW, this is something the Linux kernel could do |
|
better: expose this information in a more direct way. |
|
|
|
- If we see the flag "sh" in the VmFlags: field (in /proc/PID/smaps), |
|
then certainly the memory mapping is shared (VM_SHARED). If we have |
|
access to the VmFlags, and we don't see the "sh" there, then |
|
certainly the mapping is private. However, older Linux kernels (see |
|
the code for more details) do not have the VmFlags field; in that |
|
case, we use another heuristic: if we see 'p' in the permission |
|
flags, then we assume that the mapping is private, even though the |
|
presence of the 's' flag there would mean VM_MAYSHARE, which means |
|
the mapping could still be private. This should work OK enough, |
|
however. |
|
|
|
Finally, it is worth mentioning that I added a new command, 'set |
|
use-coredump-filter on/off'. When it is 'on', it will read the |
|
coredump_filter' file (if it exists) and use its value; otherwise, it |
|
will use the default value mentioned above (0x33) to decide which memory |
|
mappings to dump. |
|
|
|
gdb/ChangeLog: |
|
2015-03-31 Sergio Durigan Junior <sergiodj@redhat.com> |
|
Jan Kratochvil <jan.kratochvil@redhat.com> |
|
Oleg Nesterov <oleg@redhat.com> |
|
|
|
PR corefiles/16092 |
|
* linux-tdep.c: Include 'gdbcmd.h' and 'gdb_regex.h'. |
|
New enum identifying the various options of the coredump_filter |
|
file. |
|
(struct smaps_vmflags): New struct. |
|
(use_coredump_filter): New variable. |
|
(decode_vmflags): New function. |
|
(mapping_is_anonymous_p): Likewise. |
|
(dump_mapping_p): Likewise. |
|
(linux_find_memory_regions_full): New variables |
|
'coredumpfilter_name', 'coredumpfilterdata', 'pid', 'filterflags'. |
|
Removed variable 'modified'. Read /proc/<PID>/smaps file; improve |
|
parsing of its information. Implement memory mapping filtering |
|
based on its contents. |
|
(show_use_coredump_filter): New function. |
|
(_initialize_linux_tdep): New command 'set use-coredump-filter'. |
|
* NEWS: Mention the possibility of using the |
|
'/proc/PID/coredump_filter' file when generating a corefile. |
|
Mention new command 'set use-coredump-filter'. |
|
|
|
gdb/doc/ChangeLog: |
|
2015-03-31 Sergio Durigan Junior <sergiodj@redhat.com> |
|
|
|
PR corefiles/16092 |
|
* gdb.texinfo (gcore): Mention new command 'set |
|
use-coredump-filter'. |
|
(set use-coredump-filter): Document new command. |
|
|
|
gdb/testsuite/ChangeLog: |
|
2015-03-31 Sergio Durigan Junior <sergiodj@redhat.com> |
|
|
|
PR corefiles/16092 |
|
* gdb.base/coredump-filter.c: New file. |
|
* gdb.base/coredump-filter.exp: Likewise. |
|
|
|
### a/gdb/ChangeLog |
|
### b/gdb/ChangeLog |
|
## -1,4 +1,28 @@ |
|
2015-03-31 Sergio Durigan Junior <sergiodj@redhat.com> |
|
+ Jan Kratochvil <jan.kratochvil@redhat.com> |
|
+ Oleg Nesterov <oleg@redhat.com> |
|
+ |
|
+ PR corefiles/16092 |
|
+ * linux-tdep.c: Include 'gdbcmd.h' and 'gdb_regex.h'. |
|
+ New enum identifying the various options of the coredump_filter |
|
+ file. |
|
+ (struct smaps_vmflags): New struct. |
|
+ (use_coredump_filter): New variable. |
|
+ (decode_vmflags): New function. |
|
+ (mapping_is_anonymous_p): Likewise. |
|
+ (dump_mapping_p): Likewise. |
|
+ (linux_find_memory_regions_full): New variables |
|
+ 'coredumpfilter_name', 'coredumpfilterdata', 'pid', 'filterflags'. |
|
+ Removed variable 'modified'. Read /proc/<PID>/smaps file; improve |
|
+ parsing of its information. Implement memory mapping filtering |
|
+ based on its contents. |
|
+ (show_use_coredump_filter): New function. |
|
+ (_initialize_linux_tdep): New command 'set use-coredump-filter'. |
|
+ * NEWS: Mention the possibility of using the |
|
+ '/proc/PID/coredump_filter' file when generating a corefile. |
|
+ Mention new command 'set use-coredump-filter'. |
|
+ |
|
+2015-03-31 Sergio Durigan Junior <sergiodj@redhat.com> |
|
|
|
* solib-svr4.c (solib_svr4_r_ldsomap): Catch possible exception by |
|
read_memory_unsigned_integer. |
|
Index: gdb-7.6.1/gdb/NEWS |
|
=================================================================== |
|
--- gdb-7.6.1.orig/gdb/NEWS 2015-11-27 22:31:13.965841796 +0100 |
|
+++ gdb-7.6.1/gdb/NEWS 2015-11-27 22:31:13.994841957 +0100 |
|
@@ -18,6 +18,14 @@ |
|
the program interruption transaction diagnostic block (TDB) is now |
|
represented as a number of additional "registers" in GDB. |
|
|
|
+* GDB now honors the content of the file /proc/PID/coredump_filter |
|
+ (PID is the process ID) on GNU/Linux systems. This file can be used |
|
+ to specify the types of memory mappings that will be included in a |
|
+ corefile. For more information, please refer to the manual page of |
|
+ "core(5)". GDB also has a new command: "set use-coredump-filter |
|
+ on|off". It allows to set whether GDB will read the content of the |
|
+ /proc/PID/coredump_filter file when generating a corefile. |
|
+ |
|
* New remote packets |
|
|
|
qXfer:libraries-svr4:read's annex |
|
Index: gdb-7.6.1/gdb/doc/gdb.texinfo |
|
=================================================================== |
|
--- gdb-7.6.1.orig/gdb/doc/gdb.texinfo 2015-11-27 22:31:13.811840941 +0100 |
|
+++ gdb-7.6.1/gdb/doc/gdb.texinfo 2015-11-27 22:31:14.001841996 +0100 |
|
@@ -10315,6 +10315,39 @@ |
|
|
|
Note that this command is implemented only for some systems (as of |
|
this writing, @sc{gnu}/Linux, FreeBSD, Solaris, and S390). |
|
+ |
|
+On @sc{gnu}/Linux, this command can take into account the value of the |
|
+file @file{/proc/@var{pid}/coredump_filter} when generating the core |
|
+dump (@pxref{set use-coredump-filter}). |
|
+ |
|
+@kindex set use-coredump-filter |
|
+@anchor{set use-coredump-filter} |
|
+@item set use-coredump-filter on |
|
+@itemx set use-coredump-filter off |
|
+Enable or disable the use of the file |
|
+@file{/proc/@var{pid}/coredump_filter} when generating core dump |
|
+files. This file is used by the Linux kernel to decide what types of |
|
+memory mappings will be dumped or ignored when generating a core dump |
|
+file. @var{pid} is the process ID of a currently running process. |
|
+ |
|
+To make use of this feature, you have to write in the |
|
+@file{/proc/@var{pid}/coredump_filter} file a value, in hexadecimal, |
|
+which is a bit mask representing the memory mapping types. If a bit |
|
+is set in the bit mask, then the memory mappings of the corresponding |
|
+types will be dumped; otherwise, they will be ignored. This |
|
+configuration is inherited by child processes. For more information |
|
+about the bits that can be set in the |
|
+@file{/proc/@var{pid}/coredump_filter} file, please refer to the |
|
+manpage of @code{core(5)}. |
|
+ |
|
+By default, this option is @code{on}. If this option is turned |
|
+@code{off}, @value{GDBN} does not read the @file{coredump_filter} file |
|
+and instead uses the same default value as the Linux kernel in order |
|
+to decide which pages will be dumped in the core dump file. This |
|
+value is currently @code{0x33}, which means that bits @code{0} |
|
+(anonymous private mappings), @code{1} (anonymous shared mappings), |
|
+@code{4} (ELF headers) and @code{5} (private huge pages) are active. |
|
+This will cause these memory mappings to be dumped automatically. |
|
@end table |
|
|
|
@node Character Sets |
|
Index: gdb-7.6.1/gdb/linux-tdep.c |
|
=================================================================== |
|
--- gdb-7.6.1.orig/gdb/linux-tdep.c 2013-04-05 21:17:27.000000000 +0200 |
|
+++ gdb-7.6.1/gdb/linux-tdep.c 2015-11-27 22:31:14.002842001 +0100 |
|
@@ -33,9 +33,61 @@ |
|
#include "arch-utils.h" |
|
#include "gdb_obstack.h" |
|
#include "cli/cli-utils.h" |
|
+#include "gdbcmd.h" |
|
+#include "gdb_regex.h" |
|
|
|
#include <ctype.h> |
|
|
|
+/* This enum represents the values that the user can choose when |
|
+ informing the Linux kernel about which memory mappings will be |
|
+ dumped in a corefile. They are described in the file |
|
+ Documentation/filesystems/proc.txt, inside the Linux kernel |
|
+ tree. */ |
|
+ |
|
+enum |
|
+ { |
|
+ COREFILTER_ANON_PRIVATE = 1 << 0, |
|
+ COREFILTER_ANON_SHARED = 1 << 1, |
|
+ COREFILTER_MAPPED_PRIVATE = 1 << 2, |
|
+ COREFILTER_MAPPED_SHARED = 1 << 3, |
|
+ COREFILTER_ELF_HEADERS = 1 << 4, |
|
+ COREFILTER_HUGETLB_PRIVATE = 1 << 5, |
|
+ COREFILTER_HUGETLB_SHARED = 1 << 6, |
|
+ }; |
|
+ |
|
+/* This struct is used to map flags found in the "VmFlags:" field (in |
|
+ the /proc/<PID>/smaps file). */ |
|
+ |
|
+struct smaps_vmflags |
|
+ { |
|
+ /* Zero if this structure has not been initialized yet. It |
|
+ probably means that the Linux kernel being used does not emit |
|
+ the "VmFlags:" field on "/proc/PID/smaps". */ |
|
+ |
|
+ unsigned int initialized_p : 1; |
|
+ |
|
+ /* Memory mapped I/O area (VM_IO, "io"). */ |
|
+ |
|
+ unsigned int io_page : 1; |
|
+ |
|
+ /* Area uses huge TLB pages (VM_HUGETLB, "ht"). */ |
|
+ |
|
+ unsigned int uses_huge_tlb : 1; |
|
+ |
|
+ /* Do not include this memory region on the coredump (VM_DONTDUMP, "dd"). */ |
|
+ |
|
+ unsigned int exclude_coredump : 1; |
|
+ |
|
+ /* Is this a MAP_SHARED mapping (VM_SHARED, "sh"). */ |
|
+ |
|
+ unsigned int shared_mapping : 1; |
|
+ }; |
|
+ |
|
+/* Whether to take the /proc/PID/coredump_filter into account when |
|
+ generating a corefile. */ |
|
+ |
|
+static int use_coredump_filter = 1; |
|
+ |
|
static struct gdbarch_data *linux_gdbarch_data_handle; |
|
|
|
struct linux_gdbarch_data |
|
@@ -245,6 +297,271 @@ |
|
*filename = p; |
|
} |
|
|
|
+/* Helper function to decode the "VmFlags" field in /proc/PID/smaps. |
|
+ |
|
+ This function was based on the documentation found on |
|
+ <Documentation/filesystems/proc.txt>, on the Linux kernel. |
|
+ |
|
+ Linux kernels before commit |
|
+ 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have this |
|
+ field on smaps. */ |
|
+ |
|
+static void |
|
+decode_vmflags (char *p, struct smaps_vmflags *v) |
|
+{ |
|
+ char *saveptr; |
|
+ const char *s; |
|
+ |
|
+ v->initialized_p = 1; |
|
+ p = skip_to_space (p); |
|
+ p = skip_spaces (p); |
|
+ |
|
+ for (s = strtok_r (p, " ", &saveptr); |
|
+ s != NULL; |
|
+ s = strtok_r (NULL, " ", &saveptr)) |
|
+ { |
|
+ if (strcmp (s, "io") == 0) |
|
+ v->io_page = 1; |
|
+ else if (strcmp (s, "ht") == 0) |
|
+ v->uses_huge_tlb = 1; |
|
+ else if (strcmp (s, "dd") == 0) |
|
+ v->exclude_coredump = 1; |
|
+ else if (strcmp (s, "sh") == 0) |
|
+ v->shared_mapping = 1; |
|
+ } |
|
+} |
|
+ |
|
+/* Compile a regexp and throw an exception on error. This returns a |
|
+ cleanup to free the resulting pattern on success. RX must not be |
|
+ NULL. */ |
|
+ |
|
+static struct cleanup * |
|
+compile_rx_or_error (regex_t *pattern, const char *rx, const char *message) |
|
+{ |
|
+ int code; |
|
+ |
|
+ gdb_assert (rx != NULL); |
|
+ |
|
+ code = regcomp (pattern, rx, REG_NOSUB); |
|
+ if (code != 0) |
|
+ { |
|
+ char *err = get_regcomp_error (code, pattern); |
|
+ |
|
+ make_cleanup (xfree, err); |
|
+ error (("%s: %s"), message, err); |
|
+ } |
|
+ |
|
+ return make_regfree_cleanup (pattern); |
|
+} |
|
+ |
|
+/* Return 1 if the memory mapping is anonymous, 0 otherwise. |
|
+ |
|
+ FILENAME is the name of the file present in the first line of the |
|
+ memory mapping, in the "/proc/PID/smaps" output. For example, if |
|
+ the first line is: |
|
+ |
|
+ 7fd0ca877000-7fd0d0da0000 r--p 00000000 fd:02 2100770 /path/to/file |
|
+ |
|
+ Then FILENAME will be "/path/to/file". */ |
|
+ |
|
+static int |
|
+mapping_is_anonymous_p (const char *filename) |
|
+{ |
|
+ static regex_t dev_zero_regex, shmem_file_regex, file_deleted_regex; |
|
+ static int init_regex_p = 0; |
|
+ |
|
+ if (!init_regex_p) |
|
+ { |
|
+ struct cleanup *c = make_cleanup (null_cleanup, NULL); |
|
+ |
|
+ /* Let's be pessimistic and assume there will be an error while |
|
+ compiling the regex'es. */ |
|
+ init_regex_p = -1; |
|
+ |
|
+ /* DEV_ZERO_REGEX matches "/dev/zero" filenames (with or |
|
+ without the "(deleted)" string in the end). We know for |
|
+ sure, based on the Linux kernel code, that memory mappings |
|
+ whose associated filename is "/dev/zero" are guaranteed to be |
|
+ MAP_ANONYMOUS. */ |
|
+ compile_rx_or_error (&dev_zero_regex, "^/dev/zero\\( (deleted)\\)\\?$", |
|
+ _("Could not compile regex to match /dev/zero " |
|
+ "filename")); |
|
+ /* SHMEM_FILE_REGEX matches "/SYSV%08x" filenames (with or |
|
+ without the "(deleted)" string in the end). These filenames |
|
+ refer to shared memory (shmem), and memory mappings |
|
+ associated with them are MAP_ANONYMOUS as well. */ |
|
+ compile_rx_or_error (&shmem_file_regex, |
|
+ "^/\\?SYSV[0-9a-fA-F]\\{8\\}\\( (deleted)\\)\\?$", |
|
+ _("Could not compile regex to match shmem " |
|
+ "filenames")); |
|
+ /* FILE_DELETED_REGEX is a heuristic we use to try to mimic the |
|
+ Linux kernel's 'n_link == 0' code, which is responsible to |
|
+ decide if it is dealing with a 'MAP_SHARED | MAP_ANONYMOUS' |
|
+ mapping. In other words, if FILE_DELETED_REGEX matches, it |
|
+ does not necessarily mean that we are dealing with an |
|
+ anonymous shared mapping. However, there is no easy way to |
|
+ detect this currently, so this is the best approximation we |
|
+ have. |
|
+ |
|
+ As a result, GDB will dump readonly pages of deleted |
|
+ executables when using the default value of coredump_filter |
|
+ (0x33), while the Linux kernel will not dump those pages. |
|
+ But we can live with that. */ |
|
+ compile_rx_or_error (&file_deleted_regex, " (deleted)$", |
|
+ _("Could not compile regex to match " |
|
+ "'<file> (deleted)'")); |
|
+ /* We will never release these regexes, so just discard the |
|
+ cleanups. */ |
|
+ discard_cleanups (c); |
|
+ |
|
+ /* If we reached this point, then everything succeeded. */ |
|
+ init_regex_p = 1; |
|
+ } |
|
+ |
|
+ if (init_regex_p == -1) |
|
+ { |
|
+ const char deleted[] = " (deleted)"; |
|
+ size_t del_len = sizeof (deleted) - 1; |
|
+ size_t filename_len = strlen (filename); |
|
+ |
|
+ /* There was an error while compiling the regex'es above. In |
|
+ order to try to give some reliable information to the caller, |
|
+ we just try to find the string " (deleted)" in the filename. |
|
+ If we managed to find it, then we assume the mapping is |
|
+ anonymous. */ |
|
+ return (filename_len >= del_len |
|
+ && strcmp (filename + filename_len - del_len, deleted) == 0); |
|
+ } |
|
+ |
|
+ if (*filename == '\0' |
|
+ || regexec (&dev_zero_regex, filename, 0, NULL, 0) == 0 |
|
+ || regexec (&shmem_file_regex, filename, 0, NULL, 0) == 0 |
|
+ || regexec (&file_deleted_regex, filename, 0, NULL, 0) == 0) |
|
+ return 1; |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+/* Return 0 if the memory mapping (which is related to FILTERFLAGS, V, |
|
+ MAYBE_PRIVATE_P, and MAPPING_ANONYMOUS_P) should not be dumped, or |
|
+ greater than 0 if it should. |
|
+ |
|
+ In a nutshell, this is the logic that we follow in order to decide |
|
+ if a mapping should be dumped or not. |
|
+ |
|
+ - If the mapping is associated to a file whose name ends with |
|
+ " (deleted)", or if the file is "/dev/zero", or if it is |
|
+ "/SYSV%08x" (shared memory), or if there is no file associated |
|
+ with it, or if the AnonHugePages: or the Anonymous: fields in the |
|
+ /proc/PID/smaps have contents, then GDB considers this mapping to |
|
+ be anonymous. Otherwise, GDB considers this mapping to be a |
|
+ file-backed mapping (because there will be a file associated with |
|
+ it). |
|
+ |
|
+ It is worth mentioning that, from all those checks described |
|
+ above, the most fragile is the one to see if the file name ends |
|
+ with " (deleted)". This does not necessarily mean that the |
|
+ mapping is anonymous, because the deleted file associated with |
|
+ the mapping may have been a hard link to another file, for |
|
+ example. The Linux kernel checks to see if "i_nlink == 0", but |
|
+ GDB cannot easily (and normally) do this check (iff running as |
|
+ root, it could find the mapping in /proc/PID/map_files/ and |
|
+ determine whether there still are other hard links to the |
|
+ inode/file). Therefore, we made a compromise here, and we assume |
|
+ that if the file name ends with " (deleted)", then the mapping is |
|
+ indeed anonymous. FWIW, this is something the Linux kernel could |
|
+ do better: expose this information in a more direct way. |
|
+ |
|
+ - If we see the flag "sh" in the "VmFlags:" field (in |
|
+ /proc/PID/smaps), then certainly the memory mapping is shared |
|
+ (VM_SHARED). If we have access to the VmFlags, and we don't see |
|
+ the "sh" there, then certainly the mapping is private. However, |
|
+ Linux kernels before commit |
|
+ 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have the |
|
+ "VmFlags:" field; in that case, we use another heuristic: if we |
|
+ see 'p' in the permission flags, then we assume that the mapping |
|
+ is private, even though the presence of the 's' flag there would |
|
+ mean VM_MAYSHARE, which means the mapping could still be private. |
|
+ This should work OK enough, however. */ |
|
+ |
|
+static int |
|
+dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v, |
|
+ int maybe_private_p, int mapping_anon_p, int mapping_file_p, |
|
+ const char *filename) |
|
+{ |
|
+ /* Initially, we trust in what we received from our caller. This |
|
+ value may not be very precise (i.e., it was probably gathered |
|
+ from the permission line in the /proc/PID/smaps list, which |
|
+ actually refers to VM_MAYSHARE, and not VM_SHARED), but it is |
|
+ what we have until we take a look at the "VmFlags:" field |
|
+ (assuming that the version of the Linux kernel being used |
|
+ supports it, of course). */ |
|
+ int private_p = maybe_private_p; |
|
+ |
|
+ /* We always dump vDSO and vsyscall mappings, because it's likely that |
|
+ there'll be no file to read the contents from at core load time. |
|
+ The kernel does the same. */ |
|
+ if (strcmp ("[vdso]", filename) == 0 |
|
+ || strcmp ("[vsyscall]", filename) == 0) |
|
+ return 1; |
|
+ |
|
+ if (v->initialized_p) |
|
+ { |
|
+ /* We never dump I/O mappings. */ |
|
+ if (v->io_page) |
|
+ return 0; |
|
+ |
|
+ /* Check if we should exclude this mapping. */ |
|
+ if (v->exclude_coredump) |
|
+ return 0; |
|
+ |
|
+ /* Update our notion of whether this mapping is shared or |
|
+ private based on a trustworthy value. */ |
|
+ private_p = !v->shared_mapping; |
|
+ |
|
+ /* HugeTLB checking. */ |
|
+ if (v->uses_huge_tlb) |
|
+ { |
|
+ if ((private_p && (filterflags & COREFILTER_HUGETLB_PRIVATE)) |
|
+ || (!private_p && (filterflags & COREFILTER_HUGETLB_SHARED))) |
|
+ return 1; |
|
+ |
|
+ return 0; |
|
+ } |
|
+ } |
|
+ |
|
+ if (private_p) |
|
+ { |
|
+ if (mapping_anon_p && mapping_file_p) |
|
+ { |
|
+ /* This is a special situation. It can happen when we see a |
|
+ mapping that is file-backed, but that contains anonymous |
|
+ pages. */ |
|
+ return ((filterflags & COREFILTER_ANON_PRIVATE) != 0 |
|
+ || (filterflags & COREFILTER_MAPPED_PRIVATE) != 0); |
|
+ } |
|
+ else if (mapping_anon_p) |
|
+ return (filterflags & COREFILTER_ANON_PRIVATE) != 0; |
|
+ else |
|
+ return (filterflags & COREFILTER_MAPPED_PRIVATE) != 0; |
|
+ } |
|
+ else |
|
+ { |
|
+ if (mapping_anon_p && mapping_file_p) |
|
+ { |
|
+ /* This is a special situation. It can happen when we see a |
|
+ mapping that is file-backed, but that contains anonymous |
|
+ pages. */ |
|
+ return ((filterflags & COREFILTER_ANON_SHARED) != 0 |
|
+ || (filterflags & COREFILTER_MAPPED_SHARED) != 0); |
|
+ } |
|
+ else if (mapping_anon_p) |
|
+ return (filterflags & COREFILTER_ANON_SHARED) != 0; |
|
+ else |
|
+ return (filterflags & COREFILTER_MAPPED_SHARED) != 0; |
|
+ } |
|
+} |
|
+ |
|
/* Implement the "info proc" command. */ |
|
|
|
static void |
|
@@ -676,48 +993,97 @@ |
|
void *obfd) |
|
{ |
|
char mapsfilename[100]; |
|
- gdb_byte *data; |
|
+ char coredumpfilter_name[100]; |
|
+ char *data, *coredumpfilterdata; |
|
+ pid_t pid; |
|
+ /* Default dump behavior of coredump_filter (0x33), according to |
|
+ Documentation/filesystems/proc.txt from the Linux kernel |
|
+ tree. */ |
|
+ unsigned int filterflags = (COREFILTER_ANON_PRIVATE |
|
+ | COREFILTER_ANON_SHARED |
|
+ | COREFILTER_ELF_HEADERS |
|
+ | COREFILTER_HUGETLB_PRIVATE); |
|
|
|
/* We need to know the real target PID to access /proc. */ |
|
if (current_inferior ()->fake_pid_p) |
|
return 1; |
|
|
|
- xsnprintf (mapsfilename, sizeof mapsfilename, |
|
- "/proc/%d/smaps", current_inferior ()->pid); |
|
+ pid = current_inferior ()->pid; |
|
+ |
|
+ if (use_coredump_filter) |
|
+ { |
|
+ xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name), |
|
+ "/proc/%d/coredump_filter", pid); |
|
+ coredumpfilterdata = target_fileio_read_stralloc (coredumpfilter_name); |
|
+ if (coredumpfilterdata != NULL) |
|
+ { |
|
+ sscanf (coredumpfilterdata, "%x", &filterflags); |
|
+ xfree (coredumpfilterdata); |
|
+ } |
|
+ } |
|
+ |
|
+ xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid); |
|
data = target_fileio_read_stralloc (mapsfilename); |
|
if (data == NULL) |
|
{ |
|
/* Older Linux kernels did not support /proc/PID/smaps. */ |
|
- xsnprintf (mapsfilename, sizeof mapsfilename, |
|
- "/proc/%d/maps", current_inferior ()->pid); |
|
+ xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid); |
|
data = target_fileio_read_stralloc (mapsfilename); |
|
} |
|
- if (data) |
|
+ |
|
+ if (data != NULL) |
|
{ |
|
struct cleanup *cleanup = make_cleanup (xfree, data); |
|
- char *line; |
|
+ char *line, *t; |
|
|
|
- line = strtok (data, "\n"); |
|
- while (line) |
|
+ line = strtok_r (data, "\n", &t); |
|
+ while (line != NULL) |
|
{ |
|
ULONGEST addr, endaddr, offset, inode; |
|
const char *permissions, *device, *filename; |
|
+ struct smaps_vmflags v; |
|
size_t permissions_len, device_len; |
|
- int read, write, exec; |
|
- int modified = 0, has_anonymous = 0; |
|
+ int read, write, exec, private; |
|
+ int has_anonymous = 0; |
|
+ int should_dump_p = 0; |
|
+ int mapping_anon_p; |
|
+ int mapping_file_p; |
|
|
|
+ memset (&v, 0, sizeof (v)); |
|
read_mapping (line, &addr, &endaddr, &permissions, &permissions_len, |
|
&offset, &device, &device_len, &inode, &filename); |
|
+ mapping_anon_p = mapping_is_anonymous_p (filename); |
|
+ /* If the mapping is not anonymous, then we can consider it |
|
+ to be file-backed. These two states (anonymous or |
|
+ file-backed) seem to be exclusive, but they can actually |
|
+ coexist. For example, if a file-backed mapping has |
|
+ "Anonymous:" pages (see more below), then the Linux |
|
+ kernel will dump this mapping when the user specified |
|
+ that she only wants anonymous mappings in the corefile |
|
+ (*even* when she explicitly disabled the dumping of |
|
+ file-backed mappings). */ |
|
+ mapping_file_p = !mapping_anon_p; |
|
|
|
/* Decode permissions. */ |
|
read = (memchr (permissions, 'r', permissions_len) != 0); |
|
write = (memchr (permissions, 'w', permissions_len) != 0); |
|
exec = (memchr (permissions, 'x', permissions_len) != 0); |
|
- |
|
- /* Try to detect if region was modified by parsing smaps counters. */ |
|
- for (line = strtok (NULL, "\n"); |
|
- line && line[0] >= 'A' && line[0] <= 'Z'; |
|
- line = strtok (NULL, "\n")) |
|
+ /* 'private' here actually means VM_MAYSHARE, and not |
|
+ VM_SHARED. In order to know if a mapping is really |
|
+ private or not, we must check the flag "sh" in the |
|
+ VmFlags field. This is done by decode_vmflags. However, |
|
+ if we are using a Linux kernel released before the commit |
|
+ 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will |
|
+ not have the VmFlags there. In this case, there is |
|
+ really no way to know if we are dealing with VM_SHARED, |
|
+ so we just assume that VM_MAYSHARE is enough. */ |
|
+ private = memchr (permissions, 'p', permissions_len) != 0; |
|
+ |
|
+ /* Try to detect if region should be dumped by parsing smaps |
|
+ counters. */ |
|
+ for (line = strtok_r (NULL, "\n", &t); |
|
+ line != NULL && line[0] >= 'A' && line[0] <= 'Z'; |
|
+ line = strtok_r (NULL, "\n", &t)) |
|
{ |
|
char keyword[64 + 1]; |
|
|
|
@@ -726,11 +1092,17 @@ |
|
warning (_("Error parsing {s,}maps file '%s'"), mapsfilename); |
|
break; |
|
} |
|
+ |
|
if (strcmp (keyword, "Anonymous:") == 0) |
|
- has_anonymous = 1; |
|
- if (strcmp (keyword, "Shared_Dirty:") == 0 |
|
- || strcmp (keyword, "Private_Dirty:") == 0 |
|
- || strcmp (keyword, "Swap:") == 0 |
|
+ { |
|
+ /* Older Linux kernels did not support the |
|
+ "Anonymous:" counter. Check it here. */ |
|
+ has_anonymous = 1; |
|
+ } |
|
+ else if (strcmp (keyword, "VmFlags:") == 0) |
|
+ decode_vmflags (line, &v); |
|
+ |
|
+ if (strcmp (keyword, "AnonHugePages:") == 0 |
|
|| strcmp (keyword, "Anonymous:") == 0) |
|
{ |
|
unsigned long number; |
|
@@ -741,19 +1113,46 @@ |
|
mapsfilename); |
|
break; |
|
} |
|
- if (number != 0) |
|
- modified = 1; |
|
+ if (number > 0) |
|
+ { |
|
+ /* Even if we are dealing with a file-backed |
|
+ mapping, if it contains anonymous pages we |
|
+ consider it to be *also* an anonymous |
|
+ mapping, because this is what the Linux |
|
+ kernel does: |
|
+ |
|
+ // Dump segments that have been written to. |
|
+ if (vma->anon_vma && FILTER(ANON_PRIVATE)) |
|
+ goto whole; |
|
+ |
|
+ Note that if the mapping is already marked as |
|
+ file-backed (i.e., mapping_file_p is |
|
+ non-zero), then this is a special case, and |
|
+ this mapping will be dumped either when the |
|
+ user wants to dump file-backed *or* anonymous |
|
+ mappings. */ |
|
+ mapping_anon_p = 1; |
|
+ } |
|
} |
|
} |
|
|
|
- /* Older Linux kernels did not support the "Anonymous:" counter. |
|
- If it is missing, we can't be sure - dump all the pages. */ |
|
- if (!has_anonymous) |
|
- modified = 1; |
|
+ if (has_anonymous) |
|
+ should_dump_p = dump_mapping_p (filterflags, &v, private, |
|
+ mapping_anon_p, mapping_file_p, |
|
+ filename); |
|
+ else |
|
+ { |
|
+ /* Older Linux kernels did not support the "Anonymous:" counter. |
|
+ If it is missing, we can't be sure - dump all the pages. */ |
|
+ should_dump_p = 1; |
|
+ } |
|
|
|
/* Invoke the callback function to create the corefile segment. */ |
|
- func (addr, endaddr - addr, offset, inode, |
|
- read, write, exec, modified, filename, obfd); |
|
+ if (should_dump_p) |
|
+ func (addr, endaddr - addr, offset, inode, |
|
+ read, write, exec, 1, /* MODIFIED is true because we |
|
+ want to dump the mapping. */ |
|
+ filename, obfd); |
|
} |
|
|
|
do_cleanups (cleanup); |
|
@@ -1447,6 +1846,17 @@ |
|
linux_collect_thread_registers); |
|
} |
|
|
|
+/* Display whether the gcore command is using the |
|
+ /proc/PID/coredump_filter file. */ |
|
+ |
|
+static void |
|
+show_use_coredump_filter (struct ui_file *file, int from_tty, |
|
+ struct cmd_list_element *c, const char *value) |
|
+{ |
|
+ fprintf_filtered (file, _("Use of /proc/PID/coredump_filter file to generate" |
|
+ " corefiles is %s.\n"), value); |
|
+} |
|
+ |
|
/* To be called from the various GDB_OSABI_LINUX handlers for the |
|
various GNU/Linux architectures and machine types. */ |
|
|
|
@@ -1470,4 +1880,16 @@ |
|
{ |
|
linux_gdbarch_data_handle = |
|
gdbarch_data_register_post_init (init_linux_gdbarch_data); |
|
+ |
|
+ add_setshow_boolean_cmd ("use-coredump-filter", class_files, |
|
+ &use_coredump_filter, _("\ |
|
+Set whether gcore should consider /proc/PID/coredump_filter."), |
|
+ _("\ |
|
+Show whether gcore should consider /proc/PID/coredump_filter."), |
|
+ _("\ |
|
+Use this command to set whether gcore should consider the contents\n\ |
|
+of /proc/PID/coredump_filter when generating the corefile. For more information\n\ |
|
+about this file, refer to the manpage of core(5)."), |
|
+ NULL, show_use_coredump_filter, |
|
+ &setlist, &showlist); |
|
} |
|
Index: gdb-7.6.1/gdb/testsuite/gdb.base/coredump-filter.c |
|
=================================================================== |
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 |
|
+++ gdb-7.6.1/gdb/testsuite/gdb.base/coredump-filter.c 2015-11-27 22:31:14.002842001 +0100 |
|
@@ -0,0 +1,61 @@ |
|
+/* Copyright 2015 Free Software Foundation, Inc. |
|
+ |
|
+ This file is part of GDB. |
|
+ |
|
+ 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/>. */ |
|
+ |
|
+#define _GNU_SOURCE |
|
+#include <stdlib.h> |
|
+#include <assert.h> |
|
+#include <unistd.h> |
|
+#include <stdio.h> |
|
+#include <sys/mman.h> |
|
+#include <errno.h> |
|
+#include <string.h> |
|
+ |
|
+static void * |
|
+do_mmap (void *addr, size_t size, int prot, int flags, int fd, off_t offset) |
|
+{ |
|
+ void *ret = mmap (addr, size, prot, flags, fd, offset); |
|
+ |
|
+ assert (ret != NULL); |
|
+ return ret; |
|
+} |
|
+ |
|
+int |
|
+main (int argc, char *argv[]) |
|
+{ |
|
+ const size_t size = 10; |
|
+ const int default_prot = PROT_READ | PROT_WRITE; |
|
+ char *private_anon, *shared_anon; |
|
+ char *dont_dump; |
|
+ int i; |
|
+ |
|
+ private_anon = do_mmap (NULL, size, default_prot, |
|
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
|
+ memset (private_anon, 0x11, size); |
|
+ |
|
+ shared_anon = do_mmap (NULL, size, default_prot, |
|
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0); |
|
+ memset (shared_anon, 0x22, size); |
|
+ |
|
+ dont_dump = do_mmap (NULL, size, default_prot, |
|
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
|
+ memset (dont_dump, 0x55, size); |
|
+ i = madvise (dont_dump, size, MADV_DONTDUMP); |
|
+ assert_perror (errno); |
|
+ assert (i == 0); |
|
+ |
|
+ return 0; /* break-here */ |
|
+} |
|
Index: gdb-7.6.1/gdb/testsuite/gdb.base/coredump-filter.exp |
|
=================================================================== |
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 |
|
+++ gdb-7.6.1/gdb/testsuite/gdb.base/coredump-filter.exp 2015-11-27 22:37:28.816872494 +0100 |
|
@@ -0,0 +1,265 @@ |
|
+# Copyright 2015 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/>. |
|
+ |
|
+standard_testfile |
|
+ |
|
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } { |
|
+ untested "could not compile test program" |
|
+ return -1 |
|
+} |
|
+ |
|
+if { ![runto_main] } { |
|
+ untested "could not run to main" |
|
+ return -1 |
|
+} |
|
+ |
|
+gdb_breakpoint [gdb_get_line_number "break-here"] |
|
+gdb_continue_to_breakpoint "break-here" ".* break-here .*" |
|
+ |
|
+# Invoke "gcore". CORE is the name of the core file to write. TEST |
|
+# is the name of the test case. This will return 1 if the core file |
|
+# was created, 0 otherwise. If this fails to make a core file because |
|
+# this configuration of gdb does not support making core files, it |
|
+# will call "unsupported", not "fail". However, if this fails to make |
|
+# a core file for some other reason, then it will call "fail". |
|
+ |
|
+proc gdb_gcore_cmd {core test} { |
|
+ global gdb_prompt |
|
+ |
|
+ set result 0 |
|
+ gdb_test_multiple "gcore $core" $test { |
|
+ -re "Saved corefile .*\[\r\n\]+$gdb_prompt $" { |
|
+ pass $test |
|
+ set result 1 |
|
+ } |
|
+ -re "(?:Can't create a corefile|Target does not support core file generation\\.)\[\r\n\]+$gdb_prompt $" { |
|
+ unsupported $test |
|
+ } |
|
+ } |
|
+ |
|
+ return $result |
|
+} |
|
+ |
|
+# Load core file CORE. TEST is the name of the test case. |
|
+# This will record a pass/fail for loading the core file. |
|
+# Returns: |
|
+# 1 - core file is successfully loaded |
|
+# 0 - core file loaded but has a non fatal error |
|
+# -1 - core file failed to load |
|
+ |
|
+proc gdb_core_cmd { core test } { |
|
+ global gdb_prompt |
|
+ |
|
+ gdb_test_multiple "core $core" "$test" { |
|
+ -re "\\\[Thread debugging using \[^ \r\n\]* enabled\\\]\r\n" { |
|
+ exp_continue |
|
+ } |
|
+ -re " is not a core dump:.*\r\n$gdb_prompt $" { |
|
+ fail "$test (bad file format)" |
|
+ return -1 |
|
+ } |
|
+ -re ": No such file or directory.*\r\n$gdb_prompt $" { |
|
+ fail "$test (file not found)" |
|
+ return -1 |
|
+ } |
|
+ -re "Couldn't find .* registers in core file.*\r\n$gdb_prompt $" { |
|
+ fail "$test (incomplete note section)" |
|
+ return 0 |
|
+ } |
|
+ -re "Core was generated by .*\r\n$gdb_prompt $" { |
|
+ pass "$test" |
|
+ return 1 |
|
+ } |
|
+ -re ".*$gdb_prompt $" { |
|
+ fail "$test" |
|
+ return -1 |
|
+ } |
|
+ timeout { |
|
+ fail "$test (timeout)" |
|
+ return -1 |
|
+ } |
|
+ } |
|
+ fail "unsupported output from 'core' command" |
|
+ return -1 |
|
+} |
|
+ |
|
+proc do_save_core { filter_flag core ipid } { |
|
+ verbose -log "writing $filter_flag to /proc/$ipid/coredump_filter" |
|
+ |
|
+ remote_exec target "sh -c \"echo $filter_flag > /proc/$ipid/coredump_filter\"" |
|
+ |
|
+ # Generate a corefile. |
|
+ gdb_gcore_cmd "$core" "save corefile" |
|
+} |
|
+ |
|
+proc do_load_and_test_core { core var working_var working_value } { |
|
+ global hex decimal addr |
|
+ |
|
+ set core_loaded [gdb_core_cmd "$core" "load core"] |
|
+ if { $core_loaded == -1 } { |
|
+ fail "loading $core" |
|
+ return |
|
+ } |
|
+ |
|
+ # Access the memory the addresses point to. |
|
+ gdb_test "print/x *(char *) $addr($var)" "\(\\\$$decimal = <error: \)?Cannot access memory at address $hex\(>\)?" \ |
|
+ "printing $var when core is loaded (should not work)" |
|
+ gdb_test "print/x *(char *) $addr($working_var)" " = $working_value.*" \ |
|
+ "print/x *$working_var ( = $working_value)" |
|
+} |
|
+ |
|
+# We do not do file-backed mappings in the test program, but it is |
|
+# important to test this anyway. One way of performing the test is to |
|
+# load GDB with a corefile but without a binary, and then ask for the |
|
+# disassemble of a function (i.e., the binary's .text section). GDB |
|
+# should fail in this case. However, it must succeed if the binary is |
|
+# provided along with the corefile. This is what we test here. |
|
+ |
|
+proc test_disasm { core address should_fail } { |
|
+ global testfile hex |
|
+ |
|
+ # Restart GDB without loading the binary. |
|
+ with_test_prefix "no binary" { |
|
+ gdb_exit |
|
+ gdb_start |
|
+ |
|
+ set core_loaded [gdb_core_cmd "$core" "load core"] |
|
+ if { $core_loaded == -1 } { |
|
+ fail "loading $core" |
|
+ return |
|
+ } |
|
+ |
|
+ if { $should_fail == 1 } { |
|
+ gdb_test "x/i \$pc" "=> $hex:\tCannot access memory at address $hex" \ |
|
+ "disassemble function with corefile and without a binary" |
|
+ } else { |
|
+ gdb_test "x/i \$pc" "=> $hex:\t\[^C\].*" \ |
|
+ "disassemble function with corefile and without a binary" |
|
+ } |
|
+ } |
|
+ |
|
+ with_test_prefix "with binary" { |
|
+ clean_restart $testfile |
|
+ |
|
+ set core_loaded [gdb_core_cmd "$core" "load core"] |
|
+ if { $core_loaded == -1 } { |
|
+ fail "loading $core" |
|
+ return |
|
+ } |
|
+ |
|
+ gdb_test "disassemble $address" "Dump of assembler code for function.*" \ |
|
+ "disassemble function with corefile and with a binary" |
|
+ } |
|
+} |
|
+ |
|
+set non_private_anon_core [standard_output_file non-private-anon.gcore] |
|
+set non_shared_anon_core [standard_output_file non-shared-anon.gcore] |
|
+# A corefile without {private,shared} {anonymous,file-backed} pages |
|
+set non_private_shared_anon_file_core [standard_output_file non-private-shared-anon-file.gcore] |
|
+set dont_dump_core [standard_output_file dont-dump.gcore] |
|
+ |
|
+# We will generate a few corefiles. |
|
+# |
|
+# This list is composed by sub-lists, and their elements are (in |
|
+# order): |
|
+# |
|
+# - name of the test |
|
+# - hexadecimal value to be put in the /proc/PID/coredump_filter file |
|
+# - name of the variable that contains the name of the corefile to be |
|
+# generated (including the initial $). |
|
+# - name of the variable in the C source code that points to the |
|
+# memory mapping that will NOT be present in the corefile. |
|
+# - name of a variable in the C source code that points to a memory |
|
+# mapping that WILL be present in the corefile |
|
+# - corresponding value expected for the above variable |
|
+# |
|
+# This list refers to the corefiles generated by MAP_ANONYMOUS in the |
|
+# test program. |
|
+ |
|
+set all_anon_corefiles { { "non-Private-Anonymous" "0x7e" \ |
|
+ $non_private_anon_core \ |
|
+ "private_anon" \ |
|
+ "shared_anon" "0x22" } |
|
+ { "non-Shared-Anonymous" "0x7d" \ |
|
+ $non_shared_anon_core "shared_anon" \ |
|
+ "private_anon" "0x11" } |
|
+ { "DoNotDump" "0x33" \ |
|
+ $dont_dump_core "dont_dump" \ |
|
+ "shared_anon" "0x22" } } |
|
+ |
|
+# If corefile loading is not supported, we do not even try to run the |
|
+# tests. |
|
+set core_supported [gdb_gcore_cmd "$non_private_anon_core" "save a corefile"] |
|
+if { !$core_supported } { |
|
+ untested "corefile generation is not supported" |
|
+ return -1 |
|
+} |
|
+ |
|
+# Get the inferior's PID. |
|
+set infpid "" |
|
+gdb_test_multiple "info inferiors" "getting inferior pid" { |
|
+ -re "process \($decimal\).*\r\n$gdb_prompt $" { |
|
+ set infpid $expect_out(1,string) |
|
+ } |
|
+} |
|
+ |
|
+# Get the main function's address. |
|
+set main_addr "" |
|
+gdb_test_multiple "print/x &main" "getting main's address" { |
|
+ -re "$decimal = \($hex\)\r\n$gdb_prompt $" { |
|
+ set main_addr $expect_out(1,string) |
|
+ } |
|
+} |
|
+ |
|
+# Obtain the address of each variable that will be checked on each |
|
+# case. |
|
+foreach item $all_anon_corefiles { |
|
+ foreach name [list [lindex $item 3] [lindex $item 4]] { |
|
+ set test "print/x $name" |
|
+ gdb_test_multiple $test $test { |
|
+ -re " = \($hex\)\r\n$gdb_prompt $" { |
|
+ set addr($name) $expect_out(1,string) |
|
+ } |
|
+ } |
|
+ } |
|
+} |
|
+ |
|
+# Generate corefiles for the "anon" case. |
|
+foreach item $all_anon_corefiles { |
|
+ with_test_prefix "saving corefile for [lindex $item 0]" { |
|
+ do_save_core [lindex $item 1] [subst [lindex $item 2]] $infpid |
|
+ } |
|
+} |
|
+ |
|
+with_test_prefix "saving corefile for non-Private-Shared-Anon-File" { |
|
+ do_save_core "0x60" $non_private_shared_anon_file_core $infpid |
|
+} |
|
+ |
|
+clean_restart $testfile |
|
+ |
|
+foreach item $all_anon_corefiles { |
|
+ with_test_prefix "loading and testing corefile for [lindex $item 0]" { |
|
+ do_load_and_test_core [subst [lindex $item 2]] [lindex $item 3] \ |
|
+ [lindex $item 4] [lindex $item 5] |
|
+ } |
|
+ |
|
+ with_test_prefix "disassembling function main for [lindex $item 0]" { |
|
+ test_disasm [subst [lindex $item 2]] $main_addr 0 |
|
+ } |
|
+} |
|
+ |
|
+with_test_prefix "loading and testing corefile for non-Private-Shared-Anon-File" { |
|
+ test_disasm $non_private_shared_anon_file_core $main_addr 1 |
|
+}
|
|
|