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