view usr/src/cmd/fm/modules/common/eversholt/eft_mdb.c @ 11050:be69f645ce17

6894028 Some fm libraries are not lint clean 6897387 IPMI facility provider methods not registered on some platforms 6893713 Some fm cmds are not lint clean
author Robert Johnston <Robert.Johnston@Sun.COM>
date Wed, 11 Nov 2009 18:52:48 -0800
parents f231270422fc
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/mdb_modapi.h>

#include <lut.h>
#include <itree.h>
#include "ipath_impl.h"
#include "lut_impl.h"
#include "config_impl.h"
#include "stats_impl.h"

#define	LUT_SIZE_INIT	300
#define	LUT_SIZE_INCR	100

struct lut_cp {
	uintptr_t lutcp_addr;
	struct lut lutcp_lut;
};

#define	LCPSZ	sizeof (struct lut_cp)

struct lut_dump_desc {
	struct lut_cp *ld_array;
	int ld_arraysz;
	int ld_nents;
};

static void
lut_dump_array_alloc(struct lut_dump_desc *lddp)
{
	struct lut_cp *new;

	if (lddp->ld_array == NULL) {
		lddp->ld_arraysz = LUT_SIZE_INIT;
		lddp->ld_array = mdb_zalloc(LUT_SIZE_INIT * LCPSZ, UM_SLEEP);
		return;
	}

	new = mdb_zalloc((lddp->ld_arraysz + LUT_SIZE_INCR) * LCPSZ, UM_SLEEP);
	bcopy(lddp->ld_array, new, lddp->ld_arraysz * LCPSZ);
	mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ);
	lddp->ld_array = new;
	lddp->ld_arraysz += LUT_SIZE_INCR;
}

static void
lut_dump_array_free(struct lut_dump_desc *lddp)
{
	if (lddp->ld_array != NULL) {
		mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ);
		lddp->ld_array = NULL;
	}
}

static void
lut_collect_addent(uintptr_t addr, struct lut *ent, struct lut_dump_desc *lddp)
{
	struct lut_cp *lcp;

	if (lddp->ld_nents == lddp->ld_arraysz)
		lut_dump_array_alloc(lddp);

	lcp = &lddp->ld_array[lddp->ld_nents++];

	lcp->lutcp_addr = addr;
	bcopy(ent, &lcp->lutcp_lut, sizeof (struct lut));
}

static int
eft_lut_walk(uintptr_t root, struct lut_dump_desc *lddp)
{
	struct lut lutent;

	if (root) {
		if (mdb_vread(&lutent, sizeof (struct lut), root) !=
		    sizeof (struct lut)) {
			mdb_warn("failed to read struct lut at %p", root);
			return (WALK_ERR);
		}

		if (eft_lut_walk((uintptr_t)lutent.lut_left, lddp) != WALK_NEXT)
			return (WALK_ERR);

		lut_collect_addent(root, &lutent, lddp);

		if (eft_lut_walk((uintptr_t)lutent.lut_right, lddp) !=
		    WALK_NEXT)
			return (WALK_ERR);
	}
	return (WALK_NEXT);
}

static int
lut_collect(uintptr_t addr, struct lut_dump_desc *lddp)
{
	lut_dump_array_alloc(lddp);

	if (eft_lut_walk(addr, lddp) != WALK_NEXT) {
		lut_dump_array_free(lddp);
		return (WALK_ERR);
	} else {
		return (WALK_NEXT);	/* caller must free dump array */
	}
}

static int
lut_walk_init(mdb_walk_state_t *wsp)
{
	if (wsp->walk_addr == NULL) {
		mdb_warn("lut walker requires a lut table address\n");
		return (WALK_ERR);
	}

	wsp->walk_data = mdb_zalloc(sizeof (struct lut_dump_desc), UM_SLEEP);
	wsp->walk_arg = 0;

	if (lut_collect(wsp->walk_addr, wsp->walk_data) == WALK_NEXT) {
		return (WALK_NEXT);
	} else {
		mdb_warn("failed to suck in full lut\n");
		mdb_free(wsp->walk_data, sizeof (struct lut_dump_desc));
		return (WALK_ERR);
	}
}

