Print this page
10654 savecore(1M) should be able to work on read-only dump devices
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: John Levon <john.levon@joyent.com>
Reviewed by: Andy Stormont <astormont@racktopsystems.com>
Reviewed by: Gergő Doma <domag02@gmail.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/savecore/savecore.c
+++ new/usr/src/cmd/savecore/savecore.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
23 - * Copyright 2016 Joyent, Inc.
23 + * Copyright 2019 Joyent, Inc.
24 24 */
25 25 /*
26 26 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
27 27 */
28 28
29 29 #include <stdio.h>
30 30 #include <stdlib.h>
31 31 #include <stdarg.h>
32 32 #include <unistd.h>
33 33 #include <fcntl.h>
34 34 #include <errno.h>
35 35 #include <string.h>
36 36 #include <deflt.h>
37 37 #include <time.h>
38 38 #include <syslog.h>
39 39 #include <stropts.h>
40 40 #include <pthread.h>
41 41 #include <limits.h>
42 42 #include <atomic.h>
43 43 #include <libnvpair.h>
44 44 #include <libintl.h>
45 45 #include <sys/mem.h>
46 46 #include <sys/statvfs.h>
47 47 #include <sys/dumphdr.h>
48 48 #include <sys/dumpadm.h>
49 49 #include <sys/compress.h>
50 50 #include <sys/panic.h>
51 51 #include <sys/sysmacros.h>
52 52 #include <sys/stat.h>
53 53 #include <sys/resource.h>
54 54 #include <bzip2/bzlib.h>
55 55 #include <sys/fm/util.h>
56 56 #include <fm/libfmevent.h>
57 57 #include <sys/int_fmtio.h>
58 58
59 59
60 60 /* fread/fwrite buffer size */
61 61 #define FBUFSIZE (1ULL << 20)
62 62
63 63 /* minimum size for output buffering */
64 64 #define MINCOREBLKSIZE (1ULL << 17)
65 65
66 66 /* create this file if metrics collection is enabled in the kernel */
67 67 #define METRICSFILE "METRICS.csv"
68 68
69 69 static char progname[9] = "savecore";
70 70 static char *savedir; /* savecore directory */
71 71 static char *dumpfile; /* source of raw crash dump */
72 72 static long bounds = -1; /* numeric suffix */
73 73 static long pagesize; /* dump pagesize */
74 74 static int dumpfd = -1; /* dumpfile descriptor */
75 75 static boolean_t have_dumpfile = B_TRUE; /* dumpfile existence */
76 76 static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */
77 77 static boolean_t dump_incomplete; /* dumphdr indicates incomplete */
78 78 static boolean_t fm_panic; /* dump is the result of fm_panic */
79 79 static offset_t endoff; /* offset of end-of-dump header */
80 80 static int verbose; /* chatty mode */
81 81 static int disregard_valid_flag; /* disregard valid flag */
82 82 static int livedump; /* dump the current running system */
83 83 static int interactive; /* user invoked; no syslog */
84 84 static int csave; /* save dump compressed */
↓ open down ↓ |
51 lines elided |
↑ open up ↑ |
85 85 static int filemode; /* processing file, not dump device */
86 86 static int percent_done; /* progress indicator */
87 87 static int sec_done; /* progress last report time */
88 88 static hrtime_t startts; /* timestamp at start */
89 89 static volatile uint64_t saved; /* count of pages written */
90 90 static volatile uint64_t zpages; /* count of zero pages not written */
91 91 static dumpdatahdr_t datahdr; /* compression info */
92 92 static long coreblksize; /* preferred write size (st_blksize) */
93 93 static int cflag; /* run as savecore -c */
94 94 static int mflag; /* run as savecore -m */
95 +static int rflag; /* run as savecore -r */
95 96
96 97 /*
97 98 * Payload information for the events we raise. These are used
98 99 * in raise_event to determine what payload to include.
99 100 */
100 101 #define SC_PAYLOAD_SAVEDIR 0x0001 /* Include savedir in event */
101 102 #define SC_PAYLOAD_INSTANCE 0x0002 /* Include bounds instance number */
102 103 #define SC_PAYLOAD_IMAGEUUID 0x0004 /* Include dump OS instance uuid */
103 104 #define SC_PAYLOAD_CRASHTIME 0x0008 /* Include epoch crashtime */
104 105 #define SC_PAYLOAD_PANICSTR 0x0010 /* Include panic string */
105 106 #define SC_PAYLOAD_PANICSTACK 0x0020 /* Include panic string */
106 107 #define SC_PAYLOAD_FAILREASON 0x0040 /* Include failure reason */
107 108 #define SC_PAYLOAD_DUMPCOMPLETE 0x0080 /* Include completeness indicator */
108 109 #define SC_PAYLOAD_ISCOMPRESSED 0x0100 /* Dump is in vmdump.N form */
109 110 #define SC_PAYLOAD_DUMPADM_EN 0x0200 /* Is dumpadm enabled or not? */
110 111 #define SC_PAYLOAD_FM_PANIC 0x0400 /* Panic initiated by FMA */
111 112 #define SC_PAYLOAD_JUSTCHECKING 0x0800 /* Run with -c flag? */
112 113
113 114 enum sc_event_type {
114 115 SC_EVENT_DUMP_PENDING,
115 116 SC_EVENT_SAVECORE_FAILURE,
116 117 SC_EVENT_DUMP_AVAILABLE
117 118 };
118 119
119 120 /*
120 121 * Common payload
121 122 */
122 123 #define _SC_PAYLOAD_CMN \
123 124 SC_PAYLOAD_IMAGEUUID | \
124 125 SC_PAYLOAD_CRASHTIME | \
125 126 SC_PAYLOAD_PANICSTR | \
126 127 SC_PAYLOAD_PANICSTACK | \
127 128 SC_PAYLOAD_DUMPCOMPLETE | \
128 129 SC_PAYLOAD_FM_PANIC | \
129 130 SC_PAYLOAD_SAVEDIR
130 131
131 132 static const struct {
132 133 const char *sce_subclass;
133 134 uint32_t sce_payload;
134 135 } sc_event[] = {
135 136 /*
136 137 * SC_EVENT_DUMP_PENDING
137 138 */
138 139 {
139 140 "dump_pending_on_device",
140 141 _SC_PAYLOAD_CMN | SC_PAYLOAD_DUMPADM_EN |
141 142 SC_PAYLOAD_JUSTCHECKING
142 143 },
143 144
144 145 /*
145 146 * SC_EVENT_SAVECORE_FAILURE
146 147 */
147 148 {
148 149 "savecore_failure",
149 150 _SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_FAILREASON
150 151 },
151 152
152 153 /*
153 154 * SC_EVENT_DUMP_AVAILABLE
154 155 */
155 156 {
156 157 "dump_available",
↓ open down ↓ |
52 lines elided |
↑ open up ↑ |
157 158 _SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_ISCOMPRESSED
158 159 },
159 160 };
160 161
161 162 static void raise_event(enum sc_event_type, char *);
162 163
163 164 static void
164 165 usage(void)
165 166 {
166 167 (void) fprintf(stderr,
167 - "usage: %s [-Lvd] [-f dumpfile] [dirname]\n", progname);
168 + "usage: %s [-L | -r] [-vd] [-f dumpfile] [dirname]\n", progname);
168 169 exit(1);
169 170 }
170 171
171 172 #define SC_SL_NONE 0x0001 /* no syslog */
172 173 #define SC_SL_ERR 0x0002 /* syslog if !interactive, LOG_ERR */
173 174 #define SC_SL_WARN 0x0004 /* syslog if !interactive, LOG_WARNING */
174 175 #define SC_IF_VERBOSE 0x0008 /* message only if -v */
175 176 #define SC_IF_ISATTY 0x0010 /* message only if interactive */
176 177 #define SC_EXIT_OK 0x0020 /* exit(0) */
177 178 #define SC_EXIT_ERR 0x0040 /* exit(1) */
178 179 #define SC_EXIT_PEND 0x0080 /* exit(2) */
179 180 #define SC_EXIT_FM 0x0100 /* exit(3) */
180 181
181 182 #define _SC_ALLEXIT (SC_EXIT_OK | SC_EXIT_ERR | SC_EXIT_PEND | SC_EXIT_FM)
182 183
183 184 static void
184 185 logprint(uint32_t flags, char *message, ...)
185 186 {
186 187 va_list args;
187 188 char buf[1024];
188 189 int do_always = ((flags & (SC_IF_VERBOSE | SC_IF_ISATTY)) == 0);
189 190 int do_ifverb = (flags & SC_IF_VERBOSE) && verbose;
190 191 int do_ifisatty = (flags & SC_IF_ISATTY) && interactive;
191 192 int code;
192 193 static int logprint_raised = 0;
193 194
194 195 if (do_always || do_ifverb || do_ifisatty) {
195 196 va_start(args, message);
196 197 /*LINTED: E_SEC_PRINTF_VAR_FMT*/
197 198 (void) vsnprintf(buf, sizeof (buf), message, args);
198 199 (void) fprintf(stderr, "%s: %s\n", progname, buf);
199 200 if (!interactive) {
200 201 switch (flags & (SC_SL_NONE | SC_SL_ERR | SC_SL_WARN)) {
201 202 case SC_SL_ERR:
202 203 /*LINTED: E_SEC_PRINTF_VAR_FMT*/
203 204 syslog(LOG_ERR, buf);
204 205 break;
205 206
206 207 case SC_SL_WARN:
207 208 /*LINTED: E_SEC_PRINTF_VAR_FMT*/
208 209 syslog(LOG_WARNING, buf);
209 210 break;
210 211
211 212 default:
212 213 break;
213 214 }
214 215 }
215 216 va_end(args);
216 217 }
217 218
218 219 switch (flags & _SC_ALLEXIT) {
219 220 case 0:
220 221 return;
221 222
↓ open down ↓ |
44 lines elided |
↑ open up ↑ |
222 223 case SC_EXIT_OK:
223 224 code = 0;
224 225 break;
225 226
226 227 case SC_EXIT_PEND:
227 228 /*
228 229 * Raise an ireport saying why we are exiting. Do not
229 230 * raise if run as savecore -m. If something in the
230 231 * raise_event codepath calls logprint avoid recursion.
231 232 */
232 - if (!mflag && logprint_raised++ == 0)
233 + if (!mflag && !rflag && logprint_raised++ == 0)
233 234 raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
234 235 code = 2;
235 236 break;
236 237
237 238 case SC_EXIT_FM:
238 239 code = 3;
239 240 break;
240 241
241 242 case SC_EXIT_ERR:
242 243 default:
243 - if (!mflag && logprint_raised++ == 0 && have_dumpfile)
244 + if (!mflag && !rflag && logprint_raised++ == 0 && have_dumpfile)
244 245 raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
245 246 code = 1;
246 247 break;
247 248 }
248 249
249 250 exit(code);
250 251 }
251 252
252 253 /*
253 254 * System call / libc wrappers that exit on error.
254 255 */
255 256 static int
256 257 Open(const char *name, int oflags, mode_t mode)
257 258 {
258 259 int fd;
259 260
260 261 if ((fd = open64(name, oflags, mode)) == -1)
261 262 logprint(SC_SL_ERR | SC_EXIT_ERR, "open(\"%s\"): %s",
262 263 name, strerror(errno));
263 264 return (fd);
264 265 }
265 266
266 267 static void
267 268 Fread(void *buf, size_t size, FILE *f)
268 269 {
269 270 if (fread(buf, size, 1, f) != 1)
270 271 logprint(SC_SL_ERR | SC_EXIT_ERR, "fread: %s",
271 272 strerror(errno));
272 273 }
273 274
274 275 static void
275 276 Fwrite(void *buf, size_t size, FILE *f)
276 277 {
277 278 if (fwrite(buf, size, 1, f) != 1)
278 279 logprint(SC_SL_ERR | SC_EXIT_ERR, "fwrite: %s",
279 280 strerror(errno));
280 281 }
281 282
282 283 static void
283 284 Fseek(offset_t off, FILE *f)
284 285 {
285 286 if (fseeko64(f, off, SEEK_SET) != 0)
286 287 logprint(SC_SL_ERR | SC_EXIT_ERR, "fseeko64: %s",
287 288 strerror(errno));
288 289 }
289 290
290 291 typedef struct stat64 Stat_t;
291 292
292 293 static void
293 294 Fstat(int fd, Stat_t *sb, const char *fname)
294 295 {
295 296 if (fstat64(fd, sb) != 0)
296 297 logprint(SC_SL_ERR | SC_EXIT_ERR, "fstat(\"%s\"): %s", fname,
297 298 strerror(errno));
298 299 }
299 300
300 301 static void
301 302 Stat(const char *fname, Stat_t *sb)
302 303 {
303 304 if (stat64(fname, sb) != 0) {
304 305 have_dumpfile = B_FALSE;
305 306 logprint(SC_SL_ERR | SC_EXIT_ERR, "failed to get status "
306 307 "of file %s", fname);
307 308 }
308 309 }
309 310
310 311 static void
311 312 Pread(int fd, void *buf, size_t size, offset_t off)
312 313 {
313 314 ssize_t sz = pread64(fd, buf, size, off);
314 315
315 316 if (sz < 0)
316 317 logprint(SC_SL_ERR | SC_EXIT_ERR,
317 318 "pread: %s", strerror(errno));
318 319 else if (sz != size)
319 320 logprint(SC_SL_ERR | SC_EXIT_ERR,
320 321 "pread: size %ld != %ld", sz, size);
321 322 }
322 323
323 324 static void
324 325 Pwrite(int fd, void *buf, size_t size, off64_t off)
325 326 {
326 327 if (pwrite64(fd, buf, size, off) != size)
327 328 logprint(SC_SL_ERR | SC_EXIT_ERR, "pwrite: %s",
328 329 strerror(errno));
329 330 }
330 331
331 332 static void *
332 333 Zalloc(size_t size)
333 334 {
334 335 void *buf;
335 336
336 337 if ((buf = calloc(size, 1)) == NULL)
337 338 logprint(SC_SL_ERR | SC_EXIT_ERR, "calloc: %s",
338 339 strerror(errno));
339 340 return (buf);
340 341 }
341 342
342 343 static long
343 344 read_number_from_file(const char *filename, long default_value)
344 345 {
345 346 long file_value = -1;
346 347 FILE *fp;
347 348
↓ open down ↓ |
94 lines elided |
↑ open up ↑ |
348 349 if ((fp = fopen(filename, "r")) != NULL) {
349 350 (void) fscanf(fp, "%ld", &file_value);
350 351 (void) fclose(fp);
351 352 }
352 353 return (file_value < 0 ? default_value : file_value);
353 354 }
354 355
355 356 static void
356 357 read_dumphdr(void)
357 358 {
358 - if (filemode)
359 + if (filemode || rflag)
359 360 dumpfd = Open(dumpfile, O_RDONLY, 0644);
360 361 else
361 362 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
362 363 endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET;
363 364 Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
364 365 Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr));
365 366
366 367 pagesize = dumphdr.dump_pagesize;
367 368
368 369 if (dumphdr.dump_magic != DUMP_MAGIC)
369 370 logprint(SC_SL_NONE | SC_EXIT_PEND, "bad magic number %x",
370 371 dumphdr.dump_magic);
371 372
372 373 if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag)
373 374 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK,
374 375 "dump already processed");
375 376
376 377 if (dumphdr.dump_version != DUMP_VERSION)
377 378 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
378 379 "dump version (%d) != %s version (%d)",
379 380 dumphdr.dump_version, progname, DUMP_VERSION);
380 381
381 382 if (dumphdr.dump_wordsize != DUMP_WORDSIZE)
382 383 logprint(SC_SL_NONE | SC_EXIT_PEND,
383 384 "dump is from %u-bit kernel - cannot save on %u-bit kernel",
384 385 dumphdr.dump_wordsize, DUMP_WORDSIZE);
385 386
386 387 if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) {
387 388 if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION)
388 389 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
389 390 "dump data version (%d) != %s data version (%d)",
390 391 datahdr.dump_datahdr_version, progname,
391 392 DUMP_DATAHDR_VERSION);
392 393 } else {
393 394 (void) memset(&datahdr, 0, sizeof (datahdr));
394 395 datahdr.dump_maxcsize = pagesize;
395 396 }
396 397
397 398 /*
398 399 * Read the initial header, clear the valid bits, and compare headers.
399 400 * The main header may have been overwritten by swapping if we're
400 401 * using a swap partition as the dump device, in which case we bail.
↓ open down ↓ |
32 lines elided |
↑ open up ↑ |
401 402 */
402 403 Pread(dumpfd, &corehdr, sizeof (dumphdr_t), dumphdr.dump_start);
403 404
404 405 corehdr.dump_flags &= ~DF_VALID;
405 406 dumphdr.dump_flags &= ~DF_VALID;
406 407
407 408 if (memcmp(&corehdr, &dumphdr, sizeof (dumphdr_t)) != 0) {
408 409 /*
409 410 * Clear valid bit so we don't complain on every invocation.
410 411 */
411 - if (!filemode)
412 + if (!filemode && !rflag)
412 413 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
413 414 logprint(SC_SL_ERR | SC_EXIT_ERR,
414 415 "initial dump header corrupt");
415 416 }
416 417 }
417 418
418 419 static void
419 420 check_space(int csave)
420 421 {
421 422 struct statvfs fsb;
422 423 int64_t spacefree, dumpsize, minfree, datasize;
423 424
424 425 if (statvfs(".", &fsb) < 0)
425 426 logprint(SC_SL_ERR | SC_EXIT_ERR, "statvfs: %s",
426 427 strerror(errno));
427 428
428 429 dumpsize = dumphdr.dump_data - dumphdr.dump_start;
429 430 datasize = dumphdr.dump_npages * pagesize;
430 431 if (!csave)
431 432 dumpsize += datasize;
432 433 else
433 434 dumpsize += datahdr.dump_data_csize;
434 435
435 436 spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize;
436 437 minfree = 1024LL * read_number_from_file("minfree", 1024);
437 438 if (spacefree < minfree + dumpsize) {
438 439 logprint(SC_SL_ERR | SC_EXIT_ERR,
439 440 "not enough space in %s (%lld MB avail, %lld MB needed)",
440 441 savedir, spacefree >> 20, (minfree + dumpsize) >> 20);
441 442 }
442 443 }
443 444
444 445 static void
445 446 build_dump_map(int corefd, const pfn_t *pfn_table)
446 447 {
447 448 long i;
448 449 static long misses = 0;
449 450 size_t dump_mapsize = (corehdr.dump_hashmask + 1) * sizeof (dump_map_t);
450 451 mem_vtop_t vtop;
451 452 dump_map_t *dmp = Zalloc(dump_mapsize);
452 453 char *inbuf = Zalloc(FBUFSIZE);
453 454 FILE *in = fdopen(dup(dumpfd), "rb");
454 455
455 456 (void) setvbuf(in, inbuf, _IOFBF, FBUFSIZE);
456 457 Fseek(dumphdr.dump_map, in);
457 458
458 459 corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize);
459 460
460 461 for (i = 0; i < corehdr.dump_nvtop; i++) {
461 462 long first = 0;
462 463 long last = corehdr.dump_npages - 1;
463 464 long middle = 0;
464 465 pfn_t pfn = 0;
465 466 uintptr_t h;
466 467
467 468 Fread(&vtop, sizeof (mem_vtop_t), in);
468 469 while (last >= first) {
469 470 middle = (first + last) / 2;
470 471 pfn = pfn_table[middle];
471 472 if (pfn == vtop.m_pfn)
472 473 break;
473 474 if (pfn < vtop.m_pfn)
474 475 first = middle + 1;
475 476 else
476 477 last = middle - 1;
477 478 }
478 479 if (pfn != vtop.m_pfn) {
479 480 if (++misses <= 10)
480 481 (void) fprintf(stderr,
481 482 "pfn %ld not found for as=%p, va=%p\n",
482 483 vtop.m_pfn, (void *)vtop.m_as, vtop.m_va);
483 484 continue;
484 485 }
485 486
486 487 dmp[i].dm_as = vtop.m_as;
487 488 dmp[i].dm_va = (uintptr_t)vtop.m_va;
488 489 dmp[i].dm_data = corehdr.dump_data +
489 490 ((uint64_t)middle << corehdr.dump_pageshift);
490 491
491 492 h = DUMP_HASH(&corehdr, dmp[i].dm_as, dmp[i].dm_va);
492 493 dmp[i].dm_next = dmp[h].dm_first;
493 494 dmp[h].dm_first = corehdr.dump_map + i * sizeof (dump_map_t);
494 495 }
495 496
496 497 Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map);
497 498 free(dmp);
498 499 (void) fclose(in);
499 500 free(inbuf);
500 501 }
501 502
502 503 /*
503 504 * Copy whole sections of the dump device to the file.
504 505 */
505 506 static void
506 507 Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf,
507 508 size_t sz)
508 509 {
509 510 size_t nr;
510 511 offset_t off = *offp;
511 512
512 513 while (nb > 0) {
513 514 nr = sz < nb ? sz : (size_t)nb;
514 515 Pread(dumpfd, buf, nr, dumpoff);
515 516 Pwrite(fd, buf, nr, off);
516 517 off += nr;
517 518 dumpoff += nr;
518 519 nb -= nr;
519 520 }
520 521 *offp = off;
521 522 }
522 523
523 524 /*
524 525 * Copy pages when the dump data header is missing.
525 526 * This supports older kernels with latest savecore.
526 527 */
527 528 static void
528 529 CopyPages(offset_t *offp, int fd, char *buf, size_t sz)
529 530 {
530 531 uint32_t csize;
531 532 FILE *in = fdopen(dup(dumpfd), "rb");
532 533 FILE *out = fdopen(dup(fd), "wb");
533 534 char *cbuf = Zalloc(pagesize);
534 535 char *outbuf = Zalloc(FBUFSIZE);
535 536 pgcnt_t np = dumphdr.dump_npages;
536 537
537 538 (void) setvbuf(out, outbuf, _IOFBF, FBUFSIZE);
538 539 (void) setvbuf(in, buf, _IOFBF, sz);
539 540 Fseek(dumphdr.dump_data, in);
540 541
541 542 Fseek(*offp, out);
542 543 while (np > 0) {
543 544 Fread(&csize, sizeof (uint32_t), in);
544 545 Fwrite(&csize, sizeof (uint32_t), out);
545 546 *offp += sizeof (uint32_t);
546 547 if (csize > pagesize || csize == 0) {
547 548 logprint(SC_SL_ERR,
548 549 "CopyPages: page %lu csize %d (0x%x) pagesize %d",
549 550 dumphdr.dump_npages - np, csize, csize,
550 551 pagesize);
551 552 break;
552 553 }
553 554 Fread(cbuf, csize, in);
554 555 Fwrite(cbuf, csize, out);
555 556 *offp += csize;
556 557 np--;
557 558 }
558 559 (void) fclose(in);
559 560 (void) fclose(out);
560 561 free(outbuf);
561 562 free(buf);
562 563 }
563 564
564 565 /*
565 566 * Concatenate dump contents into a new file.
566 567 * Update corehdr with new offsets.
567 568 */
568 569 static void
569 570 copy_crashfile(const char *corefile)
570 571 {
571 572 int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
572 573 size_t bufsz = FBUFSIZE;
573 574 char *inbuf = Zalloc(bufsz);
574 575 offset_t coreoff;
575 576 size_t nb;
576 577
577 578 logprint(SC_SL_ERR | SC_IF_VERBOSE,
578 579 "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
579 580
580 581 /*
581 582 * This dump file is still compressed
582 583 */
583 584 corehdr.dump_flags |= DF_COMPRESSED | DF_VALID;
584 585
585 586 /*
586 587 * Leave room for corehdr, it is updated and written last
587 588 */
588 589 corehdr.dump_start = 0;
589 590 coreoff = sizeof (corehdr);
590 591
591 592 /*
592 593 * Read in the compressed symbol table, copy it to corefile.
593 594 */
594 595 coreoff = roundup(coreoff, pagesize);
595 596 corehdr.dump_ksyms = coreoff;
596 597 Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd,
597 598 inbuf, bufsz);
598 599
599 600 /*
600 601 * Save the pfn table.
601 602 */
602 603 coreoff = roundup(coreoff, pagesize);
603 604 corehdr.dump_pfn = coreoff;
604 605 Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff,
605 606 corefd, inbuf, bufsz);
606 607
607 608 /*
608 609 * Save the dump map.
609 610 */
610 611 coreoff = roundup(coreoff, pagesize);
611 612 corehdr.dump_map = coreoff;
612 613 Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t),
613 614 &coreoff, corefd, inbuf, bufsz);
614 615
615 616 /*
616 617 * Save the data pages.
617 618 */
618 619 coreoff = roundup(coreoff, pagesize);
619 620 corehdr.dump_data = coreoff;
620 621 if (datahdr.dump_data_csize != 0)
621 622 Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff,
622 623 corefd, inbuf, bufsz);
623 624 else
624 625 CopyPages(&coreoff, corefd, inbuf, bufsz);
625 626
626 627 /*
627 628 * Now write the modified dump header to front and end of the copy.
628 629 * Make it look like a valid dump device.
629 630 *
630 631 * From dumphdr.h: Two headers are written out: one at the
631 632 * beginning of the dump, and the other at the very end of the
632 633 * dump device. The terminal header is at a known location
633 634 * (end of device) so we can always find it.
634 635 *
635 636 * Pad with zeros to each DUMP_OFFSET boundary.
636 637 */
637 638 (void) memset(inbuf, 0, DUMP_OFFSET);
638 639
639 640 nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1));
640 641 if (nb > 0) {
641 642 Pwrite(corefd, inbuf, nb, coreoff);
642 643 coreoff += nb;
643 644 }
644 645
645 646 Pwrite(corefd, &corehdr, sizeof (corehdr), coreoff);
646 647 coreoff += sizeof (corehdr);
647 648
648 649 Pwrite(corefd, &datahdr, sizeof (datahdr), coreoff);
649 650 coreoff += sizeof (datahdr);
650 651
651 652 nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1));
652 653 if (nb > 0) {
↓ open down ↓ |
231 lines elided |
↑ open up ↑ |
653 654 Pwrite(corefd, inbuf, nb, coreoff);
654 655 }
655 656
656 657 free(inbuf);
657 658 Pwrite(corefd, &corehdr, sizeof (corehdr), corehdr.dump_start);
658 659
659 660 /*
660 661 * Write out the modified dump header to the dump device.
661 662 * The dump device has been processed, so DF_VALID is clear.
662 663 */
663 - if (!filemode)
664 + if (!filemode && !rflag)
664 665 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
665 666
666 667 (void) close(corefd);
667 668 }
668 669
669 670 /*
670 671 * compressed streams
671 672 */
672 673 typedef struct blockhdr blockhdr_t;
673 674 typedef struct block block_t;
674 675
675 676 struct blockhdr {
676 677 block_t *head;
677 678 block_t *tail;
678 679 };
679 680
680 681 struct block {
681 682 block_t *next;
682 683 char *block;
683 684 int size;
684 685 };
685 686
686 687 typedef enum streamstate {
687 688 STREAMSTART,
688 689 STREAMPAGES
689 690 } streamstate_t;
690 691
691 692 typedef struct stream {
692 693 streamstate_t state;
693 694 int init;
694 695 int tag;
695 696 int bound;
696 697 int nout;
697 698 char *blkbuf;
698 699 blockhdr_t blocks;
699 700 pgcnt_t pagenum;
700 701 pgcnt_t curpage;
701 702 pgcnt_t npages;
702 703 pgcnt_t done;
703 704 bz_stream strm;
704 705 dumpcsize_t sc;
705 706 dumpstreamhdr_t sh;
706 707 } stream_t;
707 708
708 709 static stream_t *streams;
709 710 static stream_t *endstreams;
710 711
711 712 const int cs = sizeof (dumpcsize_t);
712 713
713 714 typedef struct tinfo {
714 715 pthread_t tid;
715 716 int corefd;
716 717 } tinfo_t;
717 718
718 719 static int threads_stop;
719 720 static int threads_active;
720 721 static tinfo_t *tinfo;
721 722 static tinfo_t *endtinfo;
722 723
723 724 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
724 725 static pthread_cond_t cvfree = PTHREAD_COND_INITIALIZER;
725 726 static pthread_cond_t cvwork = PTHREAD_COND_INITIALIZER;
726 727 static pthread_cond_t cvbarrier = PTHREAD_COND_INITIALIZER;
727 728
728 729 static blockhdr_t freeblocks;
729 730
730 731 static void
731 732 enqt(blockhdr_t *h, block_t *b)
732 733 {
733 734 b->next = NULL;
734 735 if (h->tail == NULL)
735 736 h->head = b;
736 737 else
737 738 h->tail->next = b;
738 739 h->tail = b;
739 740 }
740 741
741 742 static block_t *
742 743 deqh(blockhdr_t *h)
743 744 {
744 745 block_t *b = h->head;
745 746
746 747 if (b != NULL) {
747 748 h->head = b->next;
748 749 if (h->head == NULL)
749 750 h->tail = NULL;
750 751 }
751 752 return (b);
752 753 }
753 754
754 755 static void *runstreams(void *arg);
755 756
756 757 static void
757 758 initstreams(int corefd, int nstreams, int maxcsize)
758 759 {
759 760 int nthreads;
760 761 int nblocks;
761 762 int i;
762 763 block_t *b;
763 764 tinfo_t *t;
764 765
765 766 nthreads = sysconf(_SC_NPROCESSORS_ONLN);
766 767 if (nstreams < nthreads)
767 768 nthreads = nstreams;
768 769 if (nthreads < 1)
769 770 nthreads = 1;
770 771 nblocks = nthreads * 2;
771 772
772 773 tinfo = Zalloc(nthreads * sizeof (tinfo_t));
773 774 endtinfo = &tinfo[nthreads];
774 775
775 776 /* init streams */
776 777 streams = Zalloc(nstreams * sizeof (stream_t));
777 778 endstreams = &streams[nstreams];
778 779
779 780 /* init stream block buffers */
780 781 for (i = 0; i < nblocks; i++) {
781 782 b = Zalloc(sizeof (block_t));
782 783 b->block = Zalloc(maxcsize);
783 784 enqt(&freeblocks, b);
784 785 }
785 786
786 787 /* init worker threads */
787 788 (void) pthread_mutex_lock(&lock);
788 789 threads_active = 1;
789 790 threads_stop = 0;
790 791 for (t = tinfo; t != endtinfo; t++) {
791 792 t->corefd = dup(corefd);
792 793 if (t->corefd < 0) {
793 794 nthreads = t - tinfo;
794 795 endtinfo = t;
795 796 break;
796 797 }
797 798 if (pthread_create(&t->tid, NULL, runstreams, t) != 0)
798 799 logprint(SC_SL_ERR | SC_EXIT_ERR, "pthread_create: %s",
799 800 strerror(errno));
800 801 }
801 802 (void) pthread_mutex_unlock(&lock);
802 803 }
803 804
804 805 static void
805 806 sbarrier()
806 807 {
807 808 stream_t *s;
808 809
809 810 (void) pthread_mutex_lock(&lock);
810 811 for (s = streams; s != endstreams; s++) {
811 812 while (s->bound || s->blocks.head != NULL)
812 813 (void) pthread_cond_wait(&cvbarrier, &lock);
813 814 }
814 815 (void) pthread_mutex_unlock(&lock);
815 816 }
816 817
817 818 static void
818 819 stopstreams()
819 820 {
820 821 tinfo_t *t;
821 822
822 823 if (threads_active) {
823 824 sbarrier();
824 825 (void) pthread_mutex_lock(&lock);
825 826 threads_stop = 1;
826 827 (void) pthread_cond_signal(&cvwork);
827 828 (void) pthread_mutex_unlock(&lock);
828 829 for (t = tinfo; t != endtinfo; t++)
829 830 (void) pthread_join(t->tid, NULL);
830 831 free(tinfo);
831 832 tinfo = NULL;
832 833 threads_active = 0;
833 834 }
834 835 }
835 836
836 837 static block_t *
837 838 getfreeblock()
838 839 {
839 840 block_t *b;
840 841
841 842 (void) pthread_mutex_lock(&lock);
842 843 while ((b = deqh(&freeblocks)) == NULL)
843 844 (void) pthread_cond_wait(&cvfree, &lock);
844 845 (void) pthread_mutex_unlock(&lock);
845 846 return (b);
846 847 }
847 848
848 849 /* data page offset from page number */
849 850 #define BTOP(b) ((b) >> dumphdr.dump_pageshift)
850 851 #define PTOB(p) ((p) << dumphdr.dump_pageshift)
851 852 #define DATAOFF(p) (corehdr.dump_data + PTOB(p))
852 853
853 854 /* check for coreblksize boundary */
854 855 static int
855 856 isblkbnd(pgcnt_t pgnum)
856 857 {
857 858 return (P2PHASE(DATAOFF(pgnum), coreblksize) == 0);
858 859 }
859 860
860 861 static int
861 862 iszpage(char *buf)
862 863 {
863 864 size_t sz;
864 865 uint64_t *pl;
865 866
866 867 /*LINTED:E_BAD_PTR_CAST_ALIGN*/
867 868 pl = (uint64_t *)(buf);
868 869 for (sz = 0; sz < pagesize; sz += sizeof (*pl))
869 870 if (*pl++ != 0)
870 871 return (0);
871 872 return (1);
872 873 }
873 874
874 875 volatile uint_t *hist;
875 876
876 877 /* write pages to the core file */
877 878 static void
878 879 putpage(int corefd, char *buf, pgcnt_t pgnum, pgcnt_t np)
879 880 {
880 881 atomic_inc_uint(&hist[np]);
881 882 if (np > 0)
882 883 Pwrite(corefd, buf, PTOB(np), DATAOFF(pgnum));
883 884 }
884 885
885 886 /*
886 887 * Process one lzjb block.
887 888 * No object (stream header or page) will be split over a block boundary.
888 889 */
889 890 static void
890 891 lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz)
891 892 {
892 893 int in = 0;
893 894 int csize;
894 895 int doflush;
895 896 char *out;
896 897 size_t dsize;
897 898 dumpcsize_t sc;
898 899 dumpstreamhdr_t sh;
899 900
900 901 if (!s->init) {
901 902 s->init = 1;
902 903 if (s->blkbuf == NULL)
903 904 s->blkbuf = Zalloc(coreblksize);
904 905 s->state = STREAMSTART;
905 906 }
906 907 while (in < blocksz) {
907 908 switch (s->state) {
908 909 case STREAMSTART:
909 910 (void) memcpy(&sh, block + in, sizeof (sh));
910 911 in += sizeof (sh);
911 912 if (strcmp(DUMP_STREAM_MAGIC, sh.stream_magic) != 0)
912 913 logprint(SC_SL_ERR | SC_EXIT_ERR,
913 914 "LZJB STREAMSTART: bad stream header");
914 915 if (sh.stream_npages > datahdr.dump_maxrange)
915 916 logprint(SC_SL_ERR | SC_EXIT_ERR,
916 917 "LZJB STREAMSTART: bad range: %d > %d",
917 918 sh.stream_npages, datahdr.dump_maxrange);
918 919 s->pagenum = sh.stream_pagenum;
919 920 s->npages = sh.stream_npages;
920 921 s->curpage = s->pagenum;
921 922 s->nout = 0;
922 923 s->done = 0;
923 924 s->state = STREAMPAGES;
924 925 break;
925 926 case STREAMPAGES:
926 927 (void) memcpy(&sc, block + in, cs);
927 928 in += cs;
928 929 csize = DUMP_GET_CSIZE(sc);
929 930 if (csize > pagesize)
930 931 logprint(SC_SL_ERR | SC_EXIT_ERR,
931 932 "LZJB STREAMPAGES: bad csize=%d", csize);
932 933
933 934 out = s->blkbuf + PTOB(s->nout);
934 935 dsize = decompress(block + in, out, csize, pagesize);
935 936
936 937 if (dsize != pagesize)
937 938 logprint(SC_SL_ERR | SC_EXIT_ERR,
938 939 "LZJB STREAMPAGES: dsize %d != pagesize %d",
939 940 dsize, pagesize);
940 941
941 942 in += csize;
942 943 atomic_inc_64(&saved);
943 944
944 945 doflush = 0;
945 946 if (s->nout == 0 && iszpage(out)) {
946 947 doflush = 1;
947 948 atomic_inc_64(&zpages);
948 949 } else if (++s->nout >= BTOP(coreblksize) ||
949 950 isblkbnd(s->curpage + s->nout)) {
950 951 doflush = 1;
951 952 }
952 953 if (++s->done >= s->npages) {
953 954 s->state = STREAMSTART;
954 955 doflush = 1;
955 956 }
956 957 if (doflush) {
957 958 putpage(corefd, s->blkbuf, s->curpage, s->nout);
958 959 s->nout = 0;
959 960 s->curpage = s->pagenum + s->done;
960 961 }
961 962 break;
962 963 }
963 964 }
964 965 }
965 966
966 967 /* bzlib library reports errors with this callback */
967 968 void
968 969 bz_internal_error(int errcode)
969 970 {
970 971 logprint(SC_SL_ERR | SC_EXIT_ERR, "bz_internal_error: err %s\n",
971 972 BZ2_bzErrorString(errcode));
972 973 }
973 974
974 975 /*
975 976 * Return one object in the stream.
976 977 *
977 978 * An object (stream header or page) will likely span an input block
978 979 * of compression data. Return non-zero when an entire object has been
979 980 * retrieved from the stream.
980 981 */
981 982 static int
982 983 bz2decompress(stream_t *s, void *buf, size_t size)
983 984 {
984 985 int rc;
985 986
986 987 if (s->strm.avail_out == 0) {
987 988 s->strm.next_out = buf;
988 989 s->strm.avail_out = size;
989 990 }
990 991 while (s->strm.avail_in > 0) {
991 992 rc = BZ2_bzDecompress(&s->strm);
992 993 if (rc == BZ_STREAM_END) {
993 994 rc = BZ2_bzDecompressReset(&s->strm);
994 995 if (rc != BZ_OK)
995 996 logprint(SC_SL_ERR | SC_EXIT_ERR,
996 997 "BZ2_bzDecompressReset: %s",
997 998 BZ2_bzErrorString(rc));
998 999 continue;
999 1000 }
1000 1001
1001 1002 if (s->strm.avail_out == 0)
1002 1003 break;
1003 1004 }
1004 1005 return (s->strm.avail_out == 0);
1005 1006 }
1006 1007
1007 1008 /*
1008 1009 * Process one bzip2 block.
1009 1010 * The interface is documented here:
1010 1011 * http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html
1011 1012 */
1012 1013 static void
1013 1014 bz2block(int corefd, stream_t *s, char *block, size_t blocksz)
1014 1015 {
1015 1016 int rc = 0;
1016 1017 int doflush;
1017 1018 char *out;
1018 1019
1019 1020 if (!s->init) {
1020 1021 s->init = 1;
1021 1022 rc = BZ2_bzDecompressInit(&s->strm, 0, 0);
1022 1023 if (rc != BZ_OK)
1023 1024 logprint(SC_SL_ERR | SC_EXIT_ERR,
1024 1025 "BZ2_bzDecompressInit: %s", BZ2_bzErrorString(rc));
1025 1026 if (s->blkbuf == NULL)
1026 1027 s->blkbuf = Zalloc(coreblksize);
1027 1028 s->strm.avail_out = 0;
1028 1029 s->state = STREAMSTART;
1029 1030 }
1030 1031 s->strm.next_in = block;
1031 1032 s->strm.avail_in = blocksz;
1032 1033
1033 1034 while (s->strm.avail_in > 0) {
1034 1035 switch (s->state) {
1035 1036 case STREAMSTART:
1036 1037 if (!bz2decompress(s, &s->sh, sizeof (s->sh)))
1037 1038 return;
1038 1039 if (strcmp(DUMP_STREAM_MAGIC, s->sh.stream_magic) != 0)
1039 1040 logprint(SC_SL_ERR | SC_EXIT_ERR,
1040 1041 "BZ2 STREAMSTART: bad stream header");
1041 1042 if (s->sh.stream_npages > datahdr.dump_maxrange)
1042 1043 logprint(SC_SL_ERR | SC_EXIT_ERR,
1043 1044 "BZ2 STREAMSTART: bad range: %d > %d",
1044 1045 s->sh.stream_npages, datahdr.dump_maxrange);
1045 1046 s->pagenum = s->sh.stream_pagenum;
1046 1047 s->npages = s->sh.stream_npages;
1047 1048 s->curpage = s->pagenum;
1048 1049 s->nout = 0;
1049 1050 s->done = 0;
1050 1051 s->state = STREAMPAGES;
1051 1052 break;
1052 1053 case STREAMPAGES:
1053 1054 out = s->blkbuf + PTOB(s->nout);
1054 1055 if (!bz2decompress(s, out, pagesize))
1055 1056 return;
1056 1057
1057 1058 atomic_inc_64(&saved);
1058 1059
1059 1060 doflush = 0;
1060 1061 if (s->nout == 0 && iszpage(out)) {
1061 1062 doflush = 1;
1062 1063 atomic_inc_64(&zpages);
1063 1064 } else if (++s->nout >= BTOP(coreblksize) ||
1064 1065 isblkbnd(s->curpage + s->nout)) {
1065 1066 doflush = 1;
1066 1067 }
1067 1068 if (++s->done >= s->npages) {
1068 1069 s->state = STREAMSTART;
1069 1070 doflush = 1;
1070 1071 }
1071 1072 if (doflush) {
1072 1073 putpage(corefd, s->blkbuf, s->curpage, s->nout);
1073 1074 s->nout = 0;
1074 1075 s->curpage = s->pagenum + s->done;
1075 1076 }
1076 1077 break;
1077 1078 }
1078 1079 }
1079 1080 }
1080 1081
1081 1082 /* report progress */
1082 1083 static void
1083 1084 report_progress()
1084 1085 {
1085 1086 int sec, percent;
1086 1087
1087 1088 if (!interactive)
1088 1089 return;
1089 1090
1090 1091 percent = saved * 100LL / corehdr.dump_npages;
1091 1092 sec = (gethrtime() - startts) / NANOSEC;
1092 1093 if (percent > percent_done || sec > sec_done) {
1093 1094 (void) printf("\r%2d:%02d %3d%% done", sec / 60, sec % 60,
1094 1095 percent);
1095 1096 (void) fflush(stdout);
1096 1097 sec_done = sec;
1097 1098 percent_done = percent;
1098 1099 }
1099 1100 }
1100 1101
1101 1102 /* thread body */
1102 1103 static void *
1103 1104 runstreams(void *arg)
1104 1105 {
1105 1106 tinfo_t *t = arg;
1106 1107 stream_t *s;
1107 1108 block_t *b;
1108 1109 int bound;
1109 1110
1110 1111 (void) pthread_mutex_lock(&lock);
1111 1112 while (!threads_stop) {
1112 1113 bound = 0;
1113 1114 for (s = streams; s != endstreams; s++) {
1114 1115 if (s->bound || s->blocks.head == NULL)
1115 1116 continue;
1116 1117 s->bound = 1;
1117 1118 bound = 1;
1118 1119 (void) pthread_cond_signal(&cvwork);
1119 1120 while (s->blocks.head != NULL) {
1120 1121 b = deqh(&s->blocks);
1121 1122 (void) pthread_mutex_unlock(&lock);
1122 1123
1123 1124 if (datahdr.dump_clevel < DUMP_CLEVEL_BZIP2)
1124 1125 lzjbblock(t->corefd, s, b->block,
1125 1126 b->size);
1126 1127 else
1127 1128 bz2block(t->corefd, s, b->block,
1128 1129 b->size);
1129 1130
1130 1131 (void) pthread_mutex_lock(&lock);
1131 1132 enqt(&freeblocks, b);
1132 1133 (void) pthread_cond_signal(&cvfree);
1133 1134
1134 1135 report_progress();
1135 1136 }
1136 1137 s->bound = 0;
1137 1138 (void) pthread_cond_signal(&cvbarrier);
1138 1139 }
1139 1140 if (!bound && !threads_stop)
1140 1141 (void) pthread_cond_wait(&cvwork, &lock);
1141 1142 }
1142 1143 (void) close(t->corefd);
1143 1144 (void) pthread_cond_signal(&cvwork);
1144 1145 (void) pthread_mutex_unlock(&lock);
1145 1146 return (arg);
1146 1147 }
1147 1148
1148 1149 /*
1149 1150 * Process compressed pages.
1150 1151 *
1151 1152 * The old format, now called single-threaded lzjb, is a 32-bit size
1152 1153 * word followed by 'size' bytes of lzjb compression data for one
1153 1154 * page. The new format extends this by storing a 12-bit "tag" in the
1154 1155 * upper bits of the size word. When the size word is pagesize or
1155 1156 * less, it is assumed to be one lzjb page. When the size word is
1156 1157 * greater than pagesize, it is assumed to be a "stream block",
1157 1158 * belonging to up to 4095 streams. In practice, the number of streams
1158 1159 * is set to one less than the number of CPUs running at crash
1159 1160 * time. One CPU processes the crash dump, the remaining CPUs
1160 1161 * separately process groups of data pages.
1161 1162 *
1162 1163 * savecore creates a thread per stream, but never more threads than
1163 1164 * the number of CPUs running savecore. This is because savecore can
1164 1165 * be processing a crash file from a remote machine, which may have
1165 1166 * more CPUs.
1166 1167 *
1167 1168 * When the kernel uses parallel lzjb or parallel bzip2, we expect a
1168 1169 * series of 128KB blocks of compression data. In this case, each
1169 1170 * block has a "tag", in the range 1-4095. Each block is handed off to
1170 1171 * to the threads running "runstreams". The dump format is either lzjb
1171 1172 * or bzip2, never a mixture. These threads, in turn, process the
1172 1173 * compression data for groups of pages. Groups of pages are delimited
1173 1174 * by a "stream header", which indicates a starting pfn and number of
1174 1175 * pages. When a stream block has been read, the condition variable
1175 1176 * "cvwork" is signalled, which causes one of the avaiable threads to
1176 1177 * wake up and process the stream.
1177 1178 *
1178 1179 * In the parallel case there will be streams blocks encoding all data
1179 1180 * pages. The stream of blocks is terminated by a zero size
1180 1181 * word. There can be a few lzjb pages tacked on the end, depending on
1181 1182 * the architecture. The sbarrier function ensures that all stream
1182 1183 * blocks have been processed so that the page number for the few
1183 1184 * single pages at the end can be known.
1184 1185 */
1185 1186 static void
1186 1187 decompress_pages(int corefd)
1187 1188 {
1188 1189 char *cpage = NULL;
1189 1190 char *dpage = NULL;
1190 1191 char *out;
1191 1192 pgcnt_t curpage = 0;
1192 1193 block_t *b;
1193 1194 FILE *dumpf;
1194 1195 FILE *tracef = NULL;
1195 1196 stream_t *s;
1196 1197 size_t dsize;
1197 1198 size_t insz = FBUFSIZE;
1198 1199 char *inbuf = Zalloc(insz);
1199 1200 uint32_t csize;
1200 1201 dumpcsize_t dcsize;
1201 1202 int nstreams = datahdr.dump_nstreams;
1202 1203 int maxcsize = datahdr.dump_maxcsize;
1203 1204 int nout = 0, tag, doflush;
1204 1205
1205 1206 dumpf = fdopen(dup(dumpfd), "rb");
1206 1207 if (dumpf == NULL)
1207 1208 logprint(SC_SL_ERR | SC_EXIT_ERR, "fdopen: %s",
1208 1209 strerror(errno));
1209 1210
1210 1211 (void) setvbuf(dumpf, inbuf, _IOFBF, insz);
1211 1212 Fseek(dumphdr.dump_data, dumpf);
1212 1213
1213 1214 /*LINTED: E_CONSTANT_CONDITION*/
1214 1215 while (1) {
1215 1216
1216 1217 /*
1217 1218 * The csize word delimits stream blocks.
1218 1219 * See dumphdr.h for a description.
1219 1220 */
1220 1221 Fread(&dcsize, sizeof (dcsize), dumpf);
1221 1222
1222 1223 tag = DUMP_GET_TAG(dcsize);
1223 1224 csize = DUMP_GET_CSIZE(dcsize);
1224 1225
1225 1226 if (tag != 0) { /* a stream block */
1226 1227
1227 1228 if (nstreams == 0)
1228 1229 logprint(SC_SL_ERR | SC_EXIT_ERR,
1229 1230 "starting data header is missing");
1230 1231
1231 1232 if (tag > nstreams)
1232 1233 logprint(SC_SL_ERR | SC_EXIT_ERR,
1233 1234 "stream tag %d not in range 1..%d",
1234 1235 tag, nstreams);
1235 1236
1236 1237 if (csize > maxcsize)
1237 1238 logprint(SC_SL_ERR | SC_EXIT_ERR,
1238 1239 "block size 0x%x > max csize 0x%x",
1239 1240 csize, maxcsize);
1240 1241
1241 1242 if (streams == NULL)
1242 1243 initstreams(corefd, nstreams, maxcsize);
1243 1244 s = &streams[tag - 1];
1244 1245 s->tag = tag;
1245 1246
1246 1247 b = getfreeblock();
1247 1248 b->size = csize;
1248 1249 Fread(b->block, csize, dumpf);
1249 1250
1250 1251 (void) pthread_mutex_lock(&lock);
1251 1252 enqt(&s->blocks, b);
1252 1253 if (!s->bound)
1253 1254 (void) pthread_cond_signal(&cvwork);
1254 1255 (void) pthread_mutex_unlock(&lock);
1255 1256
1256 1257 } else if (csize > 0) { /* one lzjb page */
1257 1258
1258 1259 if (csize > pagesize)
1259 1260 logprint(SC_SL_ERR | SC_EXIT_ERR,
1260 1261 "csize 0x%x > pagesize 0x%x",
1261 1262 csize, pagesize);
1262 1263
1263 1264 if (cpage == NULL)
1264 1265 cpage = Zalloc(pagesize);
1265 1266 if (dpage == NULL) {
1266 1267 dpage = Zalloc(coreblksize);
1267 1268 nout = 0;
1268 1269 }
1269 1270
1270 1271 Fread(cpage, csize, dumpf);
1271 1272
1272 1273 out = dpage + PTOB(nout);
1273 1274 dsize = decompress(cpage, out, csize, pagesize);
1274 1275
1275 1276 if (dsize != pagesize)
1276 1277 logprint(SC_SL_ERR | SC_EXIT_ERR,
1277 1278 "dsize 0x%x != pagesize 0x%x",
1278 1279 dsize, pagesize);
1279 1280
1280 1281 /*
1281 1282 * wait for streams to flush so that 'saved' is correct
1282 1283 */
1283 1284 if (threads_active)
1284 1285 sbarrier();
1285 1286
1286 1287 doflush = 0;
1287 1288 if (nout == 0)
1288 1289 curpage = saved;
1289 1290
1290 1291 atomic_inc_64(&saved);
1291 1292
1292 1293 if (nout == 0 && iszpage(dpage)) {
1293 1294 doflush = 1;
1294 1295 atomic_inc_64(&zpages);
1295 1296 } else if (++nout >= BTOP(coreblksize) ||
1296 1297 isblkbnd(curpage + nout) ||
1297 1298 saved >= dumphdr.dump_npages) {
1298 1299 doflush = 1;
1299 1300 }
1300 1301
1301 1302 if (doflush) {
1302 1303 putpage(corefd, dpage, curpage, nout);
1303 1304 nout = 0;
1304 1305 }
1305 1306
1306 1307 report_progress();
1307 1308
1308 1309 /*
1309 1310 * Non-streams lzjb does not use blocks. Stop
1310 1311 * here if all the pages have been decompressed.
1311 1312 */
1312 1313 if (saved >= dumphdr.dump_npages)
1313 1314 break;
1314 1315
1315 1316 } else {
1316 1317 break; /* end of data */
1317 1318 }
1318 1319 }
1319 1320
1320 1321 stopstreams();
1321 1322 if (tracef != NULL)
1322 1323 (void) fclose(tracef);
1323 1324 (void) fclose(dumpf);
1324 1325 if (inbuf)
1325 1326 free(inbuf);
1326 1327 if (cpage)
1327 1328 free(cpage);
1328 1329 if (dpage)
1329 1330 free(dpage);
1330 1331 if (streams)
1331 1332 free(streams);
1332 1333 }
1333 1334
1334 1335 static void
1335 1336 build_corefile(const char *namelist, const char *corefile)
1336 1337 {
1337 1338 size_t pfn_table_size = dumphdr.dump_npages * sizeof (pfn_t);
1338 1339 size_t ksyms_size = dumphdr.dump_ksyms_size;
1339 1340 size_t ksyms_csize = dumphdr.dump_ksyms_csize;
1340 1341 pfn_t *pfn_table;
1341 1342 char *ksyms_base = Zalloc(ksyms_size);
1342 1343 char *ksyms_cbase = Zalloc(ksyms_csize);
1343 1344 size_t ksyms_dsize;
1344 1345 Stat_t st;
1345 1346 int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1346 1347 int namefd = Open(namelist, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1347 1348
1348 1349 (void) printf("Constructing namelist %s/%s\n", savedir, namelist);
1349 1350
1350 1351 /*
1351 1352 * Determine the optimum write size for the core file
1352 1353 */
1353 1354 Fstat(corefd, &st, corefile);
1354 1355
1355 1356 if (verbose > 1)
1356 1357 (void) printf("%s: %ld block size\n", corefile,
1357 1358 (long)st.st_blksize);
1358 1359 coreblksize = st.st_blksize;
1359 1360 if (coreblksize < MINCOREBLKSIZE || !ISP2(coreblksize))
1360 1361 coreblksize = MINCOREBLKSIZE;
1361 1362
1362 1363 hist = Zalloc((sizeof (uint64_t) * BTOP(coreblksize)) + 1);
1363 1364
1364 1365 /*
1365 1366 * This dump file is now uncompressed
1366 1367 */
1367 1368 corehdr.dump_flags &= ~DF_COMPRESSED;
1368 1369
1369 1370 /*
1370 1371 * Read in the compressed symbol table, copy it to corefile,
1371 1372 * decompress it, and write the result to namelist.
1372 1373 */
1373 1374 corehdr.dump_ksyms = pagesize;
1374 1375 Pread(dumpfd, ksyms_cbase, ksyms_csize, dumphdr.dump_ksyms);
1375 1376 Pwrite(corefd, ksyms_cbase, ksyms_csize, corehdr.dump_ksyms);
1376 1377
1377 1378 ksyms_dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize,
1378 1379 ksyms_size);
1379 1380 if (ksyms_dsize != ksyms_size)
1380 1381 logprint(SC_SL_WARN,
1381 1382 "bad data in symbol table, %lu of %lu bytes saved",
1382 1383 ksyms_dsize, ksyms_size);
1383 1384
1384 1385 Pwrite(namefd, ksyms_base, ksyms_size, 0);
1385 1386 (void) close(namefd);
1386 1387 free(ksyms_cbase);
1387 1388 free(ksyms_base);
1388 1389
1389 1390 (void) printf("Constructing corefile %s/%s\n", savedir, corefile);
1390 1391
1391 1392 /*
1392 1393 * Read in and write out the pfn table.
1393 1394 */
1394 1395 pfn_table = Zalloc(pfn_table_size);
1395 1396 corehdr.dump_pfn = corehdr.dump_ksyms + roundup(ksyms_size, pagesize);
1396 1397 Pread(dumpfd, pfn_table, pfn_table_size, dumphdr.dump_pfn);
1397 1398 Pwrite(corefd, pfn_table, pfn_table_size, corehdr.dump_pfn);
1398 1399
1399 1400 /*
1400 1401 * Convert the raw translation data into a hashed dump map.
1401 1402 */
1402 1403 corehdr.dump_map = corehdr.dump_pfn + roundup(pfn_table_size, pagesize);
1403 1404 build_dump_map(corefd, pfn_table);
1404 1405 free(pfn_table);
1405 1406
1406 1407 /*
1407 1408 * Decompress the pages
1408 1409 */
1409 1410 decompress_pages(corefd);
1410 1411 (void) printf(": %ld of %ld pages saved\n", (pgcnt_t)saved,
1411 1412 dumphdr.dump_npages);
1412 1413
1413 1414 if (verbose)
1414 1415 (void) printf("%ld (%ld%%) zero pages were not written\n",
↓ open down ↓ |
741 lines elided |
↑ open up ↑ |
1415 1416 (pgcnt_t)zpages, (pgcnt_t)zpages * 100 /
1416 1417 dumphdr.dump_npages);
1417 1418
1418 1419 if (saved != dumphdr.dump_npages)
1419 1420 logprint(SC_SL_WARN, "bad data after page %ld", saved);
1420 1421
1421 1422 /*
1422 1423 * Write out the modified dump headers.
1423 1424 */
1424 1425 Pwrite(corefd, &corehdr, sizeof (corehdr), 0);
1425 - if (!filemode)
1426 + if (!filemode && !rflag)
1426 1427 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
1427 1428
1428 1429 (void) close(corefd);
1429 1430 }
1430 1431
1431 1432 /*
1432 1433 * When the system panics, the kernel saves all undelivered messages (messages
1433 1434 * that never made it out to syslogd(1M)) in the dump. At a mimimum, the
1434 1435 * panic message itself will always fall into this category. Upon reboot,
1435 1436 * the syslog startup script runs savecore -m to recover these messages.
1436 1437 *
1437 1438 * To do this, we read the unsent messages from the dump and send them to
1438 1439 * /dev/conslog on priority band 1. This has the effect of prepending them
1439 1440 * to any already-accumulated messages in the console backlog, thus preserving
1440 1441 * temporal ordering across the reboot.
1441 1442 *
1442 1443 * Note: since savecore -m is used *only* for this purpose, it does *not*
1443 1444 * attempt to save the crash dump. The dump will be saved later, after
1444 1445 * syslogd(1M) starts, by the savecore startup script.
1445 1446 */
1446 1447 static int
1447 1448 message_save(void)
1448 1449 {
1449 1450 offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE);
1450 1451 offset_t ldoff;
1451 1452 log_dump_t ld;
1452 1453 log_ctl_t lc;
1453 1454 struct strbuf ctl, dat;
1454 1455 int logfd;
1455 1456
1456 1457 logfd = Open("/dev/conslog", O_WRONLY, 0644);
1457 1458 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
1458 1459 dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
1459 1460
1460 1461 ctl.buf = (void *)&lc;
1461 1462 ctl.len = sizeof (log_ctl_t);
1462 1463
1463 1464 dat.buf = Zalloc(DUMP_LOGSIZE);
1464 1465
1465 1466 for (;;) {
1466 1467 ldoff = dumpoff;
1467 1468
1468 1469 Pread(dumpfd, &ld, sizeof (log_dump_t), dumpoff);
1469 1470 dumpoff += sizeof (log_dump_t);
1470 1471 dat.len = ld.ld_msgsize;
1471 1472
1472 1473 if (ld.ld_magic == 0)
1473 1474 break;
1474 1475
1475 1476 if (ld.ld_magic != LOG_MAGIC)
1476 1477 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
1477 1478 "bad magic %x", ld.ld_magic);
1478 1479
1479 1480 if (dat.len >= DUMP_LOGSIZE)
1480 1481 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
1481 1482 "bad size %d", ld.ld_msgsize);
1482 1483
1483 1484 Pread(dumpfd, ctl.buf, ctl.len, dumpoff);
1484 1485 dumpoff += ctl.len;
1485 1486
1486 1487 if (ld.ld_csum != checksum32(ctl.buf, ctl.len))
1487 1488 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
1488 1489 "bad log_ctl checksum");
1489 1490
1490 1491 lc.flags |= SL_LOGONLY;
1491 1492
1492 1493 Pread(dumpfd, dat.buf, dat.len, dumpoff);
1493 1494 dumpoff += dat.len;
1494 1495
1495 1496 if (ld.ld_msum != checksum32(dat.buf, dat.len))
1496 1497 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
1497 1498 "bad message checksum");
1498 1499
1499 1500 if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1)
1500 1501 logprint(SC_SL_ERR | SC_EXIT_ERR, "putpmsg: %s",
1501 1502 strerror(errno));
1502 1503
1503 1504 ld.ld_magic = 0; /* clear magic so we never save twice */
1504 1505 Pwrite(dumpfd, &ld, sizeof (log_dump_t), ldoff);
1505 1506 }
1506 1507 return (0);
1507 1508 }
1508 1509
1509 1510 static long
1510 1511 getbounds(const char *f)
1511 1512 {
1512 1513 long b = -1;
1513 1514 const char *p = strrchr(f, '/');
1514 1515
1515 1516 if (p == NULL || strncmp(p, "vmdump", 6) != 0)
1516 1517 p = strstr(f, "vmdump");
1517 1518
1518 1519 if (p != NULL && *p == '/')
1519 1520 p++;
1520 1521
1521 1522 (void) sscanf(p ? p : f, "vmdump.%ld", &b);
1522 1523
1523 1524 return (b);
↓ open down ↓ |
88 lines elided |
↑ open up ↑ |
1524 1525 }
1525 1526
1526 1527 static void
1527 1528 stack_retrieve(char *stack)
1528 1529 {
1529 1530 summary_dump_t sd;
1530 1531 offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE +
1531 1532 DUMP_ERPTSIZE);
1532 1533 dumpoff -= DUMP_SUMMARYSIZE;
1533 1534
1534 - dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
1535 + if (rflag)
1536 + dumpfd = Open(dumpfile, O_RDONLY, 0644);
1537 + else
1538 + dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
1535 1539 dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
1536 1540
1537 1541 Pread(dumpfd, &sd, sizeof (summary_dump_t), dumpoff);
1538 1542 dumpoff += sizeof (summary_dump_t);
1539 1543
1540 1544 if (sd.sd_magic == 0) {
1541 1545 *stack = '\0';
1542 1546 return;
1543 1547 }
1544 1548
1545 1549 if (sd.sd_magic != SUMMARY_MAGIC) {
1546 1550 *stack = '\0';
1547 1551 logprint(SC_SL_NONE | SC_IF_VERBOSE,
1548 1552 "bad summary magic %x", sd.sd_magic);
1549 1553 return;
1550 1554 }
1551 1555 Pread(dumpfd, stack, STACK_BUF_SIZE, dumpoff);
1552 1556 if (sd.sd_ssum != checksum32(stack, STACK_BUF_SIZE))
1553 1557 logprint(SC_SL_NONE | SC_IF_VERBOSE, "bad stack checksum");
1554 1558 }
1555 1559
1556 1560 static void
1557 1561 raise_event(enum sc_event_type evidx, char *warn_string)
1558 1562 {
1559 1563 uint32_t pl = sc_event[evidx].sce_payload;
1560 1564 char panic_stack[STACK_BUF_SIZE];
1561 1565 nvlist_t *attr = NULL;
1562 1566 char uuidbuf[36 + 1];
1563 1567 int err = 0;
1564 1568
1565 1569 if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0)
1566 1570 goto publish; /* try to send payload-free event */
1567 1571
1568 1572 if (pl & SC_PAYLOAD_SAVEDIR && savedir != NULL)
1569 1573 err |= nvlist_add_string(attr, "dumpdir", savedir);
1570 1574
1571 1575 if (pl & SC_PAYLOAD_INSTANCE && bounds != -1)
1572 1576 err |= nvlist_add_int64(attr, "instance", bounds);
1573 1577
1574 1578 if (pl & SC_PAYLOAD_ISCOMPRESSED) {
1575 1579 err |= nvlist_add_boolean_value(attr, "compressed",
1576 1580 csave ? B_TRUE : B_FALSE);
1577 1581 }
1578 1582
1579 1583 if (pl & SC_PAYLOAD_DUMPADM_EN) {
1580 1584 char *disabled = defread("DUMPADM_ENABLE=no");
1581 1585
1582 1586 err |= nvlist_add_boolean_value(attr, "savecore-enabled",
1583 1587 disabled ? B_FALSE : B_TRUE);
1584 1588 }
1585 1589
1586 1590 if (pl & SC_PAYLOAD_IMAGEUUID) {
1587 1591 (void) strncpy(uuidbuf, corehdr.dump_uuid, 36);
1588 1592 uuidbuf[36] = '\0';
1589 1593 err |= nvlist_add_string(attr, "os-instance-uuid", uuidbuf);
1590 1594 }
1591 1595
1592 1596 if (pl & SC_PAYLOAD_CRASHTIME) {
1593 1597 err |= nvlist_add_int64(attr, "crashtime",
1594 1598 (int64_t)corehdr.dump_crashtime);
1595 1599 }
1596 1600
1597 1601 if (pl & SC_PAYLOAD_PANICSTR && corehdr.dump_panicstring[0] != '\0') {
1598 1602 err |= nvlist_add_string(attr, "panicstr",
1599 1603 corehdr.dump_panicstring);
1600 1604 }
1601 1605
1602 1606 if (pl & SC_PAYLOAD_PANICSTACK) {
1603 1607 stack_retrieve(panic_stack);
1604 1608
1605 1609 if (panic_stack[0] != '\0') {
1606 1610 /*
1607 1611 * The summary page may not be present if the dump
1608 1612 * was previously recorded compressed.
1609 1613 */
1610 1614 (void) nvlist_add_string(attr, "panicstack",
1611 1615 panic_stack);
1612 1616 }
1613 1617 }
1614 1618
1615 1619 /* add warning string if this is an ireport for dump failure */
1616 1620 if (pl & SC_PAYLOAD_FAILREASON && warn_string != NULL)
1617 1621 (void) nvlist_add_string(attr, "failure-reason", warn_string);
1618 1622
1619 1623 if (pl & SC_PAYLOAD_DUMPCOMPLETE)
1620 1624 err |= nvlist_add_boolean_value(attr, "dump-incomplete",
1621 1625 dump_incomplete ? B_TRUE : B_FALSE);
1622 1626
1623 1627 if (pl & SC_PAYLOAD_FM_PANIC) {
1624 1628 err |= nvlist_add_boolean_value(attr, "fm-panic",
1625 1629 fm_panic ? B_TRUE : B_FALSE);
1626 1630 }
1627 1631
1628 1632 if (pl & SC_PAYLOAD_JUSTCHECKING) {
1629 1633 err |= nvlist_add_boolean_value(attr, "will-attempt-savecore",
1630 1634 cflag ? B_FALSE : B_TRUE);
1631 1635 }
1632 1636
1633 1637 if (err)
1634 1638 logprint(SC_SL_WARN, "Errors while constructing '%s' "
1635 1639 "event payload; will try to publish anyway.");
1636 1640 publish:
1637 1641 if (fmev_rspublish_nvl(FMEV_RULESET_ON_SUNOS,
1638 1642 "panic", sc_event[evidx].sce_subclass, FMEV_HIPRI,
1639 1643 attr) != FMEV_SUCCESS) {
1640 1644 logprint(SC_SL_ERR, "failed to publish '%s' event: %s",
1641 1645 sc_event[evidx].sce_subclass, fmev_strerror(fmev_errno));
1642 1646 nvlist_free(attr);
1643 1647 }
1644 1648
1645 1649 }
1646 1650
1647 1651
1648 1652 int
1649 1653 main(int argc, char *argv[])
1650 1654 {
1651 1655 int i, c, bfd;
1652 1656 Stat_t st;
1653 1657 struct rlimit rl;
1654 1658 long filebounds = -1;
1655 1659 char namelist[30], corefile[30], boundstr[30];
1656 1660 dumpfile = NULL;
1657 1661
1658 1662 startts = gethrtime();
1659 1663
1660 1664 (void) getrlimit(RLIMIT_NOFILE, &rl);
↓ open down ↓ |
116 lines elided |
↑ open up ↑ |
1661 1665 rl.rlim_cur = rl.rlim_max;
1662 1666 (void) setrlimit(RLIMIT_NOFILE, &rl);
1663 1667
1664 1668 openlog(progname, LOG_ODELAY, LOG_AUTH);
1665 1669
1666 1670 (void) defopen("/etc/dumpadm.conf");
1667 1671 savedir = defread("DUMPADM_SAVDIR=");
1668 1672 if (savedir != NULL)
1669 1673 savedir = strdup(savedir);
1670 1674
1671 - while ((c = getopt(argc, argv, "Lvcdmf:")) != EOF) {
1675 + while ((c = getopt(argc, argv, "Lvcdmf:r")) != EOF) {
1672 1676 switch (c) {
1673 1677 case 'L':
1674 1678 livedump++;
1675 1679 break;
1676 1680 case 'v':
1677 1681 verbose++;
1678 1682 break;
1679 1683 case 'c':
1680 1684 cflag++;
1681 1685 break;
1682 1686 case 'd':
1683 1687 disregard_valid_flag++;
1684 1688 break;
1685 1689 case 'm':
1686 1690 mflag++;
1687 1691 break;
1692 + case 'r':
1693 + rflag++;
1694 + break;
1688 1695 case 'f':
1689 1696 dumpfile = optarg;
1690 1697 filebounds = getbounds(dumpfile);
1691 1698 break;
1692 1699 case '?':
1693 1700 usage();
1694 1701 }
1695 1702 }
1696 1703
1697 1704 /*
1698 1705 * If doing something other than extracting an existing dump (i.e.
1699 1706 * dumpfile has been provided as an option), the user must be root.
1700 1707 */
1701 1708 if (geteuid() != 0 && dumpfile == NULL) {
↓ open down ↓ |
4 lines elided |
↑ open up ↑ |
1702 1709 (void) fprintf(stderr, "%s: %s %s\n", progname,
1703 1710 gettext("you must be root to use"), progname);
1704 1711 exit(1);
1705 1712 }
1706 1713
1707 1714 interactive = isatty(STDOUT_FILENO);
1708 1715
1709 1716 if (cflag && livedump)
1710 1717 usage();
1711 1718
1719 + if (rflag && (cflag || mflag || livedump))
1720 + usage();
1721 +
1712 1722 if (dumpfile == NULL || livedump)
1713 1723 dumpfd = Open("/dev/dump", O_RDONLY, 0444);
1714 1724
1715 1725 if (dumpfile == NULL) {
1716 1726 dumpfile = Zalloc(MAXPATHLEN);
1717 1727 if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) {
1718 1728 have_dumpfile = B_FALSE;
1719 1729 logprint(SC_SL_NONE | SC_IF_ISATTY | SC_EXIT_ERR,
1720 1730 "no dump device configured");
1721 1731 }
1722 1732 }
1723 1733
1724 1734 if (mflag)
1725 1735 return (message_save());
1726 1736
1727 1737 if (optind == argc - 1)
1728 1738 savedir = argv[optind];
1729 1739
1730 1740 if (savedir == NULL || optind < argc - 1)
1731 1741 usage();
1732 1742
1733 1743 if (livedump && ioctl(dumpfd, DIOCDUMP, NULL) == -1)
1734 1744 logprint(SC_SL_NONE | SC_EXIT_ERR,
1735 1745 "dedicated dump device required");
1736 1746
1737 1747 (void) close(dumpfd);
1738 1748 dumpfd = -1;
1739 1749
1740 1750 Stat(dumpfile, &st);
1741 1751
1742 1752 filemode = S_ISREG(st.st_mode);
1743 1753
1744 1754 if (!filemode && defread("DUMPADM_CSAVE=off") == NULL)
↓ open down ↓ |
23 lines elided |
↑ open up ↑ |
1745 1755 csave = 1;
1746 1756
1747 1757 read_dumphdr();
1748 1758
1749 1759 /*
1750 1760 * We want this message to go to the log file, but not the console.
1751 1761 * There's no good way to do that with the existing syslog facility.
1752 1762 * We could extend it to handle this, but there doesn't seem to be
1753 1763 * a general need for it, so we isolate the complexity here instead.
1754 1764 */
1755 - if (dumphdr.dump_panicstring[0] != '\0') {
1765 + if (dumphdr.dump_panicstring[0] != '\0' && !rflag) {
1756 1766 int logfd = Open("/dev/conslog", O_WRONLY, 0644);
1757 1767 log_ctl_t lc;
1758 1768 struct strbuf ctl, dat;
1759 1769 char msg[DUMP_PANICSIZE + 100];
1760 1770 char fmt[] = "reboot after panic: %s";
1761 1771 uint32_t msgid;
1762 1772
1763 1773 STRLOG_MAKE_MSGID(fmt, msgid);
1764 1774
1765 1775 /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
1766 1776 (void) sprintf(msg, "%s: [ID %u FACILITY_AND_PRIORITY] ",
1767 1777 progname, msgid);
1768 1778 /* LINTED: E_SEC_PRINTF_VAR_FMT */
1769 1779 (void) sprintf(msg + strlen(msg), fmt,
1770 1780 dumphdr.dump_panicstring);
1771 1781
1772 1782 lc.pri = LOG_AUTH | LOG_ERR;
1773 1783 lc.flags = SL_CONSOLE | SL_LOGONLY;
1774 1784 lc.level = 0;
1775 1785
1776 1786 ctl.buf = (void *)&lc;
1777 1787 ctl.len = sizeof (log_ctl_t);
1778 1788
1779 1789 dat.buf = (void *)msg;
1780 1790 dat.len = strlen(msg) + 1;
1781 1791
1782 1792 (void) putmsg(logfd, &ctl, &dat, 0);
1783 1793 (void) close(logfd);
1784 1794 }
1785 1795
1786 1796 if ((dumphdr.dump_flags & DF_COMPLETE) == 0) {
1787 1797 logprint(SC_SL_WARN, "incomplete dump on dump device");
1788 1798 dump_incomplete = B_TRUE;
1789 1799 }
1790 1800
1791 1801 if (dumphdr.dump_fm_panic)
1792 1802 fm_panic = B_TRUE;
1793 1803
↓ open down ↓ |
28 lines elided |
↑ open up ↑ |
1794 1804 /*
1795 1805 * We have a valid dump on a dump device and know as much about
1796 1806 * it as we're going to at this stage. Raise an event for
1797 1807 * logging and so that FMA can open a case for this panic.
1798 1808 * Avoid this step for FMA-initiated panics - FMA will replay
1799 1809 * ereports off the dump device independently of savecore and
1800 1810 * will make a diagnosis, so we don't want to open two cases
1801 1811 * for the same event. Also avoid raising an event for a
1802 1812 * livedump, or when we inflating a compressed dump.
1803 1813 */
1804 - if (!fm_panic && !livedump && !filemode)
1814 + if (!fm_panic && !livedump && !filemode && !rflag)
1805 1815 raise_event(SC_EVENT_DUMP_PENDING, NULL);
1806 1816
1807 1817 logprint(SC_SL_WARN, "System dump time: %s",
1808 1818 ctime(&dumphdr.dump_crashtime));
1809 1819
1810 1820 /*
1811 1821 * Option -c is designed for use from svc-dumpadm where we know
1812 1822 * that dumpadm -n is in effect but run savecore -c just to
1813 1823 * get the above dump_pending_on_device event raised. If it is run
1814 1824 * interactively then just print further panic details.
1815 1825 */
1816 1826 if (cflag) {
1817 1827 char *disabled = defread("DUMPADM_ENABLE=no");
1818 1828 int lvl = interactive ? SC_SL_WARN : SC_SL_ERR;
1819 1829 int ec = fm_panic ? SC_EXIT_FM : SC_EXIT_PEND;
1820 1830
1821 1831 logprint(lvl | ec,
1822 1832 "Panic crashdump pending on dump device%s "
1823 1833 "run savecore(1M) manually to extract. "
1824 1834 "Image UUID %s%s.",
1825 1835 disabled ? " but dumpadm -n in effect;" : ";",
1826 1836 corehdr.dump_uuid,
1827 1837 fm_panic ? "(fault-management initiated)" : "");
1828 1838 /*NOTREACHED*/
1829 1839 }
1830 1840
1831 1841 if (chdir(savedir) == -1)
1832 1842 logprint(SC_SL_ERR | SC_EXIT_ERR, "chdir(\"%s\"): %s",
1833 1843 savedir, strerror(errno));
1834 1844
1835 1845 check_space(csave);
1836 1846
1837 1847 if (filebounds < 0)
1838 1848 bounds = read_number_from_file("bounds", 0);
1839 1849 else
1840 1850 bounds = filebounds;
1841 1851
1842 1852 if (csave) {
1843 1853 size_t metrics_size = datahdr.dump_metrics;
1844 1854
1845 1855 (void) sprintf(corefile, "vmdump.%ld", bounds);
1846 1856
1847 1857 datahdr.dump_metrics = 0;
1848 1858
1849 1859 logprint(SC_SL_ERR,
↓ open down ↓ |
35 lines elided |
↑ open up ↑ |
1850 1860 "Saving compressed system crash dump in %s/%s",
1851 1861 savedir, corefile);
1852 1862
1853 1863 copy_crashfile(corefile);
1854 1864
1855 1865 /*
1856 1866 * Raise a fault management event that indicates the system
1857 1867 * has panicked. We know a reasonable amount about the
1858 1868 * condition at this time, but the dump is still compressed.
1859 1869 */
1860 - if (!livedump && !fm_panic)
1870 + if (!livedump && !fm_panic && !rflag)
1861 1871 raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1862 1872
1863 1873 if (metrics_size > 0) {
1864 1874 int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1865 1875 FILE *mfile = fopen(METRICSFILE, "a");
1866 1876 char *metrics = Zalloc(metrics_size + 1);
1867 1877
1868 1878 Pread(dumpfd, metrics, metrics_size, endoff +
1869 1879 sizeof (dumphdr) + sizeof (datahdr));
1870 1880
1871 1881 if (sec < 1)
1872 1882 sec = 1;
1873 1883
1874 1884 if (mfile == NULL) {
1875 1885 logprint(SC_SL_WARN,
1876 1886 "Can't create %s:\n%s",
1877 1887 METRICSFILE, metrics);
1878 1888 } else {
1879 1889 (void) fprintf(mfile, "[[[[,,,");
1880 1890 for (i = 0; i < argc; i++)
1881 1891 (void) fprintf(mfile, "%s ", argv[i]);
1882 1892 (void) fprintf(mfile, "\n");
1883 1893 (void) fprintf(mfile, ",,,%s %s %s %s %s\n",
1884 1894 dumphdr.dump_utsname.sysname,
1885 1895 dumphdr.dump_utsname.nodename,
1886 1896 dumphdr.dump_utsname.release,
1887 1897 dumphdr.dump_utsname.version,
1888 1898 dumphdr.dump_utsname.machine);
1889 1899 (void) fprintf(mfile, ",,,%s dump time %s\n",
1890 1900 dumphdr.dump_flags & DF_LIVE ? "Live" :
1891 1901 "Crash", ctime(&dumphdr.dump_crashtime));
1892 1902 (void) fprintf(mfile, ",,,%s/%s\n", savedir,
1893 1903 corefile);
1894 1904 (void) fprintf(mfile, "Metrics:\n%s\n",
1895 1905 metrics);
1896 1906 (void) fprintf(mfile, "Copy pages,%ld\n",
1897 1907 dumphdr. dump_npages);
1898 1908 (void) fprintf(mfile, "Copy time,%d\n", sec);
1899 1909 (void) fprintf(mfile, "Copy pages/sec,%ld\n",
1900 1910 dumphdr.dump_npages / sec);
1901 1911 (void) fprintf(mfile, "]]]]\n");
1902 1912 (void) fclose(mfile);
1903 1913 }
1904 1914 free(metrics);
1905 1915 }
1906 1916
1907 1917 logprint(SC_SL_ERR,
1908 1918 "Decompress the crash dump with "
1909 1919 "\n'savecore -vf %s/%s'",
1910 1920 savedir, corefile);
1911 1921
1912 1922 } else {
1913 1923 (void) sprintf(namelist, "unix.%ld", bounds);
1914 1924 (void) sprintf(corefile, "vmcore.%ld", bounds);
1915 1925
1916 1926 if (interactive && filebounds >= 0 && access(corefile, F_OK)
1917 1927 == 0)
1918 1928 logprint(SC_SL_NONE | SC_EXIT_ERR,
↓ open down ↓ |
48 lines elided |
↑ open up ↑ |
1919 1929 "%s already exists: remove with "
1920 1930 "'rm -f %s/{unix,vmcore}.%ld'",
1921 1931 corefile, savedir, bounds);
1922 1932
1923 1933 logprint(SC_SL_ERR,
1924 1934 "saving system crash dump in %s/{unix,vmcore}.%ld",
1925 1935 savedir, bounds);
1926 1936
1927 1937 build_corefile(namelist, corefile);
1928 1938
1929 - if (!livedump && !filemode && !fm_panic)
1939 + if (!livedump && !filemode && !fm_panic && !rflag)
1930 1940 raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1931 1941
1932 1942 if (access(METRICSFILE, F_OK) == 0) {
1933 1943 int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1934 1944 FILE *mfile = fopen(METRICSFILE, "a");
1935 1945
1936 1946 if (sec < 1)
1937 1947 sec = 1;
1938 1948
1939 1949 if (mfile == NULL) {
1940 1950 logprint(SC_SL_WARN,
1941 1951 "Can't create %s: %s",
1942 1952 METRICSFILE, strerror(errno));
1943 1953 } else {
1944 1954 (void) fprintf(mfile, "[[[[,,,");
1945 1955 for (i = 0; i < argc; i++)
1946 1956 (void) fprintf(mfile, "%s ", argv[i]);
1947 1957 (void) fprintf(mfile, "\n");
1948 1958 (void) fprintf(mfile, ",,,%s/%s\n", savedir,
1949 1959 corefile);
1950 1960 (void) fprintf(mfile, ",,,%s %s %s %s %s\n",
1951 1961 dumphdr.dump_utsname.sysname,
1952 1962 dumphdr.dump_utsname.nodename,
1953 1963 dumphdr.dump_utsname.release,
1954 1964 dumphdr.dump_utsname.version,
1955 1965 dumphdr.dump_utsname.machine);
1956 1966 (void) fprintf(mfile,
1957 1967 "Uncompress pages,%"PRIu64"\n", saved);
1958 1968 (void) fprintf(mfile, "Uncompress time,%d\n",
1959 1969 sec);
1960 1970 (void) fprintf(mfile, "Uncompress pages/sec,%"
1961 1971 PRIu64"\n", saved / sec);
1962 1972 (void) fprintf(mfile, "]]]]\n");
1963 1973 (void) fclose(mfile);
1964 1974 }
1965 1975 }
1966 1976 }
1967 1977
1968 1978 if (filebounds < 0) {
1969 1979 (void) sprintf(boundstr, "%ld\n", bounds + 1);
1970 1980 bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644);
1971 1981 Pwrite(bfd, boundstr, strlen(boundstr), 0);
1972 1982 (void) close(bfd);
1973 1983 }
1974 1984
1975 1985 if (verbose) {
1976 1986 int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1977 1987
1978 1988 (void) printf("%d:%02d dump %s is done\n",
1979 1989 sec / 60, sec % 60,
1980 1990 csave ? "copy" : "decompress");
1981 1991 }
1982 1992
1983 1993 if (verbose > 1 && hist != NULL) {
1984 1994 int i, nw;
1985 1995
1986 1996 for (i = 1, nw = 0; i <= BTOP(coreblksize); ++i)
1987 1997 nw += hist[i] * i;
1988 1998 (void) printf("pages count %%\n");
1989 1999 for (i = 0; i <= BTOP(coreblksize); ++i) {
1990 2000 if (hist[i] == 0)
1991 2001 continue;
1992 2002 (void) printf("%3d %5u %6.2f\n",
1993 2003 i, hist[i], 100.0 * hist[i] * i / nw);
1994 2004 }
1995 2005 }
1996 2006
1997 2007 (void) close(dumpfd);
1998 2008 dumpfd = -1;
1999 2009
2000 2010 return (0);
2001 2011 }
↓ open down ↓ |
62 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX