xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/memory.c (revision f875b4ebb1dd9fdbeb043557cab38ab3bf7f6e01)
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 #include <mdb/mdb_modapi.h>
29 #include <sys/types.h>
30 #include <vm/page.h>
31 #include <sys/thread.h>
32 #include <sys/swap.h>
33 #include <sys/memlist.h>
34 
35 
36 /*
37  * Page walker.
38  * By default, this will walk all pages in the system.  If given an
39  * address, it will walk all pages belonging to the vnode at that
40  * address.
41  */
42 
43 /*
44  * page_walk_data
45  *
46  * pw_hashleft is set to -1 when walking a vnode's pages, and holds the
47  * number of hash locations remaining in the page hash table when
48  * walking all pages.
49  *
50  * The astute reader will notice that pw_hashloc is only used when
51  * reading all pages (to hold a pointer to our location in the page
52  * hash table), and that pw_first is only used when reading the pages
53  * belonging to a particular vnode (to hold a pointer to the first
54  * page).  While these could be combined to be a single pointer, they
55  * are left separate for clarity.
56  */
57 typedef struct page_walk_data {
58 	long		pw_hashleft;
59 	void		**pw_hashloc;
60 	uintptr_t	pw_first;
61 } page_walk_data_t;
62 
63 int
64 page_walk_init(mdb_walk_state_t *wsp)
65 {
66 	page_walk_data_t	*pwd;
67 	void	**ptr;
68 	size_t	hashsz;
69 	vnode_t	vn;
70 
71 	if (wsp->walk_addr == NULL) {
72 
73 		/*
74 		 * Walk all pages
75 		 */
76 
77 		if ((mdb_readvar(&ptr, "page_hash") == -1) ||
78 		    (mdb_readvar(&hashsz, "page_hashsz") == -1) ||
79 		    (ptr == NULL) || (hashsz == 0)) {
80 			mdb_warn("page_hash, page_hashsz not found or invalid");
81 			return (WALK_ERR);
82 		}
83 
84 		/*
85 		 * Since we are walking all pages, initialize hashleft
86 		 * to be the remaining number of entries in the page
87 		 * hash.  hashloc is set the start of the page hash
88 		 * table.  Setting the walk address to 0 indicates that
89 		 * we aren't currently following a hash chain, and that
90 		 * we need to scan the page hash table for a page.
91 		 */
92 		pwd = mdb_alloc(sizeof (page_walk_data_t), UM_SLEEP);
93 		pwd->pw_hashleft = hashsz;
94 		pwd->pw_hashloc = ptr;
95 		wsp->walk_addr = 0;
96 	} else {
97 
98 		/*
99 		 * Walk just this vnode
100 		 */
101 
102 		if (mdb_vread(&vn, sizeof (vnode_t), wsp->walk_addr) == -1) {
103 			mdb_warn("unable to read vnode_t at %#lx",
104 			    wsp->walk_addr);
105 			return (WALK_ERR);
106 		}
107 
108 		/*
109 		 * We set hashleft to -1 to indicate that we are
110 		 * walking a vnode, and initialize first to 0 (it is
111 		 * used to terminate the walk, so it must not be set
112 		 * until after we have walked the first page).  The
113 		 * walk address is set to the first page.
114 		 */
115 		pwd = mdb_alloc(sizeof (page_walk_data_t), UM_SLEEP);
116 		pwd->pw_hashleft = -1;
117 		pwd->pw_first = 0;
118 
119 		wsp->walk_addr = (uintptr_t)vn.v_pages;
120 	}
121 
122 	wsp->walk_data = pwd;
123 
124 	return (WALK_NEXT);
125 }
126 
127 int
128 page_walk_step(mdb_walk_state_t *wsp)
129 {
130 	page_walk_data_t	*pwd = wsp->walk_data;
131 	page_t		page;
132 	uintptr_t	pp;
133 
134 	pp = wsp->walk_addr;
135 
136 	if (pwd->pw_hashleft < 0) {
137 
138 		/* We're walking a vnode's pages */
139 
140 		/*
141 		 * If we don't have any pages to walk, we have come
142 		 * back around to the first one (we finished), or we
143 		 * can't read the page we're looking at, we are done.
144 		 */
145 		if (pp == NULL || pp == pwd->pw_first)
146 			return (WALK_DONE);
147 		if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
148 			mdb_warn("unable to read page_t at %#lx", pp);
149 			return (WALK_ERR);
150 		}
151 
152 		/*
153 		 * Set the walk address to the next page, and if the
154 		 * first page hasn't been set yet (i.e. we are on the
155 		 * first page), set it.
156 		 */
157 		wsp->walk_addr = (uintptr_t)page.p_vpnext;
158 		if (pwd->pw_first == NULL)
159 			pwd->pw_first = pp;
160 
161 	} else if (pwd->pw_hashleft > 0) {
162 
163 		/* We're walking all pages */
164 
165 		/*
166 		 * If pp (the walk address) is NULL, we scan through
167 		 * the page hash table until we find a page.
168 		 */
169 		if (pp == NULL) {
170 
171 			/*
172 			 * Iterate through the page hash table until we
173 			 * find a page or reach the end.
174 			 */
175 			do {
176 				if (mdb_vread(&pp, sizeof (uintptr_t),
177 				    (uintptr_t)pwd->pw_hashloc) == -1) {
178 					mdb_warn("unable to read from %#p",
179 					    pwd->pw_hashloc);
180 					return (WALK_ERR);
181 				}
182 				pwd->pw_hashleft--;
183 				pwd->pw_hashloc++;
184 			} while (pwd->pw_hashleft && (pp == NULL));
185 
186 			/*
187 			 * We've reached the end; exit.
188 			 */
189 			if (pp == NULL)
190 				return (WALK_DONE);
191 		}
192 
193 		if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
194 			mdb_warn("unable to read page_t at %#lx", pp);
195 			return (WALK_ERR);
196 		}
197 
198 		/*
199 		 * Set the walk address to the next page.
200 		 */
201 		wsp->walk_addr = (uintptr_t)page.p_hash;
202 
203 	} else {
204 		/* We've finished walking all pages. */
205 		return (WALK_DONE);
206 	}
207 
208 	return (wsp->walk_callback(pp, &page, wsp->walk_cbdata));
209 }
210 
211 void
212 page_walk_fini(mdb_walk_state_t *wsp)
213 {
214 	mdb_free(wsp->walk_data, sizeof (page_walk_data_t));
215 }
216 
217 /* Summary statistics of pages */
218 typedef struct memstat {
219 	struct vnode    *ms_kvp;	/* Cached address of kernel vnode */
220 	struct vnode    *ms_zvp;	/* Cached address of zio vnode    */
221 	uint64_t	ms_kmem;	/* Pages of kernel memory	  */
222 	uint64_t	ms_anon;	/* Pages of anonymous memory	  */
223 	uint64_t	ms_vnode;	/* Pages of named (vnode) memory  */
224 	uint64_t	ms_exec;	/* Pages of exec/library memory	  */
225 	uint64_t	ms_cachelist;	/* Pages on the cachelist (free)  */
226 	uint64_t	ms_total;	/* Pages on page hash		  */
227 } memstat_t;
228 
229 #define	MS_PP_ISKAS(pp, stats)				\
230 	(((pp)->p_vnode == (stats)->ms_kvp) ||		\
231 	    (((stats)->ms_zvp != NULL) && ((pp)->p_vnode == (stats)->ms_zvp)))
232 
233 /*
234  * Summarize pages by type; called from page walker.
235  */
236 
237 /* ARGSUSED */
238 static int
239 memstat_callback(page_t *page, page_t *pp, memstat_t *stats)
240 {
241 	struct vnode vn, *vp;
242 	uintptr_t ptr;
243 
244 	/* read page's vnode pointer */
245 	if ((ptr = (uintptr_t)(pp->p_vnode)) != NULL) {
246 		if (mdb_vread(&vn, sizeof (vnode_t), ptr) == -1) {
247 			mdb_warn("unable to read vnode_t at %#lx",
248 			    ptr);
249 			return (WALK_ERR);
250 		}
251 		vp = &vn;
252 	} else
253 		vp = NULL;
254 
255 	if (PP_ISFREE(pp))
256 		stats->ms_cachelist++;
257 	else if (vp && IS_SWAPFSVP(vp))
258 		stats->ms_anon++;
259 	else if (MS_PP_ISKAS(pp, stats))
260 		stats->ms_kmem++;
261 	else if (vp && (((vp)->v_flag & VVMEXEC)) != 0)
262 		stats->ms_exec++;
263 	else
264 		stats->ms_vnode++;
265 
266 	stats->ms_total++;
267 
268 	return (WALK_NEXT);
269 }
270 
271 /* ARGSUSED */
272 int
273 memstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
274 {
275 	ulong_t pagesize;
276 	pgcnt_t total_pages;
277 	ulong_t physmem;
278 	memstat_t stats;
279 	memstat_t unused_stats;
280 	GElf_Sym sym;
281 
282 	bzero(&stats, sizeof (memstat_t));
283 	bzero(&unused_stats, sizeof (memstat_t));
284 
285 	if (argc != 0 || (flags & DCMD_ADDRSPEC))
286 		return (DCMD_USAGE);
287 
288 	/* Grab base page size */
289 	if (mdb_readvar(&pagesize, "_pagesize") == -1) {
290 		mdb_warn("unable to read _pagesize");
291 		return (DCMD_ERR);
292 	}
293 
294 	/* Total physical memory */
295 	if (mdb_readvar(&total_pages, "total_pages") == -1) {
296 		mdb_warn("unable to read total_pages");
297 		return (DCMD_ERR);
298 	}
299 
300 	/* Artificially limited memory */
301 	if (mdb_readvar(&physmem, "physmem") == -1) {
302 		mdb_warn("unable to read physmem");
303 		return (DCMD_ERR);
304 	}
305 
306 	/* read kernel vnode pointer */
307 	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "kvp",
308 		(GElf_Sym *)&sym) == -1) {
309 		mdb_warn("unable to read kvp");
310 		return (DCMD_ERR);
311 	}
312 
313 	stats.ms_kvp = (struct vnode *)(uintptr_t)sym.st_value;
314 
315 	/*
316 	 * Read the zio vnode pointer.  It may not exist on all kernels, so it
317 	 * it isn't found, it's not a fatal error.
318 	 */
319 	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "zvp",
320 		(GElf_Sym *)&sym) == -1) {
321 		stats.ms_zvp = NULL;
322 	} else {
323 		stats.ms_zvp = (struct vnode *)(uintptr_t)sym.st_value;
324 	}
325 
326 	/* Walk page structures, summarizing usage */
327 	if (mdb_walk("page", (mdb_walk_cb_t)memstat_callback,
328 		&stats) == -1) {
329 		mdb_warn("can't walk pages");
330 		return (DCMD_ERR);
331 	}
332 
333 	/* read unused pages vnode */
334 	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "unused_pages_vp",
335 		(GElf_Sym *)&sym) == -1) {
336 		mdb_warn("unable to read unused_pages_vp");
337 		return (DCMD_ERR);
338 	}
339 
340 	unused_stats.ms_kvp = (struct vnode *)(uintptr_t)sym.st_value;
341 
342 	/* Find unused pages */
343 	if (mdb_walk("page", (mdb_walk_cb_t)memstat_callback,
344 		&unused_stats) == -1) {
345 		mdb_warn("can't walk pages");
346 		return (DCMD_ERR);
347 	}
348 
349 	/*
350 	 * If physmem != total_pages, then the administrator has limited the
351 	 * number of pages available in the system.  In order to account for
352 	 * this, we reduce the amount normally attributed to the page cache.
353 	 */
354 	stats.ms_vnode -= unused_stats.ms_kmem;
355 	stats.ms_total -= unused_stats.ms_kmem;
356 
357 #define	MS_PCT_TOTAL(x)	(((5 * total_pages) + ((x) * 1000ull))) / \
358 		((physmem) * 10)
359 
360 	mdb_printf("Page Summary                Pages                MB"
361 	    "  %%Tot\n");
362 	mdb_printf("------------     ----------------  ----------------"
363 		"  ----\n");
364 	mdb_printf("Kernel           %16llu  %16llu  %3llu%%\n",
365 	    stats.ms_kmem,
366 	    (uint64_t)stats.ms_kmem * pagesize / (1024 * 1024),
367 	    MS_PCT_TOTAL(stats.ms_kmem));
368 	mdb_printf("Anon             %16llu  %16llu  %3llu%%\n",
369 	    stats.ms_anon,
370 	    (uint64_t)stats.ms_anon * pagesize / (1024 * 1024),
371 	    MS_PCT_TOTAL(stats.ms_anon));
372 	mdb_printf("Exec and libs    %16llu  %16llu  %3llu%%\n",
373 	    stats.ms_exec,
374 	    (uint64_t)stats.ms_exec * pagesize / (1024 * 1024),
375 	    MS_PCT_TOTAL(stats.ms_exec));
376 	mdb_printf("Page cache       %16llu  %16llu  %3llu%%\n",
377 	    stats.ms_vnode,
378 	    (uint64_t)stats.ms_vnode * pagesize / (1024 * 1024),
379 	    MS_PCT_TOTAL(stats.ms_vnode));
380 	mdb_printf("Free (cachelist) %16llu  %16llu  %3llu%%\n",
381 	    stats.ms_cachelist,
382 	    (uint64_t)stats.ms_cachelist * pagesize / (1024 * 1024),
383 	    MS_PCT_TOTAL(stats.ms_cachelist));
384 	mdb_printf("Free (freelist)  %16llu  %16llu  %3llu%%\n",
385 	    physmem - stats.ms_total,
386 	    (uint64_t)(physmem - stats.ms_total) * pagesize / (1024 * 1024),
387 	    MS_PCT_TOTAL(physmem - stats.ms_total));
388 	mdb_printf("\nTotal            %16lu  %16lu\n",
389 	    physmem,
390 	    (uint64_t)physmem * pagesize / (1024 * 1024));
391 
392 	if (physmem != total_pages) {
393 		mdb_printf("Physical         %16lu  %16lu\n",
394 		    total_pages,
395 		    (uint64_t)total_pages * pagesize / (1024 * 1024));
396 	}
397 
398 #undef MS_PCT_TOTAL
399 
400 	return (DCMD_OK);
401 }
402 
403 int
404 page(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
405 {
406 	page_t	p;
407 
408 	if (!(flags & DCMD_ADDRSPEC)) {
409 		if (mdb_walk_dcmd("page", "page", argc, argv) == -1) {
410 			mdb_warn("can't walk pages");
411 			return (DCMD_ERR);
412 		}
413 		return (DCMD_OK);
414 	}
415 
416 	if (DCMD_HDRSPEC(flags)) {
417 		mdb_printf("%<u>%?s %?s %16s %8s %3s %3s %2s %2s %2s%</u>\n",
418 		    "PAGE", "VNODE", "OFFSET", "SELOCK",
419 		    "LCT", "COW", "IO", "FS", "ST");
420 	}
421 
422 	if (mdb_vread(&p, sizeof (page_t), addr) == -1) {
423 		mdb_warn("can't read page_t at %#lx", addr);
424 		return (DCMD_ERR);
425 	}
426 
427 	mdb_printf("%0?lx %?p %16llx %8x %3d %3d %2x %2x %2x\n",
428 	    addr, p.p_vnode, p.p_offset, p.p_selock, p.p_lckcnt, p.p_cowcnt,
429 	    p.p_iolock_state, p.p_fsdata, p.p_state);
430 
431 	return (DCMD_OK);
432 }
433 
434 int
435 swap_walk_init(mdb_walk_state_t *wsp)
436 {
437 	void	*ptr;
438 
439 	if ((mdb_readvar(&ptr, "swapinfo") == -1) || ptr == NULL) {
440 		mdb_warn("swapinfo not found or invalid");
441 		return (WALK_ERR);
442 	}
443 
444 	wsp->walk_addr = (uintptr_t)ptr;
445 
446 	return (WALK_NEXT);
447 }
448 
449 int
450 swap_walk_step(mdb_walk_state_t *wsp)
451 {
452 	uintptr_t	sip;
453 	struct swapinfo	si;
454 
455 	sip = wsp->walk_addr;
456 
457 	if (sip == NULL)
458 		return (WALK_DONE);
459 
460 	if (mdb_vread(&si, sizeof (struct swapinfo), sip) == -1) {
461 		mdb_warn("unable to read swapinfo at %#lx", sip);
462 		return (WALK_ERR);
463 	}
464 
465 	wsp->walk_addr = (uintptr_t)si.si_next;
466 
467 	return (wsp->walk_callback(sip, &si, wsp->walk_cbdata));
468 }
469 
470 int
471 swapinfof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
472 {
473 	struct swapinfo	si;
474 	char		*name;
475 
476 	if (!(flags & DCMD_ADDRSPEC)) {
477 		if (mdb_walk_dcmd("swapinfo", "swapinfo", argc, argv) == -1) {
478 			mdb_warn("can't walk swapinfo");
479 			return (DCMD_ERR);
480 		}
481 		return (DCMD_OK);
482 	}
483 
484 	if (DCMD_HDRSPEC(flags)) {
485 		mdb_printf("%<u>%?s %?s %9s %9s %s%</u>\n",
486 		    "ADDR", "VNODE", "PAGES", "FREE", "NAME");
487 	}
488 
489 	if (mdb_vread(&si, sizeof (struct swapinfo), addr) == -1) {
490 		mdb_warn("can't read swapinfo at %#lx", addr);
491 		return (DCMD_ERR);
492 	}
493 
494 	name = mdb_alloc(si.si_pnamelen, UM_SLEEP | UM_GC);
495 	if (mdb_vread(name, si.si_pnamelen, (uintptr_t)si.si_pname) == -1)
496 		name = "*error*";
497 
498 	mdb_printf("%0?lx %?p %9d %9d %s\n",
499 	    addr, si.si_vp, si.si_npgs, si.si_nfpgs, name);
500 
501 	return (DCMD_OK);
502 }
503 
504 int
505 memlist_walk_step(mdb_walk_state_t *wsp)
506 {
507 	uintptr_t	mlp;
508 	struct memlist	ml;
509 
510 	mlp = wsp->walk_addr;
511 
512 	if (mlp == NULL)
513 		return (WALK_DONE);
514 
515 	if (mdb_vread(&ml, sizeof (struct memlist), mlp) == -1) {
516 		mdb_warn("unable to read memlist at %#lx", mlp);
517 		return (WALK_ERR);
518 	}
519 
520 	wsp->walk_addr = (uintptr_t)ml.next;
521 
522 	return (wsp->walk_callback(mlp, &ml, wsp->walk_cbdata));
523 }
524 
525 int
526 memlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
527 {
528 	struct memlist	ml;
529 
530 	if (!(flags & DCMD_ADDRSPEC)) {
531 		uintptr_t ptr;
532 		uint_t list = 0;
533 		int i;
534 		static const char *lists[] = {
535 			"phys_install",
536 			"phys_avail",
537 			"virt_avail"
538 		};
539 
540 		if (mdb_getopts(argc, argv,
541 		    'i', MDB_OPT_SETBITS, (1 << 0), &list,
542 		    'a', MDB_OPT_SETBITS, (1 << 1), &list,
543 		    'v', MDB_OPT_SETBITS, (1 << 2), &list, NULL) != argc)
544 			return (DCMD_USAGE);
545 
546 		if (!list)
547 			list = 1;
548 
549 		for (i = 0; list; i++, list >>= 1) {
550 			if (!(list & 1))
551 				continue;
552 			if ((mdb_readvar(&ptr, lists[i]) == -1) ||
553 			    (ptr == NULL)) {
554 				mdb_warn("%s not found or invalid", lists[i]);
555 				return (DCMD_ERR);
556 			}
557 
558 			mdb_printf("%s:\n", lists[i]);
559 			if (mdb_pwalk_dcmd("memlist", "memlist", 0, NULL,
560 			    ptr) == -1) {
561 				mdb_warn("can't walk memlist");
562 				return (DCMD_ERR);
563 			}
564 		}
565 		return (DCMD_OK);
566 	}
567 
568 	if (DCMD_HDRSPEC(flags))
569 		mdb_printf("%<u>%?s %16s %16s%</u>\n", "ADDR", "BASE", "SIZE");
570 
571 	if (mdb_vread(&ml, sizeof (struct memlist), addr) == -1) {
572 		mdb_warn("can't read memlist at %#lx", addr);
573 		return (DCMD_ERR);
574 	}
575 
576 	mdb_printf("%0?lx %16llx %16llx\n", addr, ml.address, ml.size);
577 
578 	return (DCMD_OK);
579 }
580