From owner-p4-projects@FreeBSD.ORG Wed Aug 4 18:07:47 2010 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 208271065676; Wed, 4 Aug 2010 18:07:47 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D92791065672 for ; Wed, 4 Aug 2010 18:07:46 +0000 (UTC) (envelope-from jhb@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id C60C08FC1E for ; Wed, 4 Aug 2010 18:07:46 +0000 (UTC) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.4/8.14.4) with ESMTP id o74I7kZc032332 for ; Wed, 4 Aug 2010 18:07:46 GMT (envelope-from jhb@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.4/8.14.4/Submit) id o74I7kvu032330 for perforce@freebsd.org; Wed, 4 Aug 2010 18:07:46 GMT (envelope-from jhb@freebsd.org) Date: Wed, 4 Aug 2010 18:07:46 GMT Message-Id: <201008041807.o74I7kvu032330@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to jhb@freebsd.org using -f From: John Baldwin To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 181845 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 04 Aug 2010 18:07:47 -0000 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 #include #include +#include +#include #endif #include #include @@ -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();