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