view usr/src/cmd/mdb/common/mdb/mdb_vcb.c @ 14167:7ac6fb740bcf

3946 ::gcore (fix sparc build)
author Christopher Siden <chris.siden@delphix.com>
date Tue, 27 Aug 2013 10:51:34 -0800
parents 68f95e015346
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, Version 1.0 only
 * (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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * In order to implement walk iteration variables (that is, ::walk walk varname)
 * we need to keep track of the active walk variables as the pipeline is
 * processed.  Each variable is tracked using a VCB (Variable Control Block)
 * that keeps a pointer to the variable in the MDB variable hash table, as
 * well as an addrvec (array of values) and parent pointer.  Each command in
 * the pipeline keeps its own list of VCBs, and these are inherited from left
 * to right in the pipeline.  The diagram shows an example pipeline and the
 * contents of c_addrv and VCBs at each stage:
 *
 *     > ::walk proc p |     ::map .+1    |   ::eval '<p=K'
 *
 *                                 vcb(p)              vcb(p)
 *                             0<- parent <----------- parent
 *                       c_addrv   addrv     c_addrv   addrv
 *                       123       123       124       123
 *                       456       456       457       456
 *                       789       789       790       789
 *
 * Then the first command (::walk) begins life with no VCBs.  It then creates
 * a new VCB for the rest of the pipeline and adds it to the next command's
 * VCB list (::map).  Before ::map is executed, it will first pass along a set
 * of VCBs to its "child" ::eval.  The important operations defined for VCBs
 * are as follows:
 *
 * (1) mdb_vcb_inherit - Prior to processing each command (pipeline stage), the
 * debugger calls the inherit routine to cause the next command to inherit the
 * VCBs from the current command.  The inherit routine allocates a new VCB
 * containing a pointer to the same variable, and sets its parent pointer to
 * point back to the parent VCB.  A VCB created by ::walk has a NULL parent
 * pointer indicating that it inherits its value from dot.
 *
 * (2) mdb_vcb_propagate - Prior to invoking the dcmd associated with a command,
 * the debugger propagates the next value stored in the VCB to its variable.
 * The VCB stores the values the variable should assume (that is, the values
 * of the variable that correspond to the value stored in the command's c_addrv)
 * in an addrvec in the VCB itself.
 *
 * (3) mdb_vcb_update - As each dcmd executes, it produces output for the next
 * stage in the pipeline.  The *next* stage of the pipeline's mdb_cmd_t has
 * already inherited the necessary VCBs in step (1), and so we just need to
 * record the current value of the variable into the VCB's addrv.  In the base
 * case (the first pipeline stage), the variable is not yet set, so we want
 * to store the current value of dot (produced by ::walk's callback) into the
 * addrv.  This value is passed in directly from the parsing code as a parameter
 * before the parser resets dot itself.  For subsequent pipeline stages, we
 * need to store into addrv the value the variable previously held when the
 * dcmd that produced this new value of dot was executed.  This value is
 * stored in the corresponding index of the parent VCB's addrv.
 *
 * (4) mdb_vcb_find - Given an mdb_var_t, determines if there already exists a
 * vcb for this variable, and if so returns it.  This allows us to avoid
 * re-creating a vcb every time through a walk, such as:
 *
 * 	> ::walk proc p | ::walk proc v | ::eval "<p=Kn"
 *
 * In this case, we don't want to create a new vcb for 'v' every time we execute
 * the second walk.
 *
 * Unfortunately, determining the addrv index is complicated by the fact that
 * pipes involve the asynchronous execution of the dcmds and the parser.  This
 * asynchrony means that the parser may not actually consume the output of a
 * given dcmd until long after it has completed, and thus when the parser is
 * ready to reset dot, it does not know what addrv index produced this value.
 * We work around this problem by explicitly flushing the pipeline after each
 * dcmd invocation if VCBs are active.  This does impact performance, so we
 * may need to re-evaluate in the future if pipelines are producing huge
 * amounts of data and a large number of VCBs are active simultaneously.
 */

#include <mdb/mdb_frame.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_vcb.h>
#include <mdb/mdb.h>

mdb_vcb_t *
mdb_vcb_create(mdb_var_t *v)
{
	mdb_vcb_t *vcb = mdb_zalloc(sizeof (mdb_vcb_t), UM_SLEEP);
	vcb->vc_var = v;
	return (vcb);
}

void
mdb_vcb_destroy(mdb_vcb_t *vcb)
{
	mdb_dprintf(MDB_DBG_DSTK, "delete vcb %p (%s)\n", (void *)vcb,
	    mdb_nv_get_name(vcb->vc_var));

	mdb_addrvec_destroy(&vcb->vc_addrv);
	mdb_free(vcb, sizeof (mdb_vcb_t));
}

void
mdb_vcb_propagate(mdb_vcb_t *vcb)
{
	while (vcb != NULL) {
		mdb_addrvec_t *adp = &vcb->vc_addrv;
		ASSERT(vcb->vc_adnext < adp->ad_nelems);
		mdb_nv_set_value(vcb->vc_var, adp->ad_data[vcb->vc_adnext++]);
		vcb = vcb->vc_link;
	}
}

void
mdb_vcb_purge(mdb_vcb_t *vcb)
{
	while (vcb != NULL) {
		mdb_vcb_t *n = vcb->vc_link;
		mdb_vcb_destroy(vcb);
		vcb = n;
	}
}

void
mdb_vcb_inherit(mdb_cmd_t *src, mdb_cmd_t *dst)
{
	mdb_vcb_t *vc1, *vc2;

	for (vc1 = src->c_vcbs; vc1 != NULL; vc1 = vc1->vc_link) {
		vc2 = mdb_vcb_create(vc1->vc_var);
		vc2->vc_parent = vc1;
		vc2->vc_link = dst->c_vcbs;
		dst->c_vcbs = vc2;
	}
}

void
mdb_vcb_insert(mdb_vcb_t *vcb, mdb_frame_t *fp)
{
	if (fp->f_pcmd != NULL) {
		mdb_cmd_t *cp = fp->f_pcmd;

		mdb_dprintf(MDB_DBG_DSTK, "insert vcb %p (%s)\n",
		    (void *)vcb, mdb_nv_get_name(vcb->vc_var));

		ASSERT(vcb->vc_link == NULL);
		vcb->vc_link = cp->c_vcbs;
		cp->c_vcbs = vcb;
	}
}

void
mdb_vcb_update(struct mdb_frame *fp, uintptr_t value)
{
	mdb_vcb_t *vcb;

	for (vcb = fp->f_pcmd->c_vcbs; vcb != NULL; vcb = vcb->vc_link) {
		if (vcb->vc_parent != NULL) {
			mdb_addrvec_t *adp = &vcb->vc_parent->vc_addrv;
			adp->ad_ndx = vcb->vc_parent->vc_adnext - 1;
			ASSERT(adp->ad_ndx < adp->ad_nelems);
			value = adp->ad_data[adp->ad_ndx++];
		}
		mdb_addrvec_unshift(&vcb->vc_addrv, value);
	}
}

mdb_vcb_t *
mdb_vcb_find(mdb_var_t *var, mdb_frame_t *fp)
{
	mdb_vcb_t *vcb;

	if (fp->f_pcmd != NULL) {
		vcb = fp->f_pcmd->c_vcbs;
		while (vcb != NULL) {
			if (vcb->vc_var == var)
				return (vcb);
			vcb = vcb->vc_link;
		}
	}

	return (NULL);
}