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 *
mdb_vcb_create(mdb_var_t * v)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
mdb_vcb_destroy(mdb_vcb_t * vcb)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
mdb_vcb_propagate(mdb_vcb_t * vcb)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
mdb_vcb_purge(mdb_vcb_t * vcb)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
mdb_vcb_inherit(mdb_cmd_t * src,mdb_cmd_t * dst)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
mdb_vcb_insert(mdb_vcb_t * vcb,mdb_frame_t * fp)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
mdb_vcb_update(struct mdb_frame * fp,uintptr_t value)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 *
mdb_vcb_find(mdb_var_t * var,mdb_frame_t * fp)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