1*789Sahrens /*
2*789Sahrens  * CDDL HEADER START
3*789Sahrens  *
4*789Sahrens  * The contents of this file are subject to the terms of the
5*789Sahrens  * Common Development and Distribution License, Version 1.0 only
6*789Sahrens  * (the "License").  You may not use this file except in compliance
7*789Sahrens  * with the License.
8*789Sahrens  *
9*789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*789Sahrens  * or http://www.opensolaris.org/os/licensing.
11*789Sahrens  * See the License for the specific language governing permissions
12*789Sahrens  * and limitations under the License.
13*789Sahrens  *
14*789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15*789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17*789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18*789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19*789Sahrens  *
20*789Sahrens  * CDDL HEADER END
21*789Sahrens  */
22*789Sahrens /*
23*789Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*789Sahrens  * Use is subject to license terms.
25*789Sahrens  */
26*789Sahrens 
27*789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*789Sahrens 
29*789Sahrens #include <sys/avl.h>
30*789Sahrens 
31*789Sahrens #include <mdb/mdb_modapi.h>
32*789Sahrens 
33*789Sahrens struct aw_info {
34*789Sahrens 	void *aw_buff;		/* buffer to hold the tree's data structure */
35*789Sahrens 	avl_tree_t aw_tree;	/* copy of avl_tree_t being walked */
36*789Sahrens };
37*789Sahrens 
38*789Sahrens /*
39*789Sahrens  * common code used to find the addr of the the leftmost child below
40*789Sahrens  * an AVL node
41*789Sahrens  */
42*789Sahrens static uintptr_t
43*789Sahrens avl_leftmostchild(uintptr_t addr, void * buff, size_t offset, size_t size)
44*789Sahrens {
45*789Sahrens 	avl_node_t *node = (avl_node_t *)((uintptr_t)buff + offset);
46*789Sahrens 
47*789Sahrens 	for (;;) {
48*789Sahrens 		addr -= offset;
49*789Sahrens 		if (mdb_vread(buff, size, addr) == -1) {
50*789Sahrens 			mdb_warn("read of avl_node_t failed: %p", addr);
51*789Sahrens 			return ((uintptr_t)-1L);
52*789Sahrens 		}
53*789Sahrens 		if (node->avl_child[0] == NULL)
54*789Sahrens 			break;
55*789Sahrens 		addr = (uintptr_t)node->avl_child[0];
56*789Sahrens 	}
57*789Sahrens 	return (addr);
58*789Sahrens }
59*789Sahrens 
60*789Sahrens /*
61*789Sahrens  * initialize a forward walk thru an avl tree.
62*789Sahrens  */
63*789Sahrens int
64*789Sahrens avl_walk_init(mdb_walk_state_t *wsp)
65*789Sahrens {
66*789Sahrens 	struct aw_info *aw;
67*789Sahrens 	avl_tree_t *tree;
68*789Sahrens 	uintptr_t addr;
69*789Sahrens 
70*789Sahrens 	/*
71*789Sahrens 	 * allocate the AVL walk data
72*789Sahrens 	 */
73*789Sahrens 	wsp->walk_data = aw = mdb_zalloc(sizeof (struct aw_info), UM_SLEEP);
74*789Sahrens 
75*789Sahrens 	/*
76*789Sahrens 	 * get an mdb copy of the avl_tree_t being walked
77*789Sahrens 	 */
78*789Sahrens 	tree = &aw->aw_tree;
79*789Sahrens 	if (mdb_vread(tree, sizeof (avl_tree_t), wsp->walk_addr) == -1) {
80*789Sahrens 		mdb_warn("read of avl_tree_t failed: %p", wsp->walk_addr);
81*789Sahrens 		goto error;
82*789Sahrens 	}
83*789Sahrens 	if (tree->avl_size < tree->avl_offset + sizeof (avl_node_t)) {
84*789Sahrens 		mdb_warn("invalid avl_tree_t at %p, avl_size:%d, avl_offset:%d",
85*789Sahrens 		    wsp->walk_addr, tree->avl_size, tree->avl_offset);
86*789Sahrens 		goto error;
87*789Sahrens 	}
88*789Sahrens 
89*789Sahrens 	/*
90*789Sahrens 	 * allocate a buffer to hold the mdb copy of tree's structs
91*789Sahrens 	 * "node" always points at the avl_node_t field inside the struct
92*789Sahrens 	 */
93*789Sahrens 	aw->aw_buff = mdb_zalloc(tree->avl_size, UM_SLEEP);
94*789Sahrens 
95*789Sahrens 	/*
96*789Sahrens 	 * get the first avl_node_t address, use same algorithm
97*789Sahrens 	 * as avl_start() -- leftmost child in tree from root
98*789Sahrens 	 */
99*789Sahrens 	addr = (uintptr_t)tree->avl_root;
100*789Sahrens 	if (addr == NULL) {
101*789Sahrens 		wsp->walk_addr = NULL;
102*789Sahrens 		return (WALK_NEXT);
103*789Sahrens 	}
104*789Sahrens 	addr = avl_leftmostchild(addr, aw->aw_buff, tree->avl_offset,
105*789Sahrens 	    tree->avl_size);
106*789Sahrens 	if (addr == (uintptr_t)-1L)
107*789Sahrens 		goto error;
108*789Sahrens 
109*789Sahrens 	wsp->walk_addr = addr;
110*789Sahrens 	return (WALK_NEXT);
111*789Sahrens 
112*789Sahrens error:
113*789Sahrens 	if (aw->aw_buff != NULL)
114*789Sahrens 		mdb_free(aw->aw_buff, sizeof (tree->avl_size));
115*789Sahrens 	mdb_free(aw, sizeof (struct aw_info));
116*789Sahrens 	return (WALK_ERR);
117*789Sahrens }
118*789Sahrens 
119*789Sahrens /*
120*789Sahrens  * At each step, visit (callback) the current node, then move to the next
121*789Sahrens  * in the AVL tree.  Uses the same algorithm as avl_walk().
122*789Sahrens  */
123*789Sahrens int
124*789Sahrens avl_walk_step(mdb_walk_state_t *wsp)
125*789Sahrens {
126*789Sahrens 	struct aw_info *aw;
127*789Sahrens 	size_t offset;
128*789Sahrens 	size_t size;
129*789Sahrens 	uintptr_t addr;
130*789Sahrens 	avl_node_t *node;
131*789Sahrens 	int status;
132*789Sahrens 	int was_child;
133*789Sahrens 
134*789Sahrens 	/*
135*789Sahrens 	 * don't walk past the end of the tree!
136*789Sahrens 	 */
137*789Sahrens 	addr = wsp->walk_addr;
138*789Sahrens 	if (addr == NULL)
139*789Sahrens 		return (WALK_DONE);
140*789Sahrens 
141*789Sahrens 	aw = (struct aw_info *)wsp->walk_data;
142*789Sahrens 	size = aw->aw_tree.avl_size;
143*789Sahrens 	offset = aw->aw_tree.avl_offset;
144*789Sahrens 	node = (avl_node_t *)((uintptr_t)aw->aw_buff + offset);
145*789Sahrens 
146*789Sahrens 	/*
147*789Sahrens 	 * must read the current node for the call back to use
148*789Sahrens 	 */
149*789Sahrens 	if (mdb_vread(aw->aw_buff, size, addr) == -1) {
150*789Sahrens 		mdb_warn("read of avl_node_t failed: %p", addr);
151*789Sahrens 		return (WALK_ERR);
152*789Sahrens 	}
153*789Sahrens 
154*789Sahrens 	/*
155*789Sahrens 	 * do the call back
156*789Sahrens 	 */
157*789Sahrens 	status = wsp->walk_callback(addr, aw->aw_buff, wsp->walk_cbdata);
158*789Sahrens 	if (status != WALK_NEXT)
159*789Sahrens 		return (status);
160*789Sahrens 
161*789Sahrens 	/*
162*789Sahrens 	 * move to the next node....
163*789Sahrens 	 * note we read in new nodes, so the pointer to the buffer is fixed
164*789Sahrens 	 */
165*789Sahrens 
166*789Sahrens 	/*
167*789Sahrens 	 * if the node has a right child then go to it and then all the way
168*789Sahrens 	 * thru as many left children as possible
169*789Sahrens 	 */
170*789Sahrens 	addr = (uintptr_t)node->avl_child[1];
171*789Sahrens 	if (addr != NULL) {
172*789Sahrens 		addr = avl_leftmostchild(addr, aw->aw_buff, offset, size);
173*789Sahrens 		if (addr == (uintptr_t)-1L)
174*789Sahrens 			return (WALK_ERR);
175*789Sahrens 
176*789Sahrens 	/*
177*789Sahrens 	 * othewise return to parent nodes, stopping if we ever return from
178*789Sahrens 	 * a left child
179*789Sahrens 	 */
180*789Sahrens 	} else {
181*789Sahrens 		for (;;) {
182*789Sahrens 			was_child = AVL_XCHILD(node);
183*789Sahrens 			addr = (uintptr_t)AVL_XPARENT(node);
184*789Sahrens 			if (addr == NULL)
185*789Sahrens 				break;
186*789Sahrens 			addr -= offset;
187*789Sahrens 			if (was_child == 0) /* stop on return from left child */
188*789Sahrens 				break;
189*789Sahrens 			if (mdb_vread(aw->aw_buff, size, addr) == -1) {
190*789Sahrens 				mdb_warn("read of avl_node_t failed: %p", addr);
191*789Sahrens 				return (WALK_ERR);
192*789Sahrens 			}
193*789Sahrens 		}
194*789Sahrens 	}
195*789Sahrens 
196*789Sahrens 	wsp->walk_addr = addr;
197*789Sahrens 	return (WALK_NEXT);
198*789Sahrens }
199*789Sahrens 
200*789Sahrens /*
201*789Sahrens  * Release the memory allocated for the walk
202*789Sahrens  */
203*789Sahrens void
204*789Sahrens avl_walk_fini(mdb_walk_state_t *wsp)
205*789Sahrens {
206*789Sahrens 	struct aw_info *aw;
207*789Sahrens 
208*789Sahrens 	aw = (struct aw_info *)wsp->walk_data;
209*789Sahrens 
210*789Sahrens 	if (aw == NULL)
211*789Sahrens 		return;
212*789Sahrens 
213*789Sahrens 	if (aw->aw_buff != NULL)
214*789Sahrens 		mdb_free(aw->aw_buff, aw->aw_tree.avl_size);
215*789Sahrens 
216*789Sahrens 	mdb_free(aw, sizeof (struct aw_info));
217*789Sahrens }
218