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