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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <mdb/mdb_modapi.h> 27 28 typedef struct combined_walk { 29 int (*cw_init)(mdb_walk_state_t *); 30 int (*cw_step)(mdb_walk_state_t *); 31 void (*cw_fini)(mdb_walk_state_t *); 32 struct combined_walk *cw_next; 33 void *cw_data; 34 boolean_t cw_initialized; 35 } combined_walk_t; 36 37 typedef struct combined_walk_data { 38 uintptr_t cwd_initial_walk_addr; /* to init each walk */ 39 combined_walk_t *cwd_current_walk; 40 combined_walk_t *cwd_final_walk; /* tail pointer */ 41 42 struct combined_walk_data *cwd_next; 43 struct combined_walk_data *cwd_prev; 44 void *cwd_tag; /* used to find this data */ 45 } combined_walk_data_t; 46 47 /* 48 * Initialize a combined walk to 49 * A) present a single concatenated series of elements from different 50 * structures, or 51 * B) select from several possible walks at runtime. 52 * Multiple walks are done in the same order passed to combined_walk_add(). Each 53 * walk is initialized with the same wsp->walk_addr. 54 */ 55 void 56 combined_walk_init(mdb_walk_state_t *wsp) 57 { 58 combined_walk_data_t *cwd; 59 60 cwd = mdb_alloc(sizeof (combined_walk_data_t), UM_SLEEP); 61 62 cwd->cwd_initial_walk_addr = wsp->walk_addr; 63 cwd->cwd_current_walk = cwd->cwd_final_walk = NULL; 64 cwd->cwd_next = cwd->cwd_prev = NULL; 65 cwd->cwd_tag = NULL; 66 wsp->walk_data = cwd; 67 } 68 69 /* 70 * If a sub-walker's walk_step() is interrupted (by Ctrl-C or entering 'q' when 71 * prompted for the next screenful of data), there won't be an opportunity to 72 * switch wsp->walk_data from the sub-walker's data back to the combined walk 73 * data, since control will not return from walk_step(). Since mdb is 74 * single-threaded, we can save the combined walk data for combined_walk_fini() 75 * to use in case it was reached from an interrupted walk_step(). To allow for 76 * the possibility of nested combined walks, we'll save them on a list tagged by 77 * the sub-walker's data. 78 */ 79 static combined_walk_data_t *cwd_saved; 80 81 static void 82 combined_walk_data_save(combined_walk_data_t *cwd, void *tag) 83 { 84 cwd->cwd_next = cwd_saved; 85 cwd->cwd_prev = NULL; 86 if (cwd_saved != NULL) { 87 cwd_saved->cwd_prev = cwd; 88 } 89 cwd_saved = cwd; 90 cwd->cwd_tag = tag; 91 } 92 93 static void 94 combined_walk_data_drop(combined_walk_data_t *cwd) 95 { 96 if (cwd->cwd_prev == NULL) { 97 cwd_saved = cwd->cwd_next; 98 } else { 99 cwd->cwd_prev->cwd_next = cwd->cwd_next; 100 } 101 if (cwd->cwd_next != NULL) { 102 cwd->cwd_next->cwd_prev = cwd->cwd_prev; 103 } 104 cwd->cwd_next = cwd->cwd_prev = NULL; 105 cwd->cwd_tag = NULL; 106 } 107 108 static combined_walk_data_t * 109 combined_walk_data_find(void *tag) 110 { 111 combined_walk_data_t *cwd; 112 113 if (tag == NULL) { 114 return (NULL); 115 } 116 117 for (cwd = cwd_saved; cwd != NULL; cwd = cwd->cwd_next) { 118 if (cwd->cwd_tag == tag) { 119 return (cwd); 120 } 121 } 122 123 return (NULL); 124 } 125 126 static void 127 combined_walk_append(combined_walk_data_t *cwd, combined_walk_t *cw) 128 { 129 if (cwd->cwd_final_walk == NULL) { 130 cwd->cwd_current_walk = cwd->cwd_final_walk = cw; 131 } else { 132 cwd->cwd_final_walk->cw_next = cw; 133 cwd->cwd_final_walk = cw; 134 } 135 } 136 137 static combined_walk_t * 138 combined_walk_remove_current(combined_walk_data_t *cwd) 139 { 140 combined_walk_t *cw = cwd->cwd_current_walk; 141 if (cw == NULL) { 142 return (NULL); 143 } 144 if (cw == cwd->cwd_final_walk) { 145 cwd->cwd_final_walk = cw->cw_next; 146 } 147 cwd->cwd_current_walk = cw->cw_next; 148 cw->cw_next = NULL; 149 return (cw); 150 } 151 152 void 153 combined_walk_add(mdb_walk_state_t *wsp, 154 int (*walk_init)(mdb_walk_state_t *), 155 int (*walk_step)(mdb_walk_state_t *), 156 void (*walk_fini)(mdb_walk_state_t *)) 157 { 158 combined_walk_data_t *cwd = wsp->walk_data; 159 combined_walk_t *cw; 160 161 cw = mdb_alloc(sizeof (combined_walk_t), UM_SLEEP); 162 163 cw->cw_init = walk_init; 164 cw->cw_step = walk_step; 165 cw->cw_fini = walk_fini; 166 cw->cw_next = NULL; 167 cw->cw_data = NULL; 168 cw->cw_initialized = B_FALSE; 169 170 combined_walk_append(cwd, cw); 171 } 172 173 int 174 combined_walk_step(mdb_walk_state_t *wsp) 175 { 176 combined_walk_data_t *cwd = wsp->walk_data; 177 combined_walk_t *cw = cwd->cwd_current_walk; 178 int status; 179 180 if (cw == NULL) { 181 return (WALK_DONE); 182 } 183 184 if (cw->cw_initialized) { 185 wsp->walk_data = cw->cw_data; 186 } else { 187 wsp->walk_addr = cwd->cwd_initial_walk_addr; 188 status = cw->cw_init(wsp); 189 cw->cw_data = wsp->walk_data; 190 if (status != WALK_NEXT) 191 goto done; 192 cw->cw_initialized = B_TRUE; 193 } 194 195 /* save cwd for fini() in case step() is interrupted */ 196 combined_walk_data_save(cwd, cw->cw_data); 197 status = cw->cw_step(wsp); 198 /* control may never reach here */ 199 combined_walk_data_drop(cwd); 200 201 if (status == WALK_DONE) 202 goto done; 203 wsp->walk_data = cwd; 204 return (status); 205 206 done: 207 (void) combined_walk_remove_current(cwd); 208 if (cw->cw_initialized) 209 cw->cw_fini(wsp); 210 mdb_free(cw, sizeof (combined_walk_t)); 211 wsp->walk_data = cwd; 212 if (status == WALK_DONE) 213 return (combined_walk_step(wsp)); 214 return (status); 215 } 216 217 void 218 combined_walk_fini(mdb_walk_state_t *wsp) 219 { 220 combined_walk_data_t *cwd; 221 combined_walk_t *cw; 222 223 /* 224 * If walk_step() was interrupted, wsp->walk_data will be the 225 * sub-walker's data, not the combined walker's data, so first check to 226 * see if there is saved combined walk data tagged by the presumed 227 * sub-walker's walk data. 228 */ 229 cwd = combined_walk_data_find(wsp->walk_data); 230 if (cwd == NULL) { 231 /* 232 * walk_step() was not interrupted, so wsp->walk_data is 233 * actually the combined walk data. 234 */ 235 cwd = wsp->walk_data; 236 } else { 237 combined_walk_data_drop(cwd); 238 } 239 240 while ((cw = combined_walk_remove_current(cwd)) != NULL) { 241 if (cw->cw_initialized) { 242 wsp->walk_data = cw->cw_data; 243 cw->cw_fini(wsp); 244 } 245 mdb_free(cw, sizeof (combined_walk_t)); 246 } 247 248 mdb_free(cwd, sizeof (combined_walk_data_t)); 249 } 250