xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/avl.c (revision 892ad1623e11186cba8b2eb40d70318d2cb89605)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5b5fca8f8Stomee  * Common Development and Distribution License (the "License").
6b5fca8f8Stomee  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21fa9e4066Sahrens /*
22b5fca8f8Stomee  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23fa9e4066Sahrens  * Use is subject to license terms.
24fa9e4066Sahrens  */
252a12f85aSJeremy Jones /*
262a12f85aSJeremy Jones  * Copyright (c) 2013 by Delphix. All rights reserved.
272a12f85aSJeremy Jones  */
28fa9e4066Sahrens 
29fa9e4066Sahrens #include <sys/avl.h>
30fa9e4066Sahrens 
31fa9e4066Sahrens #include <mdb/mdb_modapi.h>
32fa9e4066Sahrens 
33fa9e4066Sahrens struct aw_info {
34b5fca8f8Stomee 	void *aw_buff;		/* buffer to hold tree element */
35fa9e4066Sahrens 	avl_tree_t aw_tree;	/* copy of avl_tree_t being walked */
36b5fca8f8Stomee 	uintptr_t aw_end;	/* last node in specified range */
37b5fca8f8Stomee 	const char *aw_elem_name;
38b5fca8f8Stomee 	int (*aw_elem_check)(void *, uintptr_t, void *);
39b5fca8f8Stomee 	void *aw_elem_check_arg;
40fa9e4066Sahrens };
41fa9e4066Sahrens 
42fa9e4066Sahrens /*
43fa9e4066Sahrens  * common code used to find the addr of the the leftmost child below
44fa9e4066Sahrens  * an AVL node
45fa9e4066Sahrens  */
46fa9e4066Sahrens static uintptr_t
avl_leftmostchild(uintptr_t addr,void * buff,size_t offset,size_t size,const char * elem_name)47b5fca8f8Stomee avl_leftmostchild(uintptr_t addr, void *buff, size_t offset, size_t size,
48b5fca8f8Stomee     const char *elem_name)
49fa9e4066Sahrens {
50fa9e4066Sahrens 	avl_node_t *node = (avl_node_t *)((uintptr_t)buff + offset);
51fa9e4066Sahrens 
52fa9e4066Sahrens 	for (;;) {
53fa9e4066Sahrens 		addr -= offset;
54fa9e4066Sahrens 		if (mdb_vread(buff, size, addr) == -1) {
55b5fca8f8Stomee 			mdb_warn("failed to read %s at %#lx", elem_name, addr);
56fa9e4066Sahrens 			return ((uintptr_t)-1L);
57fa9e4066Sahrens 		}
58fa9e4066Sahrens 		if (node->avl_child[0] == NULL)
59fa9e4066Sahrens 			break;
60fa9e4066Sahrens 		addr = (uintptr_t)node->avl_child[0];
61fa9e4066Sahrens 	}
62fa9e4066Sahrens 	return (addr);
63fa9e4066Sahrens }
64fa9e4066Sahrens 
65fa9e4066Sahrens /*
66fa9e4066Sahrens  * initialize a forward walk thru an avl tree.
67b5fca8f8Stomee  *
68b5fca8f8Stomee  * begin and end optionally specify objects other than the first and last
69b5fca8f8Stomee  * objects in the tree; either or both may be NULL (defaulting to first and
70b5fca8f8Stomee  * last).
71b5fca8f8Stomee  *
72b5fca8f8Stomee  * avl_name and element_name specify command-specific labels other than
73b5fca8f8Stomee  * "avl_tree_t" and "tree element" for use in error messages.
74b5fca8f8Stomee  *
75b5fca8f8Stomee  * element_check() returns -1, 1, or 0: abort the walk with an error, stop
76b5fca8f8Stomee  * without an error, or allow the normal callback; arg is an optional user
77b5fca8f8Stomee  * argument to element_check().
78fa9e4066Sahrens  */
79fa9e4066Sahrens 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)80b5fca8f8Stomee avl_walk_init_range(mdb_walk_state_t *wsp, uintptr_t begin, uintptr_t end,
81b5fca8f8Stomee     const char *avl_name, const char *element_name,
82b5fca8f8Stomee     int (*element_check)(void *, uintptr_t, void *), void *arg)
83fa9e4066Sahrens {
84fa9e4066Sahrens 	struct aw_info *aw;
85fa9e4066Sahrens 	avl_tree_t *tree;
86fa9e4066Sahrens 	uintptr_t addr;
87fa9e4066Sahrens 
88b5fca8f8Stomee 	if (avl_name == NULL)
89b5fca8f8Stomee 		avl_name = "avl_tree_t";
90b5fca8f8Stomee 	if (element_name == NULL)
91b5fca8f8Stomee 		element_name = "tree element";
92b5fca8f8Stomee 
93fa9e4066Sahrens 	/*
94fa9e4066Sahrens 	 * allocate the AVL walk data
95fa9e4066Sahrens 	 */
96fa9e4066Sahrens 	wsp->walk_data = aw = mdb_zalloc(sizeof (struct aw_info), UM_SLEEP);
97fa9e4066Sahrens 
98fa9e4066Sahrens 	/*
99fa9e4066Sahrens 	 * get an mdb copy of the avl_tree_t being walked
100fa9e4066Sahrens 	 */
101fa9e4066Sahrens 	tree = &aw->aw_tree;
102fa9e4066Sahrens 	if (mdb_vread(tree, sizeof (avl_tree_t), wsp->walk_addr) == -1) {
103b5fca8f8Stomee 		mdb_warn("failed to read %s at %#lx", avl_name, wsp->walk_addr);
104fa9e4066Sahrens 		goto error;
105fa9e4066Sahrens 	}
106fa9e4066Sahrens 	if (tree->avl_size < tree->avl_offset + sizeof (avl_node_t)) {
107fa9e4066Sahrens 		mdb_warn("invalid avl_tree_t at %p, avl_size:%d, avl_offset:%d",
108fa9e4066Sahrens 		    wsp->walk_addr, tree->avl_size, tree->avl_offset);
109fa9e4066Sahrens 		goto error;
110fa9e4066Sahrens 	}
111fa9e4066Sahrens 
112fa9e4066Sahrens 	/*
113fa9e4066Sahrens 	 * allocate a buffer to hold the mdb copy of tree's structs
114fa9e4066Sahrens 	 * "node" always points at the avl_node_t field inside the struct
115fa9e4066Sahrens 	 */
116fa9e4066Sahrens 	aw->aw_buff = mdb_zalloc(tree->avl_size, UM_SLEEP);
117*892ad162SToomas Soome 	aw->aw_end = (end == 0 ? 0 : end + tree->avl_offset);
118b5fca8f8Stomee 	aw->aw_elem_name = element_name;
119b5fca8f8Stomee 	aw->aw_elem_check = element_check;
120b5fca8f8Stomee 	aw->aw_elem_check_arg = arg;
121fa9e4066Sahrens 
122fa9e4066Sahrens 	/*
123fa9e4066Sahrens 	 * get the first avl_node_t address, use same algorithm
124fa9e4066Sahrens 	 * as avl_start() -- leftmost child in tree from root
125fa9e4066Sahrens 	 */
126*892ad162SToomas Soome 	if (begin == 0) {
127fa9e4066Sahrens 		addr = (uintptr_t)tree->avl_root;
128*892ad162SToomas Soome 		if (addr == 0) {
129*892ad162SToomas Soome 			wsp->walk_addr = 0;
130fa9e4066Sahrens 			return (WALK_NEXT);
131fa9e4066Sahrens 		}
132fa9e4066Sahrens 		addr = avl_leftmostchild(addr, aw->aw_buff, tree->avl_offset,
133b5fca8f8Stomee 		    tree->avl_size, aw->aw_elem_name);
134fa9e4066Sahrens 		if (addr == (uintptr_t)-1L)
135fa9e4066Sahrens 			goto error;
136fa9e4066Sahrens 		wsp->walk_addr = addr;
137b5fca8f8Stomee 	} else {
138b5fca8f8Stomee 		wsp->walk_addr = begin + tree->avl_offset;
139b5fca8f8Stomee 	}
140b5fca8f8Stomee 
141fa9e4066Sahrens 	return (WALK_NEXT);
142fa9e4066Sahrens 
143fa9e4066Sahrens error:
144fa9e4066Sahrens 	if (aw->aw_buff != NULL)
145fa9e4066Sahrens 		mdb_free(aw->aw_buff, sizeof (tree->avl_size));
146fa9e4066Sahrens 	mdb_free(aw, sizeof (struct aw_info));
147fa9e4066Sahrens 	return (WALK_ERR);
148fa9e4066Sahrens }
149fa9e4066Sahrens 
150b5fca8f8Stomee int
avl_walk_init(mdb_walk_state_t * wsp)151b5fca8f8Stomee avl_walk_init(mdb_walk_state_t *wsp)
152b5fca8f8Stomee {
153*892ad162SToomas Soome 	return (avl_walk_init_range(wsp, 0, 0, NULL, NULL, NULL, NULL));
154b5fca8f8Stomee }
155b5fca8f8Stomee 
156b5fca8f8Stomee int
avl_walk_init_named(mdb_walk_state_t * wsp,const char * avl_name,const char * element_name)157b5fca8f8Stomee avl_walk_init_named(mdb_walk_state_t *wsp,
158b5fca8f8Stomee     const char *avl_name, const char *element_name)
159b5fca8f8Stomee {
160*892ad162SToomas Soome 	return (avl_walk_init_range(wsp, 0, 0, avl_name, element_name,
161b5fca8f8Stomee 	    NULL, NULL));
162b5fca8f8Stomee }
163b5fca8f8Stomee 
164b5fca8f8Stomee 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)165b5fca8f8Stomee avl_walk_init_checked(mdb_walk_state_t *wsp,
166b5fca8f8Stomee     const char *avl_name, const char *element_name,
167b5fca8f8Stomee     int (*element_check)(void *, uintptr_t, void *), void *arg)
168b5fca8f8Stomee {
169*892ad162SToomas Soome 	return (avl_walk_init_range(wsp, 0, 0, avl_name, element_name,
170b5fca8f8Stomee 	    element_check, arg));
171b5fca8f8Stomee }
172b5fca8f8Stomee 
173fa9e4066Sahrens /*
174fa9e4066Sahrens  * At each step, visit (callback) the current node, then move to the next
175fa9e4066Sahrens  * in the AVL tree.  Uses the same algorithm as avl_walk().
176fa9e4066Sahrens  */
177fa9e4066Sahrens int
avl_walk_step(mdb_walk_state_t * wsp)178fa9e4066Sahrens avl_walk_step(mdb_walk_state_t *wsp)
179fa9e4066Sahrens {
180fa9e4066Sahrens 	struct aw_info *aw;
181fa9e4066Sahrens 	size_t offset;
182fa9e4066Sahrens 	size_t size;
183fa9e4066Sahrens 	uintptr_t addr;
184fa9e4066Sahrens 	avl_node_t *node;
185fa9e4066Sahrens 	int status;
186fa9e4066Sahrens 	int was_child;
187fa9e4066Sahrens 
188fa9e4066Sahrens 	/*
189fa9e4066Sahrens 	 * don't walk past the end of the tree!
190fa9e4066Sahrens 	 */
191fa9e4066Sahrens 	addr = wsp->walk_addr;
192*892ad162SToomas Soome 	if (addr == 0)
193fa9e4066Sahrens 		return (WALK_DONE);
194fa9e4066Sahrens 
195fa9e4066Sahrens 	aw = (struct aw_info *)wsp->walk_data;
196b5fca8f8Stomee 
197*892ad162SToomas Soome 	if (aw->aw_end != 0 && wsp->walk_addr == aw->aw_end)
198b5fca8f8Stomee 		return (WALK_DONE);
199b5fca8f8Stomee 
200fa9e4066Sahrens 	size = aw->aw_tree.avl_size;
201fa9e4066Sahrens 	offset = aw->aw_tree.avl_offset;
202fa9e4066Sahrens 	node = (avl_node_t *)((uintptr_t)aw->aw_buff + offset);
203fa9e4066Sahrens 
204fa9e4066Sahrens 	/*
205fa9e4066Sahrens 	 * must read the current node for the call back to use
206fa9e4066Sahrens 	 */
207fa9e4066Sahrens 	if (mdb_vread(aw->aw_buff, size, addr) == -1) {
208b5fca8f8Stomee 		mdb_warn("failed to read %s at %#lx", aw->aw_elem_name, addr);
209fa9e4066Sahrens 		return (WALK_ERR);
210fa9e4066Sahrens 	}
211fa9e4066Sahrens 
212b5fca8f8Stomee 	if (aw->aw_elem_check != NULL) {
213b5fca8f8Stomee 		int rc = aw->aw_elem_check(aw->aw_buff, addr,
214b5fca8f8Stomee 		    aw->aw_elem_check_arg);
215b5fca8f8Stomee 		if (rc == -1)
216b5fca8f8Stomee 			return (WALK_ERR);
217b5fca8f8Stomee 		else if (rc == 1)
218b5fca8f8Stomee 			return (WALK_DONE);
219b5fca8f8Stomee 	}
220b5fca8f8Stomee 
221fa9e4066Sahrens 	/*
222fa9e4066Sahrens 	 * do the call back
223fa9e4066Sahrens 	 */
224fa9e4066Sahrens 	status = wsp->walk_callback(addr, aw->aw_buff, wsp->walk_cbdata);
225fa9e4066Sahrens 	if (status != WALK_NEXT)
226fa9e4066Sahrens 		return (status);
227fa9e4066Sahrens 
228fa9e4066Sahrens 	/*
229fa9e4066Sahrens 	 * move to the next node....
230fa9e4066Sahrens 	 * note we read in new nodes, so the pointer to the buffer is fixed
231fa9e4066Sahrens 	 */
232fa9e4066Sahrens 
233fa9e4066Sahrens 	/*
234fa9e4066Sahrens 	 * if the node has a right child then go to it and then all the way
235fa9e4066Sahrens 	 * thru as many left children as possible
236fa9e4066Sahrens 	 */
237fa9e4066Sahrens 	addr = (uintptr_t)node->avl_child[1];
238*892ad162SToomas Soome 	if (addr != 0) {
239b5fca8f8Stomee 		addr = avl_leftmostchild(addr, aw->aw_buff, offset, size,
240b5fca8f8Stomee 		    aw->aw_elem_name);
241fa9e4066Sahrens 		if (addr == (uintptr_t)-1L)
242fa9e4066Sahrens 			return (WALK_ERR);
243fa9e4066Sahrens 
244fa9e4066Sahrens 	/*
245fa9e4066Sahrens 	 * othewise return to parent nodes, stopping if we ever return from
246fa9e4066Sahrens 	 * a left child
247fa9e4066Sahrens 	 */
248fa9e4066Sahrens 	} else {
249fa9e4066Sahrens 		for (;;) {
250fa9e4066Sahrens 			was_child = AVL_XCHILD(node);
251fa9e4066Sahrens 			addr = (uintptr_t)AVL_XPARENT(node);
252*892ad162SToomas Soome 			if (addr == 0)
253fa9e4066Sahrens 				break;
254fa9e4066Sahrens 			addr -= offset;
255fa9e4066Sahrens 			if (was_child == 0) /* stop on return from left child */
256fa9e4066Sahrens 				break;
257fa9e4066Sahrens 			if (mdb_vread(aw->aw_buff, size, addr) == -1) {
258b5fca8f8Stomee 				mdb_warn("failed to read %s at %#lx",
259b5fca8f8Stomee 				    aw->aw_elem_name, addr);
260fa9e4066Sahrens 				return (WALK_ERR);
261fa9e4066Sahrens 			}
262fa9e4066Sahrens 		}
263fa9e4066Sahrens 	}
264fa9e4066Sahrens 
265fa9e4066Sahrens 	wsp->walk_addr = addr;
266fa9e4066Sahrens 	return (WALK_NEXT);
267fa9e4066Sahrens }
268fa9e4066Sahrens 
269fa9e4066Sahrens /*
270fa9e4066Sahrens  * Release the memory allocated for the walk
271fa9e4066Sahrens  */
272fa9e4066Sahrens void
avl_walk_fini(mdb_walk_state_t * wsp)273fa9e4066Sahrens avl_walk_fini(mdb_walk_state_t *wsp)
274fa9e4066Sahrens {
275fa9e4066Sahrens 	struct aw_info *aw;
276fa9e4066Sahrens 
277fa9e4066Sahrens 	aw = (struct aw_info *)wsp->walk_data;
278fa9e4066Sahrens 
279fa9e4066Sahrens 	if (aw == NULL)
280fa9e4066Sahrens 		return;
281fa9e4066Sahrens 
282fa9e4066Sahrens 	if (aw->aw_buff != NULL)
283fa9e4066Sahrens 		mdb_free(aw->aw_buff, aw->aw_tree.avl_size);
284fa9e4066Sahrens 
285fa9e4066Sahrens 	mdb_free(aw, sizeof (struct aw_info));
286fa9e4066Sahrens }
2872a12f85aSJeremy Jones 
2882a12f85aSJeremy Jones /*
2892a12f85aSJeremy Jones  * This function is named avl_walk_mdb to avoid a naming conflict with the
2902a12f85aSJeremy Jones  * existing avl_walk function.
2912a12f85aSJeremy Jones  */
2922a12f85aSJeremy Jones int
avl_walk_mdb(uintptr_t addr,mdb_walk_cb_t callback,void * cbdata)2932a12f85aSJeremy Jones avl_walk_mdb(uintptr_t addr, mdb_walk_cb_t callback, void *cbdata)
2942a12f85aSJeremy Jones {
2952a12f85aSJeremy Jones 	mdb_walk_state_t ws;
2962a12f85aSJeremy Jones 	int ret;
2972a12f85aSJeremy Jones 
2982a12f85aSJeremy Jones 	ws.walk_addr = addr;
2992a12f85aSJeremy Jones 	ws.walk_callback = callback;
3002a12f85aSJeremy Jones 	ws.walk_cbdata = cbdata;
3012a12f85aSJeremy Jones 
3022a12f85aSJeremy Jones 	avl_walk_init(&ws);
3032a12f85aSJeremy Jones 	while ((ret = avl_walk_step(&ws)) == WALK_NEXT)
3042a12f85aSJeremy Jones 		continue;
3052a12f85aSJeremy Jones 	avl_walk_fini(&ws);
3062a12f85aSJeremy Jones 
3072a12f85aSJeremy Jones 	return (ret);
3082a12f85aSJeremy Jones }
309