Date: Wed, 4 Aug 2010 18:07:46 GMT From: John Baldwin <jhb@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 181845 for review Message-ID: <201008041807.o74I7kvu032330@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@181845?ac=10 Change 181845 by jhb@jhb_jhbbsd on 2010/08/04 18:07:01 Add support for parsing machine check records from a crashdump. Use the -M/-N arguments as with other libkvm-using tools to use. Affected files ... .. //depot/projects/mcelog/Makefile#3 edit .. //depot/projects/mcelog/mcelog.c#4 edit Differences ... ==== //depot/projects/mcelog/Makefile#3 (text) ==== @@ -28,6 +28,7 @@ .PHONY: install clean depend +LIBS := OBJ := p4.o k8.o mcelog.o dmi.o tsc.o core2.o bitfield.o intel.o \ nehalem.o dunnington.o tulsa.o config.o memutil.o msg.o \ eventloop.o leaky-bucket.o memdb.o server.o client.o \ @@ -37,6 +38,7 @@ endif ifdef FREEBSD OBJ += memstream.o +LIBS += -lkvm endif DISKDB_OBJ := diskdb.o dimm.o db.o CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o ${DISKDB_OBJ} @@ -53,7 +55,7 @@ SRC := $(OBJ:.o=.c) -mcelog: ${OBJ} +mcelog: ${OBJ} ${LIBS} # dbquery intentionally not installed by default install: mcelog ==== //depot/projects/mcelog/mcelog.c#4 (text) ==== @@ -32,6 +32,8 @@ #include <machine/specialreg.h> #include <machine/mca.h> #include <err.h> +#include <kvm.h> +#include <limits.h> #endif #include <stdlib.h> #include <stdio.h> @@ -91,6 +93,10 @@ static struct config_cred runcred = { .uid = -1U, .gid = -1U }; static int numerrors; static char *pidfile; +#ifdef __FreeBSD__ +static char *execfile; +static char *corefile; +#endif static void check_cpu(void); @@ -962,6 +968,10 @@ " mcelog [options] --ascii < log\n" " mcelog [options] --ascii --file log\n" "Decode machine check ASCII output from kernel logs\n" +#ifdef __FreeBSD__ +" mcelog [options] -M vmcore -N kernel\n" +"Decode machine check error records from kernel crashdump.\n" +#endif "Options:\n" "--cpu CPU Set CPU type CPU to decode (see below for valid types)\n" "--cpumhz MHZ Set CPU Mhz to decode time (output unreliable, not needed on new kernels)\n" @@ -1142,6 +1152,14 @@ case O_CONFIG_FILE: /* parsed in config.c */ break; +#ifdef __FreeBSD__ + case 'M': + corefile = strdup(optarg); + break; + case 'N': + execfile = strdup(optarg); + break; +#endif case 0: break; default: @@ -1246,22 +1264,128 @@ #endif #ifdef __FreeBSD__ -static void process(int fd __unused, unsigned recordlen, - unsigned loglen, char *buf __unused) +struct mca_record_old { + uint64_t mr_status; + uint64_t mr_addr; + uint64_t mr_misc; + uint64_t mr_tsc; + int mr_apic_id; + int mr_bank; +}; + +struct mca_record_internal { + struct mca_record rec; + int logged; + STAILQ_ENTRY(mca_internal) link; +}; + +struct mca_record_internal_old { + struct mca_record_old rec; + int logged; + STAILQ_ENTRY(mca_internal) link; +}; + +static struct nlist nl[] = { +#define X_MCA_RECORDS 0 + { .n_name = "_mca_records" }, +#define X_SNAPDATE 1 + { .n_name = "_snapdate" }, + { .n_name = NULL }, +}; + +static int +kread(kvm_t *kvm, void *kvm_pointer, void *buf, size_t size, size_t offset) +{ + ssize_t ret; + + ret = kvm_read(kvm, (unsigned long)kvm_pointer + offset, buf, size); + if (ret < 0 || (size_t)ret != size) + return (-1); + return (0); +} + +static int +kread_symbol(kvm_t *kvm, int index, void *buf, size_t size) +{ + ssize_t ret; + + ret = kvm_read(kvm, nl[index].n_value, buf, size); + if (ret < 0 || (size_t)ret != size) + return (-1); + return (0); +} + +static void process_kvm(const char *execfile, const char *corefile) +{ + struct mca_record mr, *mrp; + struct mce mce; + char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kvm; + size_t record_size, link_offset; + int i, snapdate; + + kvm = kvm_openfiles(execfile, corefile, NULL, O_RDONLY, errbuf); + if (kvm == NULL) + errx(1, "kvm_openfiles: %s", errbuf); + if (kvm_nlist(kvm, nl) != 0) + errx(1, "kvm_nlist: %s", kvm_geterr(kvm)); + + if (kread_symbol(kvm, X_SNAPDATE, &snapdate, sizeof(snapdate)) < 0) + errx(1, "kvm_read(snapdate) failed"); + /* stqh_first is the first pointer at this address. */ + if (kread_symbol(kvm, X_MCA_RECORDS, &mrp, sizeof(mrp)) < 0) + errx(1, "kvm_read(mca_records) failed"); + if (snapdate >= 20100329) { + record_size = sizeof(struct mca_record); + link_offset = __offsetof(struct mca_record_internal, + link.stqe_next); + } else { + record_size = sizeof(struct mca_record_old); + link_offset = __offsetof(struct mca_record_internal_old, + link.stqe_next); + } + + for (i = 0; mrp != NULL; i++) { + memset(&mr, 0, sizeof(mr)); + if (kread(kvm, mrp, &mr, record_size, 0) < 0) + break; + if (kread(kvm, mrp, &mrp, sizeof(mrp), link_offset) < 0) + mrp = NULL; + + convert_mca(&mr, &mce, 1, record_size); + mce_prepare(&mce); + if (!mce_filter(&mce, sizeof(struct mce))) + continue; + if (!dump_raw_ascii) { + disclaimer(); + Wprintf("MCE %d\n", i); + dump_mce(&mce, sizeof(struct mce)); + } else + dump_mce_raw_ascii(&mce, sizeof(struct mce)); + flushlog(); + } + + exit(0); +} + +static void process_live(void) { struct mca_record mr; struct mce mce; int mib[4]; size_t len; - int finish, i; + int count, finish, i; + + len = sizeof(count); + if (sysctlbyname("hw.mca.count", &count, &len, NULL, 0) < 0) + return; len = 4; if (sysctlnametomib("hw.mca.records", mib, &len) < 0) return; finish = 0; - recordlen = sizeof(struct mce); - for (i = 0; i < (int)loglen; i++) { + for (i = 0; i < count; i++) { mib[3] = i; len = sizeof(mr); memset(&mr, 0, sizeof(mr)); @@ -1274,14 +1398,14 @@ mce_prepare(&mce); if (numerrors > 0 && --numerrors == 0) finish = 1; - if (!mce_filter(&mce, recordlen)) + if (!mce_filter(&mce, sizeof(struct mce))) continue; if (!dump_raw_ascii) { disclaimer(); Wprintf("MCE %d\n", i); - dump_mce(&mce, recordlen); + dump_mce(&mce, sizeof(struct mce)); } else - dump_mce_raw_ascii(&mce, recordlen); + dump_mce_raw_ascii(&mce, sizeof(struct mce)); flushlog(); } @@ -1356,29 +1480,21 @@ } #endif -#ifdef __FreeBSD__ -/* Fetch current MCA records using sysctls. */ -static void fetch_records_info(struct mcefd_data *d) -{ - size_t len; - int count; - - memset(d, 0, sizeof(*d)); - len = sizeof(count); - if (sysctlbyname("hw.mca.count", &count, &len, NULL, 0) == 0) - d->loglen = count; -} -#endif - int main(int ac, char **av) { +#ifdef __Linux__ struct mcefd_data d = {}; + int fd; +#endif int opt; - int fd; parse_config(av); - while ((opt = getopt_long(ac, av, "", options, NULL)) != -1) { +#ifdef __FreeBSD__ + while ((opt = getopt_long(ac, av, "M:N:", options, NULL)) != -1) { +#else + while ((opt = getopt_long(ac, av, "", options, NULL)) != -1) { +#endif if (opt == '?') { usage(); } else if (combined_modifier(opt) > 0) { @@ -1404,6 +1520,11 @@ #endif if (av[optind]) usage(); +#ifdef __FreeBSD__ + if ((corefile != NULL) ^ (execfile != NULL) || + (corefile != NULL && daemon_mode)) + usage(); +#endif checkdmi(); general_setup(); @@ -1423,10 +1544,6 @@ d.buf = xalloc(d.recordlen * d.loglen); #endif -#ifdef __FreeBSD__ - fetch_records_info(&d); - fd = -1; -#endif if (daemon_mode) { check_cpu(); prefill_memdb(); @@ -1446,7 +1563,15 @@ write_pidfile(); eventloop(); } else { +#ifdef __Linux__ process(fd, d.recordlen, d.loglen, d.buf); +#endif +#ifdef __FreeBSD__ + if (corefile != NULL) + process_kvm(execfile, corefile); + else + process_live(); +#endif } #ifdef __Linux__ trigger_wait();
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201008041807.o74I7kvu032330>