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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/mdb_modapi.h>
30 
31 #include <lut.h>
32 #include <itree.h>
33 
34 #define	LUT_SIZE_INIT	300
35 #define	LUT_SIZE_INCR	100
36 
37 struct lut {
38 	struct lut *lut_left;
39 	struct lut *lut_right;
40 	uintptr_t lut_lhs;		/* search key */
41 	uintptr_t lut_rhs;		/* the datum */
42 };
43 
44 struct lut_cp {
45 	uintptr_t lutcp_addr;
46 	struct lut lutcp_lut;
47 };
48 
49 #define	LCPSZ	sizeof (struct lut_cp)
50 
51 struct lut_dump_desc {
52 	struct lut_cp *ld_array;
53 	int ld_arraysz;
54 	int ld_nents;
55 };
56 
57 static void
58 lut_dump_array_alloc(struct lut_dump_desc *lddp)
59 {
60 	struct lut_cp *new;
61 
62 	if (lddp->ld_array == NULL) {
63 		lddp->ld_arraysz = LUT_SIZE_INIT;
64 		lddp->ld_array = mdb_zalloc(LUT_SIZE_INIT * LCPSZ, UM_SLEEP);
65 		return;
66 	}
67 
68 	new = mdb_zalloc((lddp->ld_arraysz + LUT_SIZE_INCR) * LCPSZ, UM_SLEEP);
69 	bcopy(lddp->ld_array, new, lddp->ld_arraysz * LCPSZ);
70 	mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ);
71 	lddp->ld_array = new;
72 	lddp->ld_arraysz += LUT_SIZE_INCR;
73 }
74 
75 static void
76 lut_dump_array_free(struct lut_dump_desc *lddp)
77 {
78 	if (lddp->ld_array != NULL) {
79 		mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ);
80 		lddp->ld_array = NULL;
81 	}
82 }
83 
84 static void
85 lut_collect_addent(uintptr_t addr, struct lut *ent, struct lut_dump_desc *lddp)
86 {
87 	struct lut_cp *lcp;
88 
89 	if (lddp->ld_nents == lddp->ld_arraysz)
90 		lut_dump_array_alloc(lddp);
91 
92 	lcp = &lddp->ld_array[lddp->ld_nents++];
93 
94 	lcp->lutcp_addr = addr;
95 	bcopy(ent, &lcp->lutcp_lut, sizeof (struct lut));
96 }
97 
98 static int
99 eft_lut_walk(uintptr_t root, struct lut_dump_desc *lddp)
100 {
101 	struct lut lutent;
102 
103 	if (root) {
104 		if (mdb_vread(&lutent, sizeof (struct lut), root) !=
105 		    sizeof (struct lut)) {
106 			mdb_warn("failed to read struct lut at %p", root);
107 			return (WALK_ERR);
108 		}
109 
110 		if (eft_lut_walk((uintptr_t)lutent.lut_left, lddp) != WALK_NEXT)
111 			return (WALK_ERR);
112 
113 		lut_collect_addent(root, &lutent, lddp);
114 
115 		if (eft_lut_walk((uintptr_t)lutent.lut_right, lddp) !=
116 		    WALK_NEXT)
117 			return (WALK_ERR);
118 	}
119 	return (WALK_NEXT);
120 }
121 
122 static int
123 lut_collect(uintptr_t addr, struct lut_dump_desc *lddp)
124 {
125 	lut_dump_array_alloc(lddp);
126 
127 	if (eft_lut_walk(addr, lddp) != WALK_NEXT) {
128 		lut_dump_array_free(lddp);
129 		return (WALK_ERR);
130 	} else {
131 		return (WALK_NEXT);	/* caller must free dump array */
132 	}
133 }
134 
135 static int
136 lut_walk_init(mdb_walk_state_t *wsp)
137 {
138 	if (wsp->walk_addr == NULL) {
139 		mdb_warn("lut walker requires a lut table address\n");
140 		return (WALK_ERR);
141 	}
142 
143 	wsp->walk_data = mdb_zalloc(sizeof (struct lut_dump_desc), UM_SLEEP);
144 	wsp->walk_arg = 0;
145 
146 	if (lut_collect(wsp->walk_addr, wsp->walk_data) == WALK_NEXT) {
147 		return (WALK_NEXT);
148 	} else {
149 		mdb_warn("failed to suck in full lut\n");
150 		mdb_free(wsp->walk_data, sizeof (struct lut_dump_desc));
151 		return (WALK_ERR);
152 	}
153 }
154 
155 static int
156 lut_walk_step(mdb_walk_state_t *wsp)
157 {
158 	struct lut_dump_desc *lddp = wsp->walk_data;
159 	int *ip = (int *)&wsp->walk_arg;
160 	struct lut_cp *lcp = &lddp->ld_array[*ip];
161 
162 	if (*ip == lddp->ld_nents)
163 		return (WALK_DONE);
164 
165 	++*ip;
166 
167 	return (wsp->walk_callback(lcp->lutcp_addr, &lcp->lutcp_lut,
168 	    wsp->walk_cbdata));
169 }
170 
171 static void
172 lut_walk_fini(mdb_walk_state_t *wsp)
173 {
174 	struct lut_dump_desc *lddp = wsp->walk_data;
175 
176 	lut_dump_array_free(lddp);
177 	mdb_free(lddp, sizeof (struct lut_dump_desc));
178 }
179 
180 static const mdb_walker_t walkers[] = {
181 	{ "lut", "walk a lookup table", lut_walk_init, lut_walk_step,
182 	    lut_walk_fini, NULL },
183 	{ NULL, NULL, NULL, NULL, NULL, NULL }
184 };
185 
186 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, NULL, walkers };
187 
188 const mdb_modinfo_t *
189 _mdb_init(void)
190 {
191 	return (&modinfo);
192 }
193