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 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 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 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 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 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 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 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