xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/avl.c (revision 6712:79afecec3f3c)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
5*6712Stomee  * Common Development and Distribution License (the "License").
6*6712Stomee  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
22*6712Stomee  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27789Sahrens 
28789Sahrens #include <sys/avl.h>
29789Sahrens 
30789Sahrens #include <mdb/mdb_modapi.h>
31789Sahrens 
32789Sahrens struct aw_info {
33*6712Stomee 	void *aw_buff;		/* buffer to hold tree element */
34789Sahrens 	avl_tree_t aw_tree;	/* copy of avl_tree_t being walked */
35*6712Stomee 	uintptr_t aw_end;	/* last node in specified range */
36*6712Stomee 	const char *aw_elem_name;
37*6712Stomee 	int (*aw_elem_check)(void *, uintptr_t, void *);
38*6712Stomee 	void *aw_elem_check_arg;
39789Sahrens };
40789Sahrens 
41789Sahrens /*
42789Sahrens  * common code used to find the addr of the the leftmost child below
43789Sahrens  * an AVL node
44789Sahrens  */
45789Sahrens static uintptr_t
avl_leftmostchild(uintptr_t addr,void * buff,size_t offset,size_t size,const char * elem_name)46*6712Stomee avl_leftmostchild(uintptr_t addr, void *buff, size_t offset, size_t size,
47*6712Stomee     const char *elem_name)
48789Sahrens {
49789Sahrens 	avl_node_t *node = (avl_node_t *)((uintptr_t)buff + offset);
50789Sahrens 
51789Sahrens 	for (;;) {
52789Sahrens 		addr -= offset;
53789Sahrens 		if (mdb_vread(buff, size, addr) == -1) {
54*6712Stomee 			mdb_warn("failed to read %s at %#lx", elem_name, addr);
55789Sahrens 			return ((uintptr_t)-1L);
56789Sahrens 		}
57789Sahrens 		if (node->avl_child[0] == NULL)
58789Sahrens 			break;
59789Sahrens 		addr = (uintptr_t)node->avl_child[0];
60789Sahrens 	}
61789Sahrens 	return (addr);
62789Sahrens }
63789Sahrens 
64789Sahrens /*
65789Sahrens  * initialize a forward walk thru an avl tree.
66*6712Stomee  *
67*6712Stomee  * begin and end optionally specify objects other than the first and last
68*6712Stomee  * objects in the tree; either or both may be NULL (defaulting to first and
69*6712Stomee  * last).
70*6712Stomee  *
71*6712Stomee  * avl_name and element_name specify command-specific labels other than
72*6712Stomee  * "avl_tree_t" and "tree element" for use in error messages.
73*6712Stomee  *
74*6712Stomee  * element_check() returns -1, 1, or 0: abort the walk with an error, stop
75*6712Stomee  * without an error, or allow the normal callback; arg is an optional user
76*6712Stomee  * argument to element_check().
77789Sahrens  */
78789Sahrens 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*6712Stomee avl_walk_init_range(mdb_walk_state_t *wsp, uintptr_t begin, uintptr_t end,
80*6712Stomee     const char *avl_name, const char *element_name,
81*6712Stomee     int (*element_check)(void *, uintptr_t, void *), void *arg)
82789Sahrens {
83789Sahrens 	struct aw_info *aw;
84789Sahrens 	avl_tree_t *tree;
85789Sahrens 	uintptr_t addr;
86789Sahrens 
87*6712Stomee 	if (avl_name == NULL)
88*6712Stomee 		avl_name = "avl_tree_t";
89*6712Stomee 	if (element_name == NULL)
90*6712Stomee 		element_name = "tree element";
91*6712Stomee 
92789Sahrens 	/*
93789Sahrens 	 * allocate the AVL walk data
94789Sahrens 	 */
95789Sahrens 	wsp->walk_data = aw = mdb_zalloc(sizeof (struct aw_info), UM_SLEEP);
96789Sahrens 
97789Sahrens 	/*
98789Sahrens 	 * get an mdb copy of the avl_tree_t being walked
99789Sahrens 	 */
100789Sahrens 	tree = &aw->aw_tree;
101789Sahrens 	if (mdb_vread(tree, sizeof (avl_tree_t), wsp->walk_addr) == -1) {
102*6712Stomee 		mdb_warn("failed to read %s at %#lx", avl_name, wsp->walk_addr);
103789Sahrens 		goto error;
104789Sahrens 	}
105789Sahrens 	if (tree->avl_size < tree->avl_offset + sizeof (avl_node_t)) {
106789Sahrens 		mdb_warn("invalid avl_tree_t at %p, avl_size:%d, avl_offset:%d",
107789Sahrens 		    wsp->walk_addr, tree->avl_size, tree->avl_offset);
108789Sahrens 		goto error;
109789Sahrens 	}
110789Sahrens 
111789Sahrens 	/*
112789Sahrens 	 * allocate a buffer to hold the mdb copy of tree's structs
113789Sahrens 	 * "node" always points at the avl_node_t field inside the struct
114789Sahrens 	 */
115789Sahrens 	aw->aw_buff = mdb_zalloc(tree->avl_size, UM_SLEEP);
116*6712Stomee 	aw->aw_end = (end == NULL ? NULL : end + tree->avl_offset);
117*6712Stomee 	aw->aw_elem_name = element_name;
118*6712Stomee 	aw->aw_elem_check = element_check;
119*6712Stomee 	aw->aw_elem_check_arg = arg;
120789Sahrens 
121789Sahrens 	/*
122789Sahrens 	 * get the first avl_node_t address, use same algorithm
123789Sahrens 	 * as avl_start() -- leftmost child in tree from root
124789Sahrens 	 */
125*6712Stomee 	if (begin == NULL) {
126*6712Stomee 		addr = (uintptr_t)tree->avl_root;
127*6712Stomee 		if (addr == NULL) {
128*6712Stomee 			wsp->walk_addr = NULL;
129*6712Stomee 			return (WALK_NEXT);
130*6712Stomee 		}
131*6712Stomee 		addr = avl_leftmostchild(addr, aw->aw_buff, tree->avl_offset,
132*6712Stomee 		    tree->avl_size, aw->aw_elem_name);
133*6712Stomee 		if (addr == (uintptr_t)-1L)
134*6712Stomee 			goto error;
135*6712Stomee 		wsp->walk_addr = addr;
136*6712Stomee 	} else {
137*6712Stomee 		wsp->walk_addr = begin + tree->avl_offset;
138789Sahrens 	}
139789Sahrens 
140789Sahrens 	return (WALK_NEXT);
141789Sahrens 
142789Sahrens error:
143789Sahrens 	if (aw->aw_buff != NULL)
144789Sahrens 		mdb_free(aw->aw_buff, sizeof (tree->avl_size));
145789Sahrens 	mdb_free(aw, sizeof (struct aw_info));
146789Sahrens 	return (WALK_ERR);
147789Sahrens }
148789Sahrens 
149*6712Stomee int
avl_walk_init(mdb_walk_state_t * wsp)150*6712Stomee avl_walk_init(mdb_walk_state_t *wsp)
151*6712Stomee {
152*6712Stomee 	return (avl_walk_init_range(wsp, NULL, NULL, NULL, NULL, NULL, NULL));
153*6712Stomee }
154*6712Stomee 
155*6712Stomee int
avl_walk_init_named(mdb_walk_state_t * wsp,const char * avl_name,const char * element_name)156*6712Stomee avl_walk_init_named(mdb_walk_state_t *wsp,
157*6712Stomee     const char *avl_name, const char *element_name)
158*6712Stomee {
159*6712Stomee 	return (avl_walk_init_range(wsp, NULL, NULL, avl_name, element_name,
160*6712Stomee 	    NULL, NULL));
161*6712Stomee }
162*6712Stomee 
163*6712Stomee 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*6712Stomee avl_walk_init_checked(mdb_walk_state_t *wsp,
165*6712Stomee     const char *avl_name, const char *element_name,
166*6712Stomee     int (*element_check)(void *, uintptr_t, void *), void *arg)
167*6712Stomee {
168*6712Stomee 	return (avl_walk_init_range(wsp, NULL, NULL, avl_name, element_name,
169*6712Stomee 	    element_check, arg));
170*6712Stomee }
171*6712Stomee 
172789Sahrens /*
173789Sahrens  * At each step, visit (callback) the current node, then move to the next
174789Sahrens  * in the AVL tree.  Uses the same algorithm as avl_walk().
175789Sahrens  */
176789Sahrens int
avl_walk_step(mdb_walk_state_t * wsp)177789Sahrens avl_walk_step(mdb_walk_state_t *wsp)
178789Sahrens {
179789Sahrens 	struct aw_info *aw;
180789Sahrens 	size_t offset;
181789Sahrens 	size_t size;
182789Sahrens 	uintptr_t addr;
183789Sahrens 	avl_node_t *node;
184789Sahrens 	int status;
185789Sahrens 	int was_child;
186789Sahrens 
187789Sahrens 	/*
188789Sahrens 	 * don't walk past the end of the tree!
189789Sahrens 	 */
190789Sahrens 	addr = wsp->walk_addr;
191789Sahrens 	if (addr == NULL)
192789Sahrens 		return (WALK_DONE);
193789Sahrens 
194789Sahrens 	aw = (struct aw_info *)wsp->walk_data;
195*6712Stomee 
196*6712Stomee 	if (aw->aw_end != NULL && wsp->walk_addr == aw->aw_end)
197*6712Stomee 		return (WALK_DONE);
198*6712Stomee 
199789Sahrens 	size = aw->aw_tree.avl_size;
200789Sahrens 	offset = aw->aw_tree.avl_offset;
201789Sahrens 	node = (avl_node_t *)((uintptr_t)aw->aw_buff + offset);
202789Sahrens 
203789Sahrens 	/*
204789Sahrens 	 * must read the current node for the call back to use
205789Sahrens 	 */
206789Sahrens 	if (mdb_vread(aw->aw_buff, size, addr) == -1) {
207*6712Stomee 		mdb_warn("failed to read %s at %#lx", aw->aw_elem_name, addr);
208789Sahrens 		return (WALK_ERR);
209789Sahrens 	}
210789Sahrens 
211*6712Stomee 	if (aw->aw_elem_check != NULL) {
212*6712Stomee 		int rc = aw->aw_elem_check(aw->aw_buff, addr,
213*6712Stomee 		    aw->aw_elem_check_arg);
214*6712Stomee 		if (rc == -1)
215*6712Stomee 			return (WALK_ERR);
216*6712Stomee 		else if (rc == 1)
217*6712Stomee 			return (WALK_DONE);
218*6712Stomee 	}
219*6712Stomee 
220789Sahrens 	/*
221789Sahrens 	 * do the call back
222789Sahrens 	 */
223789Sahrens 	status = wsp->walk_callback(addr, aw->aw_buff, wsp->walk_cbdata);
224789Sahrens 	if (status != WALK_NEXT)
225789Sahrens 		return (status);
226789Sahrens 
227789Sahrens 	/*
228789Sahrens 	 * move to the next node....
229789Sahrens 	 * note we read in new nodes, so the pointer to the buffer is fixed
230789Sahrens 	 */
231789Sahrens 
232789Sahrens 	/*
233789Sahrens 	 * if the node has a right child then go to it and then all the way
234789Sahrens 	 * thru as many left children as possible
235789Sahrens 	 */
236789Sahrens 	addr = (uintptr_t)node->avl_child[1];
237789Sahrens 	if (addr != NULL) {
238*6712Stomee 		addr = avl_leftmostchild(addr, aw->aw_buff, offset, size,
239*6712Stomee 		    aw->aw_elem_name);
240789Sahrens 		if (addr == (uintptr_t)-1L)
241789Sahrens 			return (WALK_ERR);
242789Sahrens 
243789Sahrens 	/*
244789Sahrens 	 * othewise return to parent nodes, stopping if we ever return from
245789Sahrens 	 * a left child
246789Sahrens 	 */
247789Sahrens 	} else {
248789Sahrens 		for (;;) {
249789Sahrens 			was_child = AVL_XCHILD(node);
250789Sahrens 			addr = (uintptr_t)AVL_XPARENT(node);
251789Sahrens 			if (addr == NULL)
252789Sahrens 				break;
253789Sahrens 			addr -= offset;
254789Sahrens 			if (was_child == 0) /* stop on return from left child */
255789Sahrens 				break;
256789Sahrens 			if (mdb_vread(aw->aw_buff, size, addr) == -1) {
257*6712Stomee 				mdb_warn("failed to read %s at %#lx",
258*6712Stomee 				    aw->aw_elem_name, addr);
259789Sahrens 				return (WALK_ERR);
260789Sahrens 			}
261789Sahrens 		}
262789Sahrens 	}
263789Sahrens 
264789Sahrens 	wsp->walk_addr = addr;
265789Sahrens 	return (WALK_NEXT);
266789Sahrens }
267789Sahrens 
268789Sahrens /*
269789Sahrens  * Release the memory allocated for the walk
270789Sahrens  */
271789Sahrens void
avl_walk_fini(mdb_walk_state_t * wsp)272789Sahrens avl_walk_fini(mdb_walk_state_t *wsp)
273789Sahrens {
274789Sahrens 	struct aw_info *aw;
275789Sahrens 
276789Sahrens 	aw = (struct aw_info *)wsp->walk_data;
277789Sahrens 
278789Sahrens 	if (aw == NULL)
279789Sahrens 		return;
280789Sahrens 
281789Sahrens 	if (aw->aw_buff != NULL)
282789Sahrens 		mdb_free(aw->aw_buff, aw->aw_tree.avl_size);
283789Sahrens 
284789Sahrens 	mdb_free(aw, sizeof (struct aw_info));
285789Sahrens }
286