static int
lut_walk_step(mdb_walk_state_t *wsp)
{
	struct lut_dump_desc *lddp = wsp->walk_data;
	int *ip = (int *)&wsp->walk_arg;
	struct lut_cp *lcp = &lddp->ld_array[*ip];

	if (*ip == lddp->ld_nents)
		return (WALK_DONE);

	++*ip;

	return (wsp->walk_callback(lcp->lutcp_addr, &lcp->lutcp_lut,
	    wsp->walk_cbdata));
}

static int
ipath_walk_init(mdb_walk_state_t *wsp)
{
	struct ipath *ipath;

	ipath = mdb_alloc(sizeof (struct ipath), UM_SLEEP);

	if (mdb_vread((void *)ipath, sizeof (struct ipath),
	    wsp->walk_addr) != sizeof (struct ipath)) {
		mdb_warn("failed to read struct ipath at %p", wsp->walk_addr);
		return (WALK_ERR);
	}
	wsp->walk_data = (void *)ipath;

	if (ipath->s == NULL)
		return (WALK_DONE);
	else
		return (WALK_NEXT);
}

static void
ipath_walk_fini(mdb_walk_state_t *wsp)
{
	mdb_free(wsp->walk_data, sizeof (struct ipath));
}

static int
ipath_walk_step(mdb_walk_state_t *wsp)
{
	int status;
	struct ipath *ipath = (struct ipath *)wsp->walk_data;
	struct ipath *ip = (struct ipath *)wsp->walk_addr;

	if (ip == NULL || ipath->s == NULL)
		return (WALK_DONE);

	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
	    wsp->walk_cbdata);

	wsp->walk_addr = (uintptr_t)(ip + 1);

	if (mdb_vread(wsp->walk_data, sizeof (struct ipath),
	    wsp->walk_addr) != sizeof (struct ipath)) {
		mdb_warn("failed to read struct ipath at %p", wsp->walk_addr);
		return (WALK_ERR);
	}

	return (status);
}

static void
lut_walk_fini(mdb_walk_state_t *wsp)
{
	struct lut_dump_desc *lddp = wsp->walk_data;

	lut_dump_array_free(lddp);
	mdb_free(lddp, sizeof (struct lut_dump_desc));
}

/*ARGSUSED*/
static int
ipath_node(uintptr_t addr, const void *data, void *arg)
{
	struct ipath *ipath = (struct ipath *)data;
	char buf[128];

	if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)ipath->s) < 0)
		(void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>",
		    ipath->s);

	mdb_printf("/%s=%d", buf, ipath->i);
	return (DCMD_OK);
}

/*ARGSUSED*/
static int
ipath(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	if (argc)
		return (DCMD_USAGE);
	if (!(flags & DCMD_ADDRSPEC))
		addr = mdb_get_dot();
	if (mdb_pwalk("eft_ipath", ipath_node, NULL, addr) != 0)
		return (DCMD_ERR);
	return (DCMD_OK);
}

/*ARGSUSED*/
static int
eft_count(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	struct lut lut;
	struct istat_entry istat_entry;
	struct stats count;
	GElf_Sym sym;
	char buf[128];

	if (argc)
		return (DCMD_USAGE);
	if (!(flags & DCMD_ADDRSPEC)) {
		if (mdb_lookup_by_obj(MDB_OBJ_EVERY, "Istats", &sym) == -1 ||
		    sym.st_size != sizeof (addr))
			return (DCMD_ERR);
		if (mdb_vread(&addr, sizeof (addr),
		    (uintptr_t)sym.st_value) != sizeof (addr))
			return (DCMD_ERR);
		if (addr == NULL)
			return (DCMD_OK);
		if (mdb_pwalk_dcmd("lut", "eft_count", argc, argv, addr) != 0)
			return (DCMD_ERR);
		return (DCMD_OK);
	}

	if (mdb_vread(&lut, sizeof (struct lut), addr) != sizeof (struct lut)) {
		mdb_warn("failed to read struct lut at %p", addr);
		return (DCMD_ERR);
	}
	if (mdb_vread(&istat_entry, sizeof (struct istat_entry),
	    (uintptr_t)lut.lut_lhs) != sizeof (struct istat_entry)) {
		mdb_warn("failed to read struct istat_entry at %p", addr);
		return (DCMD_ERR);
	}
	if (mdb_vread(&count, sizeof (struct stats),
	    (uintptr_t)lut.lut_rhs) != sizeof (struct stats)) {
		mdb_warn("failed to read struct stats at %p", addr);
		return (DCMD_ERR);
	}

	if (mdb_readstr(buf, (size_t)sizeof (buf),
	    (uintptr_t)istat_entry.ename) < 0)
		(void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>",
		    istat_entry.ename);

	mdb_printf("%s@", buf);
	(void) ipath((uintptr_t)istat_entry.ipath, DCMD_ADDRSPEC, 0, NULL);
	mdb_printf(" %d\n", count.fmd_stats.fmds_value.i32);
	return (DCMD_OK);
}

