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