PERFORCE change 181845 for review
John Baldwin
jhb at FreeBSD.org
Wed Aug 4 18:07:47 UTC 2010
http://p4web.freebsd.org/@@181845?ac=10
Change 181845 by jhb at 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();
More information about the p4-projects
mailing list