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 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <sys/avl.h>
29
30 #include <mdb/mdb_modapi.h>
31
32 struct aw_info {
33 void *aw_buff; /* buffer to hold tree element */
34 avl_tree_t aw_tree; /* copy of avl_tree_t being walked */
35 uintptr_t aw_end; /* last node in specified range */
36 const char *aw_elem_name;
37 int (*aw_elem_check)(void *, uintptr_t, void *);
38 void *aw_elem_check_arg;
39 };
40
41 /*
42 * common code used to find the addr of the the leftmost child below
43 * an AVL node
44 */
45 static uintptr_t
avl_leftmostchild(uintptr_t addr,void * buff,size_t offset,size_t size,const char * elem_name)46 avl_leftmostchild(uintptr_t addr, void *buff, size_t offset, size_t size,
47 const char *elem_name)
48 {
49 avl_node_t *node = (avl_node_t *)((uintptr_t)buff + offset);
50
51 for (;;) {
52 addr -= offset;
53 if (mdb_vread(buff, size, addr) == -1) {
54 mdb_warn("failed to read %s at %#lx", elem_name, addr);
55 return ((uintptr_t)-1L);
56 }
57 if (node->avl_child[0] == NULL)
58 break;
59 addr = (uintptr_t)node->avl_child[0];
60 }
61 return (addr);
62 }
63
64 /*
65 * initialize a forward walk thru an avl tree.
66 *
67 * begin and end optionally specify objects other than the first and last
68 * objects in the tree; either or both may be NULL (defaulting to first and
69 * last).
70 *
71 * avl_name and element_name specify command-specific labels other than
72 * "avl_tree_t" and "tree element" for use in error messages.
73 *
74 * element_check() returns -1, 1, or 0: abort the walk with an error, stop
75 * without an error, or allow the normal callback; arg is an optional user
76 * argument to element_check().
77 */
78 int
avl_walk_init_range(mdb_walk_state_t * wsp,uintptr_t begin,uintptr_t end,const char * avl_name,const char * element_name,int (* element_check)(void *,uintptr_t,void *),void * arg)79 avl_walk_init_range(mdb_walk_state_t *wsp, uintptr_t begin, uintptr_t end,
80 const char *avl_name, const char *element_name,
81 int (*element_check)(void *, uintptr_t, void *), void *arg)
82 {
83 struct aw_info *aw;
84 avl_tree_t *tree;
85 uintptr_t addr;
86
87 if (avl_name == NULL)
88 avl_name = "avl_tree_t";
89 if (element_name == NULL)
90 element_name = "tree element";
91
92 /*
93 * allocate the AVL walk data
94 */
95 wsp->walk_data = aw = mdb_zalloc(sizeof (struct aw_info), UM_SLEEP);
96
97 /*
98 * get an mdb copy of the avl_tree_t being walked
99 */
100 tree = &aw->aw_tree;
101 if (mdb_vread(tree, sizeof (avl_tree_t), wsp->walk_addr) == -1) {
102 mdb_warn("failed to read %s at %#lx", avl_name, wsp->walk_addr);
103 goto error;
104 }
105 if (tree->avl_size < tree->avl_offset + sizeof (avl_node_t)) {
106 mdb_warn("invalid avl_tree_t at %p, avl_size:%d, avl_offset:%d",
107 wsp->walk_addr, tree->avl_size, tree->avl_offset);
108 goto error;
109 }
110
111 /*
112 * allocate a buffer to hold the mdb copy of tree's structs
113 * "node" always points at the avl_node_t field inside the struct
114 */
115 aw->aw_buff = mdb_zalloc(tree->avl_size, UM_SLEEP);
116 aw->aw_end = (end == NULL ? NULL : end + tree->avl_offset);
117 aw->aw_elem_name = element_name;
118 aw->aw_elem_check = element_check;
119 aw->aw_elem_check_arg = arg;
120
121 /*
122 * get the first avl_node_t address, use same algorithm
123 * as avl_start() -- leftmost child in tree from root
124 */
125 if (begin == NULL) {
126 addr = (uintptr_t)tree->avl_root;
127 if (addr == NULL) {
128 wsp->walk_addr = NULL;
129 return (WALK_NEXT);
130 }
131 addr = avl_leftmostchild(addr, aw->aw_buff, tree->avl_offset,
132 tree->avl_size, aw->aw_elem_name);
133 if (addr == (uintptr_t)-1L)
134 goto error;
135 wsp->walk_addr = addr;
136 } else {
137 wsp->walk_addr = begin + tree->avl_offset;
138 }
139
140 return (WALK_NEXT);
141
142 error:
143 if (aw->aw_buff != NULL)
144 mdb_free(aw->aw_buff, sizeof (tree->avl_size));
145 mdb_free(aw, sizeof (struct aw_info));
146 return (WALK_ERR);
147 }
148
149 int
avl_walk_init(mdb_walk_state_t * wsp)150 avl_walk_init(mdb_walk_state_t *wsp)
151 {
152 return (avl_walk_init_range(wsp, NULL, NULL, NULL, NULL, NULL, NULL));
153 }
154
155 int
avl_walk_init_named(mdb_walk_state_t * wsp,const char * avl_name,const char * element_name)156 avl_walk_init_named(mdb_walk_state_t *wsp,
157 const char *avl_name, const char *element_name)
158 {
159 return (avl_walk_init_range(wsp, NULL, NULL, avl_name, element_name,
160 NULL, NULL));
161 }
162
163 int
avl_walk_init_checked(mdb_walk_state_t * wsp,const char * avl_name,const char * element_name,int (* element_check)(void *,uintptr_t,void *),void * arg)164 avl_walk_init_checked(mdb_walk_state_t *wsp,
165 const char *avl_name, const char *element_name,
166 int (*element_check)(void *, uintptr_t, void *), void *arg)
167 {
168 return (avl_walk_init_range(wsp, NULL, NULL, avl_name, element_name,
169 element_check, arg));
170 }
171
172 /*
173 * At each step, visit (callback) the current node, then move to the next
174 * in the AVL tree. Uses the same algorithm as avl_walk().
175 */
176 int
avl_walk_step(mdb_walk_state_t * wsp)177 avl_walk_step(mdb_walk_state_t *wsp)
178 {
179 struct aw_info *aw;
180 size_t offset;
181 size_t size;
182 uintptr_t addr;
183 avl_node_t *node;
184 int status;
185 int was_child;
186
187 /*
188 * don't walk past the end of the tree!
189 */
190 addr = wsp->walk_addr;
191 if (addr == NULL)
192 return (WALK_DONE);
193
194 aw = (struct aw_info *)wsp->walk_data;
195
196 if (aw->aw_end != NULL && wsp->walk_addr == aw->aw_end)
197 return (WALK_DONE);
198
199 size = aw->aw_tree.avl_size;
200 offset = aw->aw_tree.avl_offset;
201 node = (avl_node_t *)((uintptr_t)aw->aw_buff + offset);
202
203 /*
204 * must read the current node for the call back to use
205 */
206 if (mdb_vread(aw->aw_buff, size, addr) == -1) {
207 mdb_warn("failed to read %s at %#lx", aw->aw_elem_name, addr);
208 return (WALK_ERR);
209 }
210
211 if (aw->aw_elem_check != NULL) {
212 int rc = aw->aw_elem_check(aw->aw_buff, addr,
213 aw->aw_elem_check_arg);
214 if (rc == -1)
215 return (WALK_ERR);
216 else if (rc == 1)
217 return (WALK_DONE);
218 }
219
220 /*
221 * do the call back
222 */
223 status = wsp->walk_callback(addr, aw->aw_buff, wsp->walk_cbdata);
224 if (status != WALK_NEXT)
225 return (status);
226
227 /*
228 * move to the next node....
229 * note we read in new nodes, so the pointer to the buffer is fixed
230 */
231
232 /*
233 * if the node has a right child then go to it and then all the way
234 * thru as many left children as possible
235 */
236 addr = (uintptr_t)node->avl_child[1];
237 if (addr != NULL) {
238 addr = avl_leftmostchild(addr, aw->aw_buff, offset, size,
239 aw->aw_elem_name);
240 if (addr == (uintptr_t)-1L)
241 return (WALK_ERR);
242
243 /*
244 * othewise return to parent nodes, stopping if we ever return from
245 * a left child
246 */
247 } else {
248 for (;;) {
249 was_child = AVL_XCHILD(node);
250 addr = (uintptr_t)AVL_XPARENT(node);
251 if (addr == NULL)
252 break;
253 addr -= offset;
254 if (was_child == 0) /* stop on return from left child */
255 break;
256 if (mdb_vread(aw->aw_buff, size, addr) == -1) {
257 mdb_warn("failed to read %s at %#lx",
258 aw->aw_elem_name, addr);
259 return (WALK_ERR);
260 }
261 }
262 }
263
264 wsp->walk_addr = addr;
265 return (WALK_NEXT);
266 }
267
268 /*
269 * Release the memory allocated for the walk
270 */
271 void
avl_walk_fini(mdb_walk_state_t * wsp)272 avl_walk_fini(mdb_walk_state_t *wsp)
273 {
274 struct aw_info *aw;
275
276 aw = (struct aw_info *)wsp->walk_data;
277
278 if (aw == NULL)
279 return;
280
281 if (aw->aw_buff != NULL)
282 mdb_free(aw->aw_buff, aw->aw_tree.avl_size);
283
284 mdb_free(aw, sizeof (struct aw_info));
285 }
286