/*ARGSUSED*/
static int
eft_time(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	unsigned long long val;
	unsigned long long ull;
	int opt_p = 0;

	if (!(flags & DCMD_ADDRSPEC))
		addr = mdb_get_dot();
	ull = addr;
	if (argc) {
		if (mdb_getopts(argc, argv,
		    'l', MDB_OPT_UINT64, &ull,
		    'p', MDB_OPT_SETBITS, TRUE, &opt_p,
		    MDB_OPT_UINT64) != argc) {
			return (DCMD_USAGE);
		}
	}
	if (opt_p) {
		if (mdb_vread(&ull, sizeof (ull), addr) != sizeof (ull)) {
			mdb_warn("failed to read timeval at %p", addr);
			return (DCMD_ERR);
		}
	}
#define	NOREMAINDER(den, num, val) (((val) = ((den) / (num))) * (num) == (den))
	if (ull == 0)
		mdb_printf("0us");
	else if (ull >= TIMEVAL_EVENTUALLY)
		mdb_printf("infinity");
	else if (NOREMAINDER(ull, 1000000000ULL*60*60*24*365, val))
		mdb_printf("%lluyear%s", val, (val == 1) ? "" : "s");
	else if (NOREMAINDER(ull, 1000000000ULL*60*60*24*30, val))
		mdb_printf("%llumonth%s", val, (val == 1) ? "" : "s");
	else if (NOREMAINDER(ull, 1000000000ULL*60*60*24*7, val))
		mdb_printf("%lluweek%s", val, (val == 1) ? "" : "s");
	else if (NOREMAINDER(ull, 1000000000ULL*60*60*24, val))
		mdb_printf("%lluday%s", val, (val == 1) ? "" : "s");
	else if (NOREMAINDER(ull, 1000000000ULL*60*60, val))
		mdb_printf("%lluhour%s", val, (val == 1) ? "" : "s");
	else if (NOREMAINDER(ull, 1000000000ULL*60, val))
		mdb_printf("%lluminute%s", val, (val == 1) ? "" : "s");
	else if (NOREMAINDER(ull, 1000000000ULL, val))
		mdb_printf("%llusecond%s", val, (val == 1) ? "" : "s");
	else if (NOREMAINDER(ull, 1000000ULL, val))
		mdb_printf("%llums", val);
	else if (NOREMAINDER(ull, 1000ULL, val))
		mdb_printf("%lluus", val);
	else
		mdb_printf("%lluns", ull);

	return (DCMD_OK);
}

