xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/combined.c (revision 8074cb1b2b37aa84a83861a8556aa1a67d0a7d8c)
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 2008 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 		cw->cw_initialized = B_TRUE;
191 		if (status != WALK_NEXT) {
192 			wsp->walk_data = cwd;
193 			return (status);
194 		}
195 	}
196 
197 	/* save cwd for fini() in case step() is interrupted */
198 	combined_walk_data_save(cwd, cw->cw_data);
199 	status = cw->cw_step(wsp);
200 	/* control may never reach here */
201 	combined_walk_data_drop(cwd);
202 
203 	if (status == WALK_DONE) {
204 		(void) combined_walk_remove_current(cwd);
205 		cw->cw_fini(wsp);
206 		mdb_free(cw, sizeof (combined_walk_t));
207 		wsp->walk_data = cwd;
208 		return (combined_walk_step(wsp));
209 	}
210 
211 	wsp->walk_data = cwd;
212 	return (status);
213 }
214 
215 void
216 combined_walk_fini(mdb_walk_state_t *wsp)
217 {
218 	combined_walk_data_t *cwd;
219 	combined_walk_t *cw;
220 
221 	/*
222 	 * If walk_step() was interrupted, wsp->walk_data will be the
223 	 * sub-walker's data, not the combined walker's data, so first check to
224 	 * see if there is saved combined walk data tagged by the presumed
225 	 * sub-walker's walk data.
226 	 */
227 	cwd = combined_walk_data_find(wsp->walk_data);
228 	if (cwd == NULL) {
229 		/*
230 		 * walk_step() was not interrupted, so wsp->walk_data is
231 		 * actually the combined walk data.
232 		 */
233 		cwd = wsp->walk_data;
234 	} else {
235 		combined_walk_data_drop(cwd);
236 	}
237 
238 	while ((cw = combined_walk_remove_current(cwd)) != NULL) {
239 		if (cw->cw_initialized) {
240 			wsp->walk_data = cw->cw_data;
241 			cw->cw_fini(wsp);
242 		}
243 		mdb_free(cw, sizeof (combined_walk_t));
244 	}
245 
246 	mdb_free(cwd, sizeof (combined_walk_data_t));
247 }
248