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