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