| 1 | /* This testcase is part of GDB, the GNU debugger. |
| 2 | |
| 3 | Copyright 2004-2017 Free Software Foundation, Inc. |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; either version 3 of the License, or |
| 8 | (at your option) any later version. |
| 9 | |
| 10 | This program is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | GNU General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU General Public License |
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| 17 | |
| 18 | /* Get 64-bit stuff if on a GNU system. */ |
| 19 | #define _GNU_SOURCE |
| 20 | |
| 21 | #include <sys/types.h> |
| 22 | #include <sys/time.h> |
| 23 | #include <sys/resource.h> |
| 24 | #include <sys/stat.h> |
| 25 | #include <fcntl.h> |
| 26 | |
| 27 | #include <stdlib.h> |
| 28 | #include <unistd.h> |
| 29 | |
| 30 | /* This test was written for >2GB core files on 32-bit systems. On |
| 31 | current 64-bit systems, generating a >4EB (2 ** 63) core file is |
| 32 | not practical, and getting as close as we can takes a lot of |
| 33 | useless CPU time. So limit ourselves to a bit bigger than |
| 34 | 32-bit, which is still a useful test. */ |
| 35 | #define RLIMIT_CAP (1ULL << 34) |
| 36 | |
| 37 | /* Print routines: |
| 38 | |
| 39 | The following are so that printf et.al. can be avoided. Those |
| 40 | might try to use malloc() and that, for this code, would be a |
| 41 | disaster. */ |
| 42 | |
| 43 | #define printf do not use |
| 44 | |
| 45 | const char digit[] = "0123456789abcdefghijklmnopqrstuvwxyz"; |
| 46 | |
| 47 | static void |
| 48 | print_char (char c) |
| 49 | { |
| 50 | write (1, &c, sizeof (c)); |
| 51 | } |
| 52 | |
| 53 | static void |
| 54 | print_unsigned (unsigned long long u) |
| 55 | { |
| 56 | if (u >= 10) |
| 57 | print_unsigned (u / 10); |
| 58 | print_char (digit[u % 10]); |
| 59 | } |
| 60 | |
| 61 | static void |
| 62 | print_hex (unsigned long long u) |
| 63 | { |
| 64 | if (u >= 16) |
| 65 | print_hex (u / 16); |
| 66 | print_char (digit[u % 16]); |
| 67 | } |
| 68 | |
| 69 | static void |
| 70 | print_string (const char *s) |
| 71 | { |
| 72 | for (; (*s) != '\0'; s++) |
| 73 | print_char ((*s)); |
| 74 | } |
| 75 | |
| 76 | static void |
| 77 | print_address (const void *a) |
| 78 | { |
| 79 | print_string ("0x"); |
| 80 | print_hex ((unsigned long) a); |
| 81 | } |
| 82 | |
| 83 | static void |
| 84 | print_byte_count (unsigned long long u) |
| 85 | { |
| 86 | print_unsigned (u); |
| 87 | print_string (" ("); |
| 88 | print_string ("0x"); |
| 89 | print_hex (u); |
| 90 | print_string (") bytes"); |
| 91 | } |
| 92 | |
| 93 | /* Print the current values of RESOURCE. */ |
| 94 | |
| 95 | static void |
| 96 | print_rlimit (int resource) |
| 97 | { |
| 98 | struct rlimit rl; |
| 99 | getrlimit (resource, &rl); |
| 100 | print_string ("cur=0x"); |
| 101 | print_hex (rl.rlim_cur); |
| 102 | print_string (" max=0x"); |
| 103 | print_hex (rl.rlim_max); |
| 104 | } |
| 105 | |
| 106 | static void |
| 107 | maximize_rlimit (int resource, const char *prefix) |
| 108 | { |
| 109 | struct rlimit rl; |
| 110 | print_string (" "); |
| 111 | print_string (prefix); |
| 112 | print_string (": "); |
| 113 | print_rlimit (resource); |
| 114 | getrlimit (resource, &rl); |
| 115 | rl.rlim_cur = rl.rlim_max; |
| 116 | if (sizeof (rl.rlim_cur) >= sizeof (RLIMIT_CAP)) |
| 117 | rl.rlim_cur = (rlim_t) RLIMIT_CAP; |
| 118 | setrlimit (resource, &rl); |
| 119 | print_string (" -> "); |
| 120 | print_rlimit (resource); |
| 121 | print_string ("\n"); |
| 122 | } |
| 123 | |
| 124 | /* Maintain a doublely linked list. */ |
| 125 | struct list |
| 126 | { |
| 127 | struct list *next; |
| 128 | struct list *prev; |
| 129 | size_t size; |
| 130 | }; |
| 131 | |
| 132 | /* Put the "heap" in the DATA section. That way it is more likely |
| 133 | that the variable will occur early in the core file (an address |
| 134 | before the heap) and hence more likely that GDB will at least get |
| 135 | its value right. |
| 136 | |
| 137 | To simplify the list append logic, start the heap out with one |
| 138 | entry (that lives in the BSS section). */ |
| 139 | |
| 140 | static struct list dummy; |
| 141 | static struct list heap = { &dummy, &dummy }; |
| 142 | |
| 143 | static unsigned long bytes_allocated; |
| 144 | |
| 145 | #ifdef O_LARGEFILE |
| 146 | #define large_off_t off64_t |
| 147 | #define large_lseek lseek64 |
| 148 | #else |
| 149 | #define large_off_t off_t |
| 150 | #define O_LARGEFILE 0 |
| 151 | #define large_lseek lseek |
| 152 | #endif |
| 153 | |
| 154 | int |
| 155 | main () |
| 156 | { |
| 157 | size_t max_chunk_size; |
| 158 | large_off_t max_core_size; |
| 159 | |
| 160 | /* Try to expand all the resource limits beyond the point of sanity |
| 161 | - we're after the biggest possible core file. */ |
| 162 | |
| 163 | print_string ("Maximize resource limits ...\n"); |
| 164 | #ifdef RLIMIT_CORE |
| 165 | maximize_rlimit (RLIMIT_CORE, "core"); |
| 166 | #endif |
| 167 | #ifdef RLIMIT_DATA |
| 168 | maximize_rlimit (RLIMIT_DATA, "data"); |
| 169 | #endif |
| 170 | #ifdef RLIMIT_STACK |
| 171 | maximize_rlimit (RLIMIT_STACK, "stack"); |
| 172 | #endif |
| 173 | #ifdef RLIMIT_AS |
| 174 | maximize_rlimit (RLIMIT_AS, "stack"); |
| 175 | #endif |
| 176 | |
| 177 | print_string ("Maximize allocation limits ...\n"); |
| 178 | |
| 179 | /* Compute the largest possible corefile size. No point in trying |
| 180 | to create a corefile larger than the largest file supported by |
| 181 | the file system. What about 64-bit lseek64? */ |
| 182 | { |
| 183 | int fd; |
| 184 | large_off_t tmp; |
| 185 | unlink ("bigcore.corefile"); |
| 186 | fd = open ("bigcore.corefile", O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, |
| 187 | 0666); |
| 188 | for (tmp = 1; tmp > 0; tmp <<= 1) |
| 189 | { |
| 190 | if (large_lseek (fd, tmp, SEEK_SET) > 0) |
| 191 | max_core_size = tmp; |
| 192 | } |
| 193 | close (fd); |
| 194 | } |
| 195 | |
| 196 | /* Compute an initial chunk size. The math is dodgy but it works |
| 197 | for the moment. Perhaphs there's a constant around somewhere. |
| 198 | Limit this to max_core_size bytes - no point in trying to |
| 199 | allocate more than can be written to the corefile. */ |
| 200 | { |
| 201 | size_t tmp; |
| 202 | for (tmp = 1; tmp > 0 && tmp < max_core_size; tmp <<= 1) |
| 203 | max_chunk_size = tmp; |
| 204 | } |
| 205 | |
| 206 | print_string (" core: "); |
| 207 | print_byte_count (max_core_size); |
| 208 | print_string ("\n"); |
| 209 | print_string (" chunk: "); |
| 210 | print_byte_count (max_chunk_size); |
| 211 | print_string ("\n"); |
| 212 | print_string (" large? "); |
| 213 | if (O_LARGEFILE) |
| 214 | print_string ("yes\n"); |
| 215 | else |
| 216 | print_string ("no\n"); |
| 217 | |
| 218 | /* Allocate as much memory as possible creating a linked list of |
| 219 | each section. The linking ensures that some, but not all, the |
| 220 | memory is allocated. NB: Some kernels handle this efficiently - |
| 221 | only allocating and writing out referenced pages leaving holes in |
| 222 | the file for unmodified pages - while others handle this poorly - |
| 223 | writing out all pages including those that weren't modified. */ |
| 224 | |
| 225 | print_string ("Alocating the entire heap ...\n"); |
| 226 | { |
| 227 | size_t chunk_size; |
| 228 | unsigned long chunks_allocated = 0; |
| 229 | /* Create a linked list of memory chunks. Start with |
| 230 | MAX_CHUNK_SIZE blocks of memory and then try allocating smaller |
| 231 | and smaller amounts until all (well at least most) memory has |
| 232 | been allocated. */ |
| 233 | for (chunk_size = max_chunk_size; |
| 234 | chunk_size >= sizeof (struct list); |
| 235 | chunk_size >>= 1) |
| 236 | { |
| 237 | unsigned long count = 0; |
| 238 | print_string (" "); |
| 239 | print_byte_count (chunk_size); |
| 240 | print_string (" ... "); |
| 241 | while (bytes_allocated + (1 + count) * chunk_size |
| 242 | < max_core_size) |
| 243 | { |
| 244 | struct list *chunk = malloc (chunk_size); |
| 245 | if (chunk == NULL) |
| 246 | break; |
| 247 | chunk->size = chunk_size; |
| 248 | /* Link it in. */ |
| 249 | chunk->next = NULL; |
| 250 | chunk->prev = heap.prev; |
| 251 | heap.prev->next = chunk; |
| 252 | heap.prev = chunk; |
| 253 | count++; |
| 254 | } |
| 255 | print_unsigned (count); |
| 256 | print_string (" chunks\n"); |
| 257 | chunks_allocated += count; |
| 258 | bytes_allocated += chunk_size * count; |
| 259 | } |
| 260 | print_string ("Total of "); |
| 261 | print_byte_count (bytes_allocated); |
| 262 | print_string (" bytes "); |
| 263 | print_unsigned (chunks_allocated); |
| 264 | print_string (" chunks\n"); |
| 265 | } |
| 266 | |
| 267 | /* Push everything out to disk. */ |
| 268 | |
| 269 | print_string ("Dump core ....\n"); |
| 270 | *(char*)0 = 0; |
| 271 | } |