xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_vcb.c (revision 948f2876ce2a3010558f4f6937e16086ebcd36f2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * In order to implement walk iteration variables (that is, ::walk walk varname)
31  * we need to keep track of the active walk variables as the pipeline is
32  * processed.  Each variable is tracked using a VCB (Variable Control Block)
33  * that keeps a pointer to the variable in the MDB variable hash table, as
34  * well as an addrvec (array of values) and parent pointer.  Each command in
35  * the pipeline keeps its own list of VCBs, and these are inherited from left
36  * to right in the pipeline.  The diagram shows an example pipeline and the
37  * contents of c_addrv and VCBs at each stage:
38  *
39  *     > ::walk proc p |     ::map .+1    |   ::eval '<p=K'
40  *
41  *                                 vcb(p)              vcb(p)
42  *                             0<- parent <----------- parent
43  *                       c_addrv   addrv     c_addrv   addrv
44  *                       123       123       124       123
45  *                       456       456       457       456
46  *                       789       789       790       789
47  *
48  * Then the first command (::walk) begins life with no VCBs.  It then creates
49  * a new VCB for the rest of the pipeline and adds it to the next command's
50  * VCB list (::map).  Before ::map is executed, it will first pass along a set
51  * of VCBs to its "child" ::eval.  The important operations defined for VCBs
52  * are as follows:
53  *
54  * (1) mdb_vcb_inherit - Prior to processing each command (pipeline stage), the
55  * debugger calls the inherit routine to cause the next command to inherit the
56  * VCBs from the current command.  The inherit routine allocates a new VCB
57  * containing a pointer to the same variable, and sets its parent pointer to
58  * point back to the parent VCB.  A VCB created by ::walk has a NULL parent
59  * pointer indicating that it inherits its value from dot.
60  *
61  * (2) mdb_vcb_propagate - Prior to invoking the dcmd associated with a command,
62  * the debugger propagates the next value stored in the VCB to its variable.
63  * The VCB stores the values the variable should assume (that is, the values
64  * of the variable that correspond to the value stored in the command's c_addrv)
65  * in an addrvec in the VCB itself.
66  *
67  * (3) mdb_vcb_update - As each dcmd executes, it produces output for the next
68  * stage in the pipeline.  The *next* stage of the pipeline's mdb_cmd_t has
69  * already inherited the necessary VCBs in step (1), and so we just need to
70  * record the current value of the variable into the VCB's addrv.  In the base
71  * case (the first pipeline stage), the variable is not yet set, so we want
72  * to store the current value of dot (produced by ::walk's callback) into the
73  * addrv.  This value is passed in directly from the parsing code as a parameter
74  * before the parser resets dot itself.  For subsequent pipeline stages, we
75  * need to store into addrv the value the variable previously held when the
76  * dcmd that produced this new value of dot was executed.  This value is
77  * stored in the corresponding index of the parent VCB's addrv.
78  *
79  * (4) mdb_vcb_find - Given an mdb_var_t, determines if there already exists a
80  * vcb for this variable, and if so returns it.  This allows us to avoid
81  * re-creating a vcb every time through a walk, such as:
82  *
83  * 	> ::walk proc p | ::walk proc v | ::eval "<p=Kn"
84  *
85  * In this case, we don't want to create a new vcb for 'v' every time we execute
86  * the second walk.
87  *
88  * Unfortunately, determining the addrv index is complicated by the fact that
89  * pipes involve the asynchronous execution of the dcmds and the parser.  This
90  * asynchrony means that the parser may not actually consume the output of a
91  * given dcmd until long after it has completed, and thus when the parser is
92  * ready to reset dot, it does not know what addrv index produced this value.
93  * We work around this problem by explicitly flushing the pipeline after each
94  * dcmd invocation if VCBs are active.  This does impact performance, so we
95  * may need to re-evaluate in the future if pipelines are producing huge
96  * amounts of data and a large number of VCBs are active simultaneously.
97  */
98 
99 #include <mdb/mdb_frame.h>
100 #include <mdb/mdb_debug.h>
101 #include <mdb/mdb_modapi.h>
102 #include <mdb/mdb_vcb.h>
103 #include <mdb/mdb.h>
104 
105 mdb_vcb_t *
106 mdb_vcb_create(mdb_var_t *v)
107 {
108 	mdb_vcb_t *vcb = mdb_zalloc(sizeof (mdb_vcb_t), UM_SLEEP);
109 	vcb->vc_var = v;
110 	return (vcb);
111 }
112 
113 void
114 mdb_vcb_destroy(mdb_vcb_t *vcb)
115 {
116 	mdb_dprintf(MDB_DBG_DSTK, "delete vcb %p (%s)\n", (void *)vcb,
117 	    mdb_nv_get_name(vcb->vc_var));
118 
119 	mdb_addrvec_destroy(&vcb->vc_addrv);
120 	mdb_free(vcb, sizeof (mdb_vcb_t));
121 }
122 
123 void
124 mdb_vcb_propagate(mdb_vcb_t *vcb)
125 {
126 	while (vcb != NULL) {
127 		mdb_addrvec_t *adp = &vcb->vc_addrv;
128 		ASSERT(vcb->vc_adnext < adp->ad_nelems);
129 		mdb_nv_set_value(vcb->vc_var, adp->ad_data[vcb->vc_adnext++]);
130 		vcb = vcb->vc_link;
131 	}
132 }
133 
134 void
135 mdb_vcb_purge(mdb_vcb_t *vcb)
136 {
137 	while (vcb != NULL) {
138 		mdb_vcb_t *n = vcb->vc_link;
139 		mdb_vcb_destroy(vcb);
140 		vcb = n;
141 	}
142 }
143 
144 void
145 mdb_vcb_inherit(mdb_cmd_t *src, mdb_cmd_t *dst)
146 {
147 	mdb_vcb_t *vc1, *vc2;
148 
149 	for (vc1 = src->c_vcbs; vc1 != NULL; vc1 = vc1->vc_link) {
150 		vc2 = mdb_vcb_create(vc1->vc_var);
151 		vc2->vc_parent = vc1;
152 		vc2->vc_link = dst->c_vcbs;
153 		dst->c_vcbs = vc2;
154 	}
155 }
156 
157 void
158 mdb_vcb_insert(mdb_vcb_t *vcb, mdb_frame_t *fp)
159 {
160 	if (fp->f_pcmd != NULL) {
161 		mdb_cmd_t *cp = fp->f_pcmd;
162 
163 		mdb_dprintf(MDB_DBG_DSTK, "insert vcb %p (%s)\n",
164 		    (void *)vcb, mdb_nv_get_name(vcb->vc_var));
165 
166 		ASSERT(vcb->vc_link == NULL);
167 		vcb->vc_link = cp->c_vcbs;
168 		cp->c_vcbs = vcb;
169 	}
170 }
171 
172 void
173 mdb_vcb_update(struct mdb_frame *fp, uintptr_t value)
174 {
175 	mdb_vcb_t *vcb;
176 
177 	for (vcb = fp->f_pcmd->c_vcbs; vcb != NULL; vcb = vcb->vc_link) {
178 		if (vcb->vc_parent != NULL) {
179 			mdb_addrvec_t *adp = &vcb->vc_parent->vc_addrv;
180 			adp->ad_ndx = vcb->vc_parent->vc_adnext - 1;
181 			ASSERT(adp->ad_ndx < adp->ad_nelems);
182 			value = adp->ad_data[adp->ad_ndx++];
183 		}
184 		mdb_addrvec_unshift(&vcb->vc_addrv, value);
185 	}
186 }
187 
188 mdb_vcb_t *
189 mdb_vcb_find(mdb_var_t *var, mdb_frame_t *fp)
190 {
191 	mdb_vcb_t *vcb;
192 
193 	if (fp->f_pcmd != NULL) {
194 		vcb = fp->f_pcmd->c_vcbs;
195 		while (vcb != NULL) {
196 			if (vcb->vc_var == var)
197 				return (vcb);
198 			vcb = vcb->vc_link;
199 		}
200 	}
201 
202 	return (NULL);
203 }
204