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