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 * 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 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 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 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 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 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 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 * 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