/*ARGSUSED*/
static int
eft_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	struct node node;
	int opt_v = 0;
	char buf[128];

	if (!(flags & DCMD_ADDRSPEC))
		addr = mdb_get_dot();
	if (argc) {
		if (mdb_getopts(argc, argv,
		    'v', MDB_OPT_SETBITS, TRUE, &opt_v,
		    NULL) != argc) {
			return (DCMD_USAGE);
		}
	}
	if (addr == NULL)
		return (DCMD_OK);
	if (mdb_vread(&node, sizeof (node), addr) != sizeof (node)) {
		mdb_warn("failed to read struct node at %p", addr);
		return (DCMD_ERR);
	}
	if (opt_v) {
		if (mdb_readstr(buf, (size_t)sizeof (buf),
		    (uintptr_t)node.file) < 0)
			(void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>",
			    node.file);

		mdb_printf("%s len %d\n", buf, node.line);
	}
	switch (node.t) {
	case T_NOTHING:			/* used to keep going on error cases */
		mdb_printf("nothing");
		break;
	case T_NAME:			/* identifiers, sometimes chained */
		if (mdb_readstr(buf, (size_t)sizeof (buf),
		    (uintptr_t)node.u.name.s) < 0)
			(void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>",
			    node.u.name.s);

		mdb_printf("%s", buf);
		if (node.u.name.cp) {
			struct config cp;
			if (mdb_vread(&cp, sizeof (cp),
			    (uintptr_t)node.u.name.cp) != sizeof (cp)) {
				mdb_warn("failed to read struct config at %p",
				    node.u.name.cp);
				return (DCMD_ERR);
			}
			mdb_printf("%d", cp.num);
		} else if (node.u.name.it == IT_HORIZONTAL) {
			if (node.u.name.child && !node.u.name.childgen) {
				mdb_printf("<");
				(void) eft_node((uintptr_t)node.u.name.child,
				    DCMD_ADDRSPEC, 0, NULL);
				mdb_printf(">");
			} else {
				mdb_printf("<> ");
			}
		} else if (node.u.name.child) {
			mdb_printf("[");
			(void) eft_node((uintptr_t)node.u.name.child,
			    DCMD_ADDRSPEC, 0, NULL);
			mdb_printf("]");
		}
		if (node.u.name.next) {
			if (node.u.name.it == IT_ENAME)
				mdb_printf(".");
			else
				mdb_printf("/");
			(void) eft_node((uintptr_t)node.u.name.next,
			    DCMD_ADDRSPEC, 0, NULL);
		}
		break;
	case T_GLOBID:			/* globals (e.g. $a) */
		if (mdb_readstr(buf, (size_t)sizeof (buf),
		    (uintptr_t)node.u.globid.s) < 0)
			(void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>",
			    node.u.globid.s);

		mdb_printf("$%s", buf);
		break;
	case T_EVENT:			/* class@path{expr} */
		(void) eft_node((uintptr_t)node.u.event.ename, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf("@");
		(void) eft_node((uintptr_t)node.u.event.epname, DCMD_ADDRSPEC,
		    0, NULL);
		if (node.u.event.eexprlist) {
			mdb_printf(" { ");
			(void) eft_node((uintptr_t)node.u.event.eexprlist,
			    DCMD_ADDRSPEC, 0, NULL);
			mdb_printf(" }");
		}
		break;
	case T_ENGINE:			/* upset threshold engine (e.g. SERD) */
		mdb_printf("engine ");
		(void) eft_node((uintptr_t)node.u.event.ename, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_ASRU:			/* ASRU declaration */
		mdb_printf("asru ");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	case T_FRU:			/* FRU declaration */
		mdb_printf("fru ");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	case T_TIMEVAL:			/* num w/time suffix (ns internally) */
		{
			mdb_arg_t mdb_arg[2];
			mdb_arg[0].a_type = MDB_TYPE_STRING;
			mdb_arg[0].a_un.a_str = "-l";
			mdb_arg[1].a_type = MDB_TYPE_IMMEDIATE;
			mdb_arg[1].a_un.a_val = node.u.ull;
			(void) eft_time((uintptr_t)0, 0, 2, mdb_arg);
			break;
		}
	case T_NUM:			/* num (ull internally) */
		mdb_printf("%llu", node.u.ull);
		break;
	case T_QUOTE:			/* quoted string */
		if (mdb_readstr(buf, (size_t)sizeof (buf),
		    (uintptr_t)node.u.quote.s) < 0)
			(void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>",
			    node.u.quote.s);

		mdb_printf("\"%s\"", buf);
		break;
	case T_FUNC:			/* func(arglist) */
		if (mdb_readstr(buf, (size_t)sizeof (buf),
		    (uintptr_t)node.u.func.s) < 0)
			(void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>",
			    node.u.func.s);

		mdb_printf("%s(", buf);
		(void) eft_node((uintptr_t)node.u.func.arglist, DCMD_ADDRSPEC,
		    0, NULL);
		mdb_printf(")");
		break;
	case T_NVPAIR:			/* name=value pair in decl */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" = ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_ASSIGN:			/* assignment statement */
		mdb_printf("(");
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" = ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(")");
		break;
	case T_CONDIF:			/* a and T_CONDELSE in (a ? b : c ) */
		mdb_printf("(");
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" ? ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(")");
		break;
	case T_CONDELSE:		/* lists b and c in (a ? b : c ) */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" : ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_NOT:			/* boolean ! operator */
		mdb_printf("!");
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_AND:			/* boolean && operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" && ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_OR:			/* boolean || operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" || ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_EQ:			/* boolean == operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" == ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_NE:			/* boolean != operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" != ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_SUB:			/* integer - operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" - ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_ADD:			/* integer + operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" + ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_MUL:			/* integer * operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" * ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_DIV:			/* integer / operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" / ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_MOD:			/* integer % operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" % ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_LT:			/* boolean < operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" < ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_LE:			/* boolean <= operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" <= ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_GT:			/* boolean > operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" > ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_GE:			/* boolean >= operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" >= ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_BITAND:			/* bitwise & operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" & ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_BITOR:			/* bitwise | operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" | ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_BITXOR:			/* bitwise ^ operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" ^ ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_BITNOT:			/* bitwise ~ operator */
		mdb_printf(" ~");
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_LSHIFT:			/* bitwise << operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" << ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_RSHIFT:			/* bitwise >> operator */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(" >> ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_ARROW:			/* lhs (N)->(K) rhs */
		(void) eft_node((uintptr_t)node.u.arrow.lhs, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.arrow.nnp) {
			mdb_printf("(");
			(void) eft_node((uintptr_t)node.u.arrow.nnp,
			    DCMD_ADDRSPEC, 0, NULL);
			mdb_printf(")");
		}
		mdb_printf("->");
		if (node.u.arrow.knp) {
			mdb_printf("(");
			(void) eft_node((uintptr_t)node.u.arrow.knp,
			    DCMD_ADDRSPEC, 0, NULL);
			mdb_printf(")");
		}
		(void) eft_node((uintptr_t)node.u.arrow.rhs, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_LIST:			/* comma-separated list */
		(void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0,
		    NULL);
		mdb_printf(", ");
		(void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_FAULT:			/* fault declaration */
		mdb_printf("fault.");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	case T_UPSET:			/* upset declaration */
		mdb_printf("upset.");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	case T_DEFECT:			/* defect declaration */
		mdb_printf("defect.");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	case T_ERROR:			/* error declaration */
		mdb_printf("error.");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	case T_EREPORT:			/* ereport declaration */
		mdb_printf("ereport.");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	case T_SERD:			/* SERD engine declaration */
		mdb_printf("serd.");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		} else if (node.u.stmt.lutp) {
			if (mdb_pwalk_dcmd("lut", "eft_node", 0, NULL,
			    (uintptr_t)node.u.stmt.lutp) != 0)
				return (DCMD_ERR);
		}
		break;
	case T_STAT:			/* STAT engine declaration */
		mdb_printf("stat.");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		} else if (node.u.stmt.lutp) {
			if (mdb_pwalk_dcmd("lut", "eft_node", 0, NULL,
			    (uintptr_t)node.u.stmt.lutp) != 0)
				return (DCMD_ERR);
		}
		break;
	case T_PROP:			/* prop statement */
		mdb_printf("prop ");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_MASK:			/* mask statement */
		mdb_printf("mask ");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		break;
	case T_CONFIG:			/* config statement */
		mdb_printf("config ");
		(void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0,
		    NULL);
		if (node.u.stmt.nvpairs) {
			mdb_printf(" ");
			(void) eft_node((uintptr_t)node.u.stmt.nvpairs,
			    DCMD_ADDRSPEC, 0, NULL);

		}
		break;
	default:
		mdb_printf("not a eversholt node\n");
		break;
	}
	return (DCMD_OK);
}

static const mdb_walker_t walkers[] = {
	{ "lut", "walk a lookup table", lut_walk_init, lut_walk_step,
	    lut_walk_fini, NULL },
	{ "eft_ipath", "walk ipath", ipath_walk_init, ipath_walk_step,
	    ipath_walk_fini, NULL },
	{ NULL, NULL, NULL, NULL, NULL, NULL }
};

static const mdb_dcmd_t dcmds[] = {
	{ "eft_ipath", "?", "print an ipath", ipath },
	{ "eft_count", "?", "print eversholt stats", eft_count },
	{ "eft_node", "?[-v]", "print eversholt node", eft_node },
	{ "eft_time", "?[-p][-l time]", "print eversholt timeval", eft_time },
	{ NULL }
};

static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };

const mdb_modinfo_t *
_mdb_init(void)
{
	return (&modinfo);
}