Skip site navigation (1)Skip section navigation (2)
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>