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 2006 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 /*
29 * Generic memory walker, used by both the genunix and libumem dmods.
30 */
31
32 #include <mdb/mdb_modapi.h>
33 #include <sys/sysmacros.h>
34
35 #include "kgrep.h"
36
37 #define KGREP_FULL_MASK (~(uintmax_t)0)
38
39 typedef struct kgrep_data {
40 uintmax_t kg_pattern;
41 uintmax_t kg_mask; /* fancy only */
42 uintmax_t kg_dist; /* fancy only */
43 uintptr_t kg_minaddr; /* fancy only */
44 uintptr_t kg_maxaddr; /* fancy only */
45 void *kg_page;
46 size_t kg_pagesize;
47 char kg_cbtype;
48 char kg_seen;
49 } kgrep_data_t;
50
51 #define KG_BASE 0
52 #define KG_VERBOSE 1
53 #define KG_PIPE 2
54
55 static void
kgrep_cb(uintptr_t addr,uintmax_t * val,int type)56 kgrep_cb(uintptr_t addr, uintmax_t *val, int type)
57 {
58 switch (type) {
59 case KG_BASE:
60 default:
61 mdb_printf("%p\n", addr);
62 break;
63 case KG_VERBOSE:
64 mdb_printf("%p:\t%llx\n", addr, *val);
65 break;
66 case KG_PIPE:
67 mdb_printf("%#lr\n", addr);
68 break;
69 }
70 }
71
72 static int
kgrep_range_basic(uintptr_t base,uintptr_t lim,void * kg_arg)73 kgrep_range_basic(uintptr_t base, uintptr_t lim, void *kg_arg)
74 {
75 kgrep_data_t *kg = kg_arg;
76 size_t pagesize = kg->kg_pagesize;
77 uintptr_t pattern = kg->kg_pattern;
78 uintptr_t *page = kg->kg_page;
79 uintptr_t *page_end = &page[pagesize / sizeof (uintptr_t)];
80 uintptr_t *pos;
81
82 uintptr_t addr, offset;
83 int seen = 0;
84
85 /*
86 * page-align everything, to simplify the loop
87 */
88 base = P2ALIGN(base, pagesize);
89 lim = P2ROUNDUP(lim, pagesize);
90
91 for (addr = base; addr < lim; addr += pagesize) {
92 if (mdb_vread(page, pagesize, addr) == -1)
93 continue;
94 seen = 1;
95
96 for (pos = page; pos < page_end; pos++) {
97 if (*pos != pattern)
98 continue;
99
100 offset = (caddr_t)pos - (caddr_t)page;
101 kgrep_cb(addr + offset, NULL, kg->kg_cbtype);
102 }
103 }
104 if (seen)
105 kg->kg_seen = 1;
106
107 return (WALK_NEXT);
108 }
109
110 /*
111 * Full-service template -- instantiated for each supported size. We support
112 * the following options:
113 *
114 * addr in [minaddr, maxaddr), and
115 * value in [pattern, pattern + dist) OR
116 * mask matching: (value & mask) == (pattern & mask)
117 */
118 #define KGREP_FANCY_TEMPLATE(kgrep_range_fancybits, uintbits_t) \
119 static int \
120 kgrep_range_fancybits(uintptr_t base, uintptr_t lim, void *kg_arg) \
121 { \
122 kgrep_data_t *kg = kg_arg; \
123 \
124 uintbits_t pattern = kg->kg_pattern; \
125 uintbits_t dist = kg->kg_dist; \
126 uintbits_t mask = kg->kg_mask; \
127 uintptr_t minaddr = kg->kg_minaddr; \
128 uintptr_t maxaddr = kg->kg_maxaddr; \
129 size_t pagesize = kg->kg_pagesize; \
130 uintbits_t *page = (uintbits_t *)kg->kg_page; \
131 uintbits_t *page_end; \
132 uintbits_t *pos; \
133 uintbits_t cur; \
134 uintmax_t out; \
135 \
136 uintptr_t addr, size, offset; \
137 int seen = 0; \
138 \
139 base = P2ROUNDUP(MAX(base, minaddr), sizeof (uintbits_t)); \
140 \
141 if (maxaddr != 0 && lim > maxaddr) \
142 lim = maxaddr; \
143 \
144 for (addr = base; addr < lim; addr += size) { \
145 /* P2END(...) computes the next page boundry */ \
146 size = MIN(lim, P2END(addr, pagesize)) - addr; \
147 \
148 if (mdb_vread(page, size, addr) == -1) \
149 continue; \
150 \
151 seen = 1; \
152 \
153 page_end = &page[size / sizeof (uintbits_t)]; \
154 for (pos = page; pos < page_end; pos++) { \
155 cur = *pos; \
156 \
157 if (((cur ^ pattern) & mask) != 0 && \
158 (cur - pattern) >= dist) \
159 continue; \
160 \
161 out = cur; \
162 offset = (caddr_t)pos - (caddr_t)page; \
163 kgrep_cb(addr + offset, &out, kg->kg_cbtype); \
164 } \
165 } \
166 if (seen) \
167 kg->kg_seen = 1; \
168 \
169 return (WALK_NEXT); \
170 }
171
KGREP_FANCY_TEMPLATE(kgrep_range_fancy8,uint8_t)172 KGREP_FANCY_TEMPLATE(kgrep_range_fancy8, uint8_t)
173 KGREP_FANCY_TEMPLATE(kgrep_range_fancy16, uint16_t)
174 KGREP_FANCY_TEMPLATE(kgrep_range_fancy32, uint32_t)
175 KGREP_FANCY_TEMPLATE(kgrep_range_fancy64, uint64_t)
176
177 #undef KGREP_FANCY_TEMPLATE
178
179 void
180 kgrep_help(void)
181 {
182 mdb_printf(
183 "\n"
184 "Search the entire virtual address space for a particular pattern,\n"
185 "%<u>addr%</u>. By default, a pointer-sized search for an exact match is\n"
186 "done.\n\n");
187 mdb_dec_indent(2);
188 mdb_printf("%<b>OPTIONS%</b>\n");
189 mdb_inc_indent(2);
190 mdb_printf(
191 " -v Report the value matched at each address\n"
192 " -a minaddr\n"
193 " Restrict the search to addresses >= minaddr\n"
194 " -A maxaddr\n"
195 " Restrict the search to addresses < maxaddr\n"
196 " -d dist\n"
197 " Search for values in [addr, addr + dist)\n"
198 " -m mask\n"
199 " Search for values where (value & mask) == addr\n"
200 " -M invmask\n"
201 " Search for values where (value & ~invmask) == addr\n"
202 " -s size\n"
203 " Instead of pointer-sized values, search for size-byte values.\n"
204 " size must be 1, 2, 4, or 8.\n");
205 }
206
207 /*ARGSUSED*/
208 int
kgrep(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)209 kgrep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
210 {
211 uintmax_t pattern = mdb_get_dot();
212 uintmax_t mask = KGREP_FULL_MASK;
213 uintmax_t invmask = 0;
214 uintmax_t dist = 0;
215 uintptr_t size = sizeof (uintptr_t);
216 uintptr_t minaddr = 0;
217 uintptr_t maxaddr = 0;
218 size_t pagesize = kgrep_subr_pagesize();
219 int verbose = 0;
220 int ret;
221 int args = 0;
222
223 kgrep_cb_func *func;
224 kgrep_data_t kg;
225
226 uintmax_t size_mask;
227
228 if (mdb_getopts(argc, argv,
229 'a', MDB_OPT_UINTPTR, &minaddr,
230 'A', MDB_OPT_UINTPTR, &maxaddr,
231 'd', MDB_OPT_UINT64, &dist,
232 'm', MDB_OPT_UINT64, &mask,
233 'M', MDB_OPT_UINT64, &invmask,
234 's', MDB_OPT_UINTPTR, &size,
235 'v', MDB_OPT_SETBITS, B_TRUE, &verbose, NULL) != argc)
236 return (DCMD_USAGE);
237
238 if (invmask != 0)
239 args++;
240 if (mask != KGREP_FULL_MASK)
241 args++;
242 if (dist != 0)
243 args++;
244
245 if (args > 1) {
246 mdb_warn("only one of -d, -m and -M may be specified\n");
247 return (DCMD_USAGE);
248 }
249
250 if (!(flags & DCMD_ADDRSPEC))
251 return (DCMD_USAGE);
252
253 if (invmask != 0)
254 mask = ~invmask;
255
256 if (pattern & ~mask)
257 mdb_warn("warning: pattern does not match mask\n");
258
259 if (size > sizeof (uintmax_t)) {
260 mdb_warn("sizes greater than %d not supported\n",
261 sizeof (uintmax_t));
262 return (DCMD_ERR);
263 }
264
265 if (size == 0 || (size & (size - 1)) != 0) {
266 mdb_warn("size must be a power of 2\n");
267 return (DCMD_ERR);
268 }
269
270 if (size == sizeof (uintmax_t))
271 size_mask = KGREP_FULL_MASK;
272 else
273 size_mask = (1ULL << (size * NBBY)) - 1ULL;
274
275 if (pattern & ~size_mask)
276 mdb_warn("warning: pattern %llx overflows requested size "
277 "%d (max: %llx)\n",
278 pattern, size, size_mask);
279
280 if (dist > 0 &&
281 ((dist & ~size_mask) || size_mask + 1 - dist < pattern)) {
282 mdb_warn("pattern %llx + distance %llx overflows size\n"
283 "%d (max: %llx)\n", pattern, dist, size, size_mask);
284 return (DCMD_ERR);
285 }
286
287 /*
288 * All arguments have now been validated.
289 */
290
291 (void) memset(&kg, '\0', sizeof (kg));
292 kg.kg_page = mdb_alloc(pagesize, UM_SLEEP | UM_GC);
293 kg.kg_pagesize = pagesize;
294 kg.kg_pattern = pattern;
295 kg.kg_mask = mask;
296 kg.kg_dist = dist;
297 kg.kg_minaddr = minaddr;
298 kg.kg_maxaddr = maxaddr;
299
300 if (flags & DCMD_PIPE_OUT) {
301 verbose = 0;
302 kg.kg_cbtype = KG_PIPE;
303 } else if (verbose) {
304 kg.kg_cbtype = KG_VERBOSE;
305 } else {
306 kg.kg_cbtype = KG_BASE;
307 }
308
309 /*
310 * kgrep_range_basic handles the common case (no arguments)
311 * with dispatch.
312 */
313 if (size == sizeof (uintptr_t) && !verbose && mask == KGREP_FULL_MASK &&
314 dist == 0 && minaddr == 0 && maxaddr == 0)
315 func = kgrep_range_basic;
316 else {
317 switch (size) {
318 case 1:
319 func = kgrep_range_fancy8;
320 break;
321 case 2:
322 func = kgrep_range_fancy16;
323 break;
324 case 4:
325 func = kgrep_range_fancy32;
326 break;
327 case 8:
328 func = kgrep_range_fancy64;
329 break;
330 default:
331 mdb_warn("can't happen: non-recognized kgrep size\n");
332 return (DCMD_ERR);
333 }
334 }
335
336 /*
337 * Invoke the target, which should invoke func(start, end, &kg) for
338 * every range [start, end) of vaddrs which might have backing.
339 * Both start and end must be multiples of kgrep_subr_pagesize().
340 */
341 ret = kgrep_subr(func, &kg);
342
343 if (ret == DCMD_OK && !kg.kg_seen)
344 mdb_warn("warning: nothing searched\n");
345
346 return (ret);
347 }
348