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
combined_walk_init(mdb_walk_state_t * wsp)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
combined_walk_data_save(combined_walk_data_t * cwd,void * tag)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
combined_walk_data_drop(combined_walk_data_t * cwd)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 *
combined_walk_data_find(void * tag)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
combined_walk_append(combined_walk_data_t * cwd,combined_walk_t * cw)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 *
combined_walk_remove_current(combined_walk_data_t * cwd)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
combined_walk_add(mdb_walk_state_t * wsp,int (* walk_init)(mdb_walk_state_t *),int (* walk_step)(mdb_walk_state_t *),void (* walk_fini)(mdb_walk_state_t *))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
combined_walk_step(mdb_walk_state_t * wsp)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
combined_walk_fini(mdb_walk_state_t * wsp)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