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 */
25*2a12f85aSJeremy Jones /*
26*2a12f85aSJeremy Jones * Copyright (c) 2013 by Delphix. All rights reserved.
27*2a12f85aSJeremy 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);
117b5fca8f8Stomee aw->aw_end = (end == NULL ? NULL : 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 */
126b5fca8f8Stomee if (begin == NULL) {
127fa9e4066Sahrens addr = (uintptr_t)tree->avl_root;
128fa9e4066Sahrens if (addr == NULL) {
129fa9e4066Sahrens wsp->walk_addr = NULL;
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 {
153b5fca8f8Stomee return (avl_walk_init_range(wsp, NULL, NULL, 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 {
160b5fca8f8Stomee return (avl_walk_init_range(wsp, NULL, NULL, 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 {
169b5fca8f8Stomee return (avl_walk_init_range(wsp, NULL, NULL, 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;
192fa9e4066Sahrens if (addr == NULL)
193fa9e4066Sahrens return (WALK_DONE);
194fa9e4066Sahrens
195fa9e4066Sahrens aw = (struct aw_info *)wsp->walk_data;
196b5fca8f8Stomee
197b5fca8f8Stomee if (aw->aw_end != NULL && 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];
238fa9e4066Sahrens if (addr != NULL) {
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);
252fa9e4066Sahrens if (addr == NULL)
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 }
287*2a12f85aSJeremy Jones
288*2a12f85aSJeremy Jones /*
289*2a12f85aSJeremy Jones * This function is named avl_walk_mdb to avoid a naming conflict with the
290*2a12f85aSJeremy Jones * existing avl_walk function.
291*2a12f85aSJeremy Jones */
292*2a12f85aSJeremy Jones int
avl_walk_mdb(uintptr_t addr,mdb_walk_cb_t callback,void * cbdata)293*2a12f85aSJeremy Jones avl_walk_mdb(uintptr_t addr, mdb_walk_cb_t callback, void *cbdata)
294*2a12f85aSJeremy Jones {
295*2a12f85aSJeremy Jones mdb_walk_state_t ws;
296*2a12f85aSJeremy Jones int ret;
297*2a12f85aSJeremy Jones
298*2a12f85aSJeremy Jones ws.walk_addr = addr;
299*2a12f85aSJeremy Jones ws.walk_callback = callback;
300*2a12f85aSJeremy Jones ws.walk_cbdata = cbdata;
301*2a12f85aSJeremy Jones
302*2a12f85aSJeremy Jones avl_walk_init(&ws);
303*2a12f85aSJeremy Jones while ((ret = avl_walk_step(&ws)) == WALK_NEXT)
304*2a12f85aSJeremy Jones continue;
305*2a12f85aSJeremy Jones avl_walk_fini(&ws);
306*2a12f85aSJeremy Jones
307*2a12f85aSJeremy Jones return (ret);
308*2a12f85aSJeremy Jones }
309