xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/zone.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <mdb/mdb_param.h>
26 #include <mdb/mdb_modapi.h>
27 #include <mdb/mdb_ks.h>
28 
29 #include "zone.h"
30 
31 #include <stddef.h>
32 #include <sys/zone.h>
33 
34 #define	ZONE_NAMELEN	20
35 #ifdef _LP64
36 #define	ZONE_PATHLEN	32
37 #else
38 #define	ZONE_PATHLEN	40
39 #endif
40 
41 /*
42  * Names corresponding to zone_status_t values in sys/zone.h
43  */
44 char *zone_status_names[] = {
45 	"uninitialized",	/* ZONE_IS_UNINITIALIZED */
46 	"initialized",		/* ZONE_IS_INITIALIZED */
47 	"ready",		/* ZONE_IS_READY */
48 	"booting",		/* ZONE_IS_BOOTING */
49 	"running",		/* ZONE_IS_RUNNING */
50 	"shutting_down",	/* ZONE_IS_SHUTTING_DOWN */
51 	"empty",		/* ZONE_IS_EMPTY */
52 	"down",			/* ZONE_IS_DOWN */
53 	"dying",		/* ZONE_IS_DYING */
54 	"dead"			/* ZONE_IS_DEAD */
55 };
56 
57 int
58 zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
59 {
60 	zone_t zn;
61 	char name[ZONE_NAMELEN];
62 	char path[ZONE_PATHLEN];
63 	int len;
64 	uint_t vopt_given;
65 	uint_t ropt_given;
66 
67 	if (argc > 2)
68 		return (DCMD_USAGE);
69 
70 	if (!(flags & DCMD_ADDRSPEC)) {
71 		if (mdb_walk_dcmd("zone", "zone", argc, argv) == -1) {
72 			mdb_warn("can't walk zones");
73 			return (DCMD_ERR);
74 		}
75 		return (DCMD_OK);
76 	}
77 
78 	/*
79 	 * Get the optional -r (reference counts) and -v (verbose output)
80 	 * arguments.
81 	 */
82 	vopt_given = FALSE;
83 	ropt_given = FALSE;
84 	if (argc > 0 && mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE,
85 	    &vopt_given, 'r', MDB_OPT_SETBITS, TRUE, &ropt_given, NULL) != argc)
86 		return (DCMD_USAGE);
87 
88 	/*
89 	 * -v can only be specified with -r.
90 	 */
91 	if (vopt_given == TRUE && ropt_given == FALSE)
92 		return (DCMD_USAGE);
93 
94 	/*
95 	 * Print a table header, if necessary.
96 	 */
97 	if (DCMD_HDRSPEC(flags)) {
98 		if (ropt_given == FALSE)
99 			mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n",
100 			    "ADDR", "ID", "STATUS", "NAME", "PATH");
101 		else
102 			mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n",
103 			    "ADDR", "ID", "REFS", "CREFS", "NAME");
104 	}
105 
106 	/*
107 	 * Read the zone_t structure at the given address and read its name.
108 	 */
109 	if (mdb_vread(&zn, sizeof (zone_t), addr) == -1) {
110 		mdb_warn("can't read zone_t structure at %p", addr);
111 		return (DCMD_ERR);
112 	}
113 	len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zn.zone_name);
114 	if (len > 0) {
115 		if (len == ZONE_NAMELEN)
116 			(void) strcpy(&name[len - 4], "...");
117 	} else {
118 		(void) strcpy(name, "??");
119 	}
120 
121 	if (ropt_given == FALSE) {
122 		char *statusp;
123 
124 		/*
125 		 * Default display
126 		 * Fetch the zone's path and print the results.
127 		 */
128 		len = mdb_readstr(path, ZONE_PATHLEN,
129 		    (uintptr_t)zn.zone_rootpath);
130 		if (len > 0) {
131 			if (len == ZONE_PATHLEN)
132 				(void) strcpy(&path[len - 4], "...");
133 		} else {
134 			(void) strcpy(path, "??");
135 		}
136 		if (zn.zone_status >= ZONE_IS_UNINITIALIZED && zn.zone_status <=
137 		    ZONE_IS_DEAD)
138 			statusp = zone_status_names[zn.zone_status];
139 		else
140 			statusp = "???";
141 		mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id,
142 		    statusp, name, path);
143 	} else {
144 		/*
145 		 * Display the zone's reference counts.
146 		 * Display the zone's subsystem-specific reference counts if
147 		 * the user specified the '-v' option.
148 		 */
149 		mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id,
150 		    zn.zone_ref, zn.zone_cred_ref, name);
151 		if (vopt_given == TRUE) {
152 			GElf_Sym subsys_names_sym;
153 			uintptr_t **zone_ref_subsys_names;
154 			uint_t num_subsys;
155 			uint_t n;
156 
157 			/*
158 			 * Read zone_ref_subsys_names from the kernel image.
159 			 */
160 			if (mdb_lookup_by_name("zone_ref_subsys_names",
161 			    &subsys_names_sym) != 0) {
162 				mdb_warn("can't find zone_ref_subsys_names");
163 				return (DCMD_ERR);
164 			}
165 			if (subsys_names_sym.st_size != ZONE_REF_NUM_SUBSYS *
166 			    sizeof (char *)) {
167 				mdb_warn("number of subsystems in target "
168 				    "differs from what mdb expects (mismatched"
169 				    " kernel versions?)");
170 				if (subsys_names_sym.st_size <
171 				    ZONE_REF_NUM_SUBSYS * sizeof (char *))
172 					num_subsys = subsys_names_sym.st_size /
173 					    sizeof (char *);
174 				else
175 					num_subsys = ZONE_REF_NUM_SUBSYS;
176 			} else {
177 				num_subsys = ZONE_REF_NUM_SUBSYS;
178 			}
179 			if ((zone_ref_subsys_names = mdb_alloc(
180 			    subsys_names_sym.st_size, UM_GC)) == NULL) {
181 				mdb_warn("out of memory");
182 				return (DCMD_ERR);
183 			}
184 			if (mdb_readvar(zone_ref_subsys_names,
185 			    "zone_ref_subsys_names") == -1) {
186 				mdb_warn("can't find zone_ref_subsys_names");
187 				return (DCMD_ERR);
188 			}
189 
190 			/*
191 			 * Display each subsystem's reference count if it's
192 			 * nonzero.
193 			 */
194 			mdb_inc_indent(7);
195 			for (n = 0; n < num_subsys; ++n) {
196 				char subsys_name[16];
197 
198 				/*
199 				 * Skip subsystems lacking outstanding
200 				 * references.
201 				 */
202 				if (zn.zone_subsys_ref[n] == 0)
203 					continue;
204 
205 				/*
206 				 * Each subsystem's name must be read from
207 				 * the target's image.
208 				 */
209 				if (mdb_readstr(subsys_name,
210 				    sizeof (subsys_name),
211 				    (uintptr_t)zone_ref_subsys_names[n]) ==
212 				    -1) {
213 					mdb_warn("unable to read subsystem name"
214 					    " from zone_ref_subsys_names[%u]",
215 					    n);
216 					return (DCMD_ERR);
217 				}
218 				mdb_printf("%15s: %10u\n", subsys_name,
219 				    zn.zone_subsys_ref[n]);
220 			}
221 			mdb_dec_indent(7);
222 		}
223 	}
224 	return (DCMD_OK);
225 }
226 
227 int
228 zone_walk_init(mdb_walk_state_t *wsp)
229 {
230 	GElf_Sym sym;
231 
232 	if (wsp->walk_addr == NULL) {
233 		if (mdb_lookup_by_name("zone_active", &sym) == -1) {
234 			mdb_warn("failed to find 'zone_active'");
235 			return (WALK_ERR);
236 		}
237 		wsp->walk_addr = (uintptr_t)sym.st_value;
238 	}
239 	if (mdb_layered_walk("list", wsp) == -1) {
240 		mdb_warn("couldn't walk 'list'");
241 		return (WALK_ERR);
242 	}
243 	return (WALK_NEXT);
244 }
245 
246 int
247 zone_walk_step(mdb_walk_state_t *wsp)
248 {
249 	return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
250 	    wsp->walk_cbdata));
251 }
252 
253 int
254 zsd_walk_init(mdb_walk_state_t *wsp)
255 {
256 	if (wsp->walk_addr == NULL) {
257 		mdb_warn("global walk not supported\n");
258 		return (WALK_ERR);
259 	}
260 	wsp->walk_addr += offsetof(struct zone, zone_zsd);
261 	if (mdb_layered_walk("list", wsp) == -1) {
262 		mdb_warn("couldn't walk 'list'");
263 		return (WALK_ERR);
264 	}
265 	return (WALK_NEXT);
266 }
267 
268 int
269 zsd_walk_step(mdb_walk_state_t *wsp)
270 {
271 	return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
272 	    wsp->walk_cbdata));
273 }
274 
275 /*
276  * Helper structure used when walking ZSD entries via zsd().
277  */
278 struct zsd_cb_data {
279 	uint_t		keygiven;	/* Was a key specified (are we */
280 					/* searching for a specific ZSD */
281 					/* entry)? */
282 	zone_key_t	key;		/* Key of ZSD for which we're looking */
283 	uint_t		found;		/* Was the specific ZSD entry found? */
284 	uint_t		voptgiven;	/* Display verbose information? */
285 };
286 
287 /*
288  * Helper function for zsd() that displays information from a single ZSD struct.
289  * 'datap' must point to a valid zsd_cb_data struct.
290  */
291 /* ARGSUSED */
292 static int
293 zsd_print(uintptr_t addrp, const void * datap, void * privatep)
294 {
295 	struct zsd_entry entry;
296 	struct zsd_cb_data *cbdp;
297 
298 	if (mdb_vread(&entry, sizeof (entry), addrp) == -1) {
299 		mdb_warn("couldn't read zsd_entry at %p", addrp);
300 		return (WALK_ERR);
301 	}
302 	cbdp = (struct zsd_cb_data *)privatep;
303 
304 	/*
305 	 * Are we looking for a single entry specified by a key?  Then make sure
306 	 * that the current ZSD's key is what we're looking for.
307 	 */
308 	if (cbdp->keygiven == TRUE && cbdp->key != entry.zsd_key)
309 		return (WALK_NEXT);
310 
311 	mdb_printf("%?x %0?p %8x\n", entry.zsd_key, entry.zsd_data,
312 	    entry.zsd_flags);
313 	if (cbdp->voptgiven == TRUE)
314 		mdb_printf("    Create CB:   %a\n    Shutdown CB: %a\n"
315 		    "    Destroy CB:  %a\n", entry.zsd_create,
316 		    entry.zsd_shutdown, entry.zsd_destroy);
317 	if (cbdp->keygiven == TRUE) {
318 		cbdp->found = TRUE;
319 		return (WALK_DONE);
320 	}
321 	return (WALK_NEXT);
322 }
323 
324 int
325 zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
326 {
327 	zone_t zone;
328 	const mdb_arg_t *argp;
329 	int argcindex;
330 	struct zsd_cb_data cbd;
331 	char name[ZONE_NAMELEN];
332 	int len;
333 
334 	/*
335 	 * Walk all zones if necessary.
336 	 */
337 	if (argc > 2)
338 		return (DCMD_USAGE);
339 	if ((flags & DCMD_ADDRSPEC) == 0) {
340 		if (mdb_walk_dcmd("zone", "zsd", argc, argv) == -1) {
341 			mdb_warn("failed to walk zone\n");
342 			return (DCMD_ERR);
343 		}
344 		return (DCMD_OK);
345 	}
346 
347 	/*
348 	 * Make sure a zone_t can be read from the specified address.
349 	 */
350 	if (mdb_vread(&zone, sizeof (zone), addr) == -1) {
351 		mdb_warn("couldn't read zone_t at %p", (void *)addr);
352 		return (DCMD_ERR);
353 	}
354 
355 	/*
356 	 * Get the optional arguments (key or -v or both).  Note that
357 	 * mdb_getopts() will not parse a key argument because it is not
358 	 * preceded by an option letter.  We'll get around this by requiring
359 	 * that all options precede the optional key argument.
360 	 */
361 	cbd.keygiven = FALSE;
362 	cbd.voptgiven = FALSE;
363 	if (argc > 0 && (argcindex = mdb_getopts(argc, argv, 'v',
364 	    MDB_OPT_SETBITS, TRUE, &cbd.voptgiven, NULL)) != argc) {
365 		/*
366 		 * No options may appear after the key.
367 		 */
368 		if (argcindex != argc - 1)
369 			return (DCMD_USAGE);
370 
371 		/*
372 		 * The missed argument should be a key.
373 		 */
374 		argp = &argv[argcindex];
375 		if (argp->a_type == MDB_TYPE_IMMEDIATE)
376 			cbd.key = argp->a_un.a_val;
377 		else
378 			cbd.key = mdb_strtoull(argp->a_un.a_str);
379 		cbd.keygiven = TRUE;
380 		cbd.found = FALSE;
381 	}
382 
383 	/*
384 	 * Prepare to output the specified zone's ZSD information.
385 	 */
386 	if (DCMD_HDRSPEC(flags))
387 		mdb_printf("%<u>%-20s %?s %?s %8s%</u>\n", "ZONE", "KEY",
388 		    "VALUE", "FLAGS");
389 	len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zone.zone_name);
390 	if (len > 0) {
391 		if (len == ZONE_NAMELEN)
392 			(void) strcpy(&name[len - 4], "...");
393 	} else {
394 		(void) strcpy(name, "??");
395 	}
396 	mdb_printf("%-20s ", name);
397 
398 	/*
399 	 * Display the requested ZSD entries.
400 	 */
401 	mdb_inc_indent(21);
402 	if (mdb_pwalk("zsd", zsd_print, &cbd, addr) != 0) {
403 		mdb_warn("failed to walk zsd\n");
404 		mdb_dec_indent(21);
405 		return (DCMD_ERR);
406 	}
407 	if (cbd.keygiven == TRUE && cbd.found == FALSE) {
408 		mdb_printf("no corresponding ZSD entry found\n");
409 		mdb_dec_indent(21);
410 		return (DCMD_ERR);
411 	}
412 	mdb_dec_indent(21);
413 	return (DCMD_OK);
414 }
415