xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/avl.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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
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
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
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
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
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
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
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