xref: /illumos-gate/usr/src/cmd/ptools/pflags/pflags.c (revision 598f4ceed9327d2d6c2325dd67cae3aa06f7fea6)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
29  */
30 
31 #include <stdio.h>
32 #include <stdio_ext.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <ctype.h>
36 #include <fcntl.h>
37 #include <strings.h>
38 #include <dirent.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/int_fmtio.h>
42 #include <libproc.h>
43 
44 typedef struct look_arg {
45 	int pflags;
46 	const char *lwps;
47 	int count;
48 } look_arg_t;
49 
50 static	int	look(char *);
51 static	int	lwplook(look_arg_t *, const lwpstatus_t *, const lwpsinfo_t *);
52 static	char	*prflags(int);
53 static	char	*prwhy(int);
54 static	char	*prwhat(int, int);
55 static	void	dumpregs(const prgregset_t, int);
56 #if defined(__sparc) && defined(_ILP32)
57 static	void	dumpregs_v8p(const prgregset_t, const prxregset_t *, int);
58 #endif
59 
60 static	char	*command;
61 static	struct	ps_prochandle *Pr;
62 
63 static	int	is64;	/* Is current process 64-bit? */
64 static	int	rflag;	/* Show registers? */
65 
66 #define	LWPFLAGS	\
67 	(PR_STOPPED|PR_ISTOP|PR_DSTOP|PR_ASLEEP|PR_PCINVAL|PR_STEP \
68 	|PR_AGENT|PR_DETACH|PR_DAEMON)
69 
70 #define	PROCFLAGS	\
71 	(PR_ISSYS|PR_VFORKP|PR_ORPHAN|PR_NOSIGCHLD|PR_WAITPID \
72 	|PR_FORK|PR_RLC|PR_KLC|PR_ASYNC|PR_BPTADJ|PR_MSACCT|PR_MSFORK|PR_PTRACE)
73 
74 #define	ALLFLAGS	(LWPFLAGS|PROCFLAGS)
75 
76 int
77 main(int argc, char **argv)
78 {
79 	int rc = 0;
80 	int errflg = 0;
81 	int opt;
82 	struct rlimit rlim;
83 
84 	if ((command = strrchr(argv[0], '/')) != NULL)
85 		command++;
86 	else
87 		command = argv[0];
88 
89 	/* options */
90 	while ((opt = getopt(argc, argv, "r")) != EOF) {
91 		switch (opt) {
92 		case 'r':		/* show registers */
93 			rflag = 1;
94 			break;
95 		default:
96 			errflg = 1;
97 			break;
98 		}
99 	}
100 
101 	argc -= optind;
102 	argv += optind;
103 
104 	if (errflg || argc <= 0) {
105 		(void) fprintf(stderr,
106 		    "usage:\t%s [-r] { pid | core }[/lwps] ...\n", command);
107 		(void) fprintf(stderr, "  (report process status flags)\n");
108 		(void) fprintf(stderr, "  -r : report registers\n");
109 		return (2);
110 	}
111 
112 	/*
113 	 * Make sure we'll have enough file descriptors to handle a target
114 	 * that has many many mappings.
115 	 */
116 	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
117 		rlim.rlim_cur = rlim.rlim_max;
118 		(void) setrlimit(RLIMIT_NOFILE, &rlim);
119 		(void) enable_extended_FILE_stdio(-1, -1);
120 	}
121 
122 	while (argc-- > 0)
123 		rc += look(*argv++);
124 
125 	return (rc);
126 }
127 
128 static int
129 look(char *arg)
130 {
131 	int gcode;
132 	int gcode2;
133 	pstatus_t pstatus;
134 	psinfo_t psinfo;
135 	int flags;
136 	sigset_t sigmask;
137 	fltset_t fltmask;
138 	sysset_t entryset;
139 	sysset_t exitset;
140 	uint32_t sigtrace, sigtrace1, sigtrace2, fltbits;
141 	uint32_t sigpend, sigpend1, sigpend2;
142 	uint32_t *bits;
143 	char buf[PRSIGBUFSZ];
144 	look_arg_t lookarg;
145 
146 	if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_ANY,
147 	    PGRAB_RETAIN | PGRAB_FORCE | PGRAB_RDONLY | PGRAB_NOSTOP, &gcode,
148 	    &lookarg.lwps)) == NULL) {
149 		if (gcode == G_NOPROC &&
150 		    proc_arg_psinfo(arg, PR_ARG_PIDS, &psinfo, &gcode2) > 0 &&
151 		    psinfo.pr_nlwp == 0) {
152 			(void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid);
153 			return (0);
154 		}
155 		(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
156 		    command, arg, Pgrab_error(gcode));
157 		return (1);
158 	}
159 
160 	(void) memcpy(&pstatus, Pstatus(Pr), sizeof (pstatus_t));
161 	(void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
162 	proc_unctrl_psinfo(&psinfo);
163 
164 	if (psinfo.pr_nlwp == 0) {
165 		(void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid);
166 		Prelease(Pr, PRELEASE_RETAIN);
167 		return (0);
168 	}
169 
170 	is64 = (pstatus.pr_dmodel == PR_MODEL_LP64);
171 
172 	sigmask = pstatus.pr_sigtrace;
173 	fltmask = pstatus.pr_flttrace;
174 	entryset = pstatus.pr_sysentry;
175 	exitset = pstatus.pr_sysexit;
176 
177 	if (Pstate(Pr) == PS_DEAD) {
178 		(void) printf("core '%s' of %d:\t%.70s\n",
179 		    arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
180 	} else {
181 		(void) printf("%d:\t%.70s\n",
182 		    (int)psinfo.pr_pid, psinfo.pr_psargs);
183 	}
184 
185 	(void) printf("\tdata model = %s", is64? "_LP64" : "_ILP32");
186 	if ((flags = (pstatus.pr_flags & PROCFLAGS)) != 0)
187 		(void) printf("  flags = %s", prflags(flags));
188 	(void) printf("\n");
189 
190 	fltbits = *((uint32_t *)&fltmask);
191 	if (fltbits)
192 		(void) printf("\tflttrace = 0x%.8x\n", fltbits);
193 
194 #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32)	/* assumption */
195 	sigtrace = *((uint32_t *)&sigmask);
196 	sigtrace1 = *((uint32_t *)&sigmask + 1);
197 	sigtrace2 = *((uint32_t *)&sigmask + 2);
198 #else
199 #error "fix me: MAXSIG out of bounds"
200 #endif
201 	if (sigtrace | sigtrace1 | sigtrace2)
202 		(void) printf("\tsigtrace = 0x%.8x 0x%.8x 0x%.8x\n\t    %s\n",
203 		    sigtrace, sigtrace1, sigtrace2,
204 		    proc_sigset2str(&sigmask, "|", 1, buf, sizeof (buf)));
205 
206 	bits = ((uint32_t *)&entryset);
207 	if (bits[0] | bits[1] | bits[2] | bits[3] |
208 	    bits[4] | bits[5] | bits[6] | bits[7])
209 		(void) printf(
210 		    "\tentryset = "
211 		    "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
212 		    "\t           "
213 		    "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
214 		    bits[0], bits[1], bits[2], bits[3],
215 		    bits[4], bits[5], bits[6], bits[7]);
216 
217 	bits = ((uint32_t *)&exitset);
218 	if (bits[0] | bits[1] | bits[2] | bits[3] |
219 	    bits[4] | bits[5] | bits[6] | bits[7])
220 		(void) printf(
221 		    "\texitset  = "
222 		    "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
223 		    "\t           "
224 		    "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
225 		    bits[0], bits[1], bits[2], bits[3],
226 		    bits[4], bits[5], bits[6], bits[7]);
227 
228 #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32)	/* assumption */
229 	sigpend  = *((uint32_t *)&pstatus.pr_sigpend);
230 	sigpend1 = *((uint32_t *)&pstatus.pr_sigpend + 1);
231 	sigpend2 = *((uint32_t *)&pstatus.pr_sigpend + 2);
232 #else
233 #error "fix me: MAXSIG out of bounds"
234 #endif
235 	if (sigpend | sigpend1 | sigpend2)
236 		(void) printf("\tsigpend = 0x%.8x,0x%.8x,0x%.8x\n",
237 		    sigpend, sigpend1, sigpend2);
238 
239 	lookarg.pflags = pstatus.pr_flags;
240 	lookarg.count = 0;
241 	(void) Plwp_iter_all(Pr, (proc_lwp_all_f *)lwplook, &lookarg);
242 
243 	if (lookarg.count == 0)
244 		(void) printf("No matching lwps found");
245 
246 	(void) printf("\n");
247 	Prelease(Pr, PRELEASE_RETAIN);
248 
249 	return (0);
250 }
251 
252 static int
253 lwplook_zombie(const lwpsinfo_t *pip)
254 {
255 	(void) printf(" /%d:\t<defunct>\n", (int)pip->pr_lwpid);
256 	return (0);
257 }
258 
259 static int
260 lwplook(look_arg_t *arg, const lwpstatus_t *psp, const lwpsinfo_t *pip)
261 {
262 	int flags;
263 	uint32_t sighold, sighold1, sighold2;
264 	uint32_t sigpend, sigpend1, sigpend2;
265 	psinfo_t ps;
266 	int cursig;
267 	char buf[32];
268 
269 	if (!proc_lwp_in_set(arg->lwps, pip->pr_lwpid))
270 		return (0);
271 
272 	arg->count++;
273 
274 	if (psp == NULL)
275 		return (lwplook_zombie(pip));
276 
277 	/*
278 	 * PR_PCINVAL is just noise if the lwp is not stopped.
279 	 * Don't bother reporting it unless the lwp is stopped.
280 	 */
281 	flags = psp->pr_flags & LWPFLAGS;
282 	if (!(flags & PR_STOPPED))
283 		flags &= ~PR_PCINVAL;
284 
285 	(void) printf(" /%d:\tflags = %s", (int)psp->pr_lwpid, prflags(flags));
286 	if ((flags & PR_ASLEEP) || (psp->pr_syscall &&
287 	    !(arg->pflags & PR_ISSYS))) {
288 		if (flags & PR_ASLEEP) {
289 			if ((flags & ~PR_ASLEEP) != 0)
290 				(void) printf("|");
291 			(void) printf("ASLEEP");
292 		}
293 		if (psp->pr_syscall && !(arg->pflags & PR_ISSYS)) {
294 			uint_t i;
295 
296 			(void) printf("  %s(",
297 			    proc_sysname(psp->pr_syscall, buf, sizeof (buf)));
298 			for (i = 0; i < psp->pr_nsysarg; i++) {
299 				if (i != 0)
300 					(void) printf(",");
301 				(void) printf("0x%lx", psp->pr_sysarg[i]);
302 			}
303 			(void) printf(")");
304 		}
305 	}
306 	(void) printf("\n");
307 
308 	if (flags & PR_STOPPED) {
309 		(void) printf("\twhy = %s", prwhy(psp->pr_why));
310 		if (psp->pr_why != PR_REQUESTED &&
311 		    psp->pr_why != PR_SUSPENDED)
312 			(void) printf("  what = %s",
313 			    prwhat(psp->pr_why, psp->pr_what));
314 		(void) printf("\n");
315 	}
316 
317 #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32)	/* assumption */
318 	sighold  = *((uint32_t *)&psp->pr_lwphold);
319 	sighold1 = *((uint32_t *)&psp->pr_lwphold + 1);
320 	sighold2 = *((uint32_t *)&psp->pr_lwphold + 2);
321 	sigpend  = *((uint32_t *)&psp->pr_lwppend);
322 	sigpend1 = *((uint32_t *)&psp->pr_lwppend + 1);
323 	sigpend2 = *((uint32_t *)&psp->pr_lwppend + 2);
324 #else
325 #error "fix me: MAXSIG out of bounds"
326 #endif
327 	cursig   = psp->pr_cursig;
328 
329 	if (sighold | sighold1 | sighold2)
330 		(void) printf("\tsigmask = 0x%.8x,0x%.8x,0x%.8x\n",
331 		    sighold, sighold1, sighold2);
332 	if (sigpend | sigpend1 | sigpend2)
333 		(void) printf("\tlwppend = 0x%.8x,0x%.8x,0x%.8x\n",
334 		    sigpend, sigpend1, sigpend2);
335 	if (cursig)
336 		(void) printf("\tcursig = %s\n",
337 		    proc_signame(cursig, buf, sizeof (buf)));
338 
339 	if ((flags & PR_AGENT) &&
340 	    Plwp_getspymaster(Pr, pip->pr_lwpid, &ps) == 0) {
341 		time_t time = ps.pr_time.tv_sec;
342 		char t[64];
343 
344 		(void) strftime(t, sizeof (t), "%F:%H.%M.%S", localtime(&time));
345 
346 		(void) printf("\tspymaster = pid %d, \"%s\" at %s\n",
347 		    (int)ps.pr_pid, ps.pr_psargs, t);
348 	}
349 
350 	if (rflag) {
351 		if (Pstate(Pr) == PS_DEAD || (arg->pflags & PR_STOPPED)) {
352 #if defined(__sparc) && defined(_ILP32)
353 			/*
354 			 * If we're SPARC/32-bit, see if we can get extra
355 			 * register state for this lwp.  If it's a v8plus
356 			 * program, print the 64-bit register values.
357 			 */
358 			prxregset_t prx;
359 
360 			if (Plwp_getxregs(Pr, psp->pr_lwpid, &prx) == 0 &&
361 			    prx.pr_type == XR_TYPE_V8P)
362 				dumpregs_v8p(psp->pr_reg, &prx, is64);
363 			else
364 #endif	/* __sparc && _ILP32 */
365 				dumpregs(psp->pr_reg, is64);
366 		} else
367 			(void) printf("\tNot stopped, can't show registers\n");
368 	}
369 
370 	return (0);
371 }
372 
373 static char *
374 prflags(int arg)
375 {
376 	static char code_buf[200];
377 	char *str = code_buf;
378 
379 	if (arg == 0)
380 		return ("0");
381 
382 	if (arg & ~ALLFLAGS)
383 		(void) sprintf(str, "0x%x", arg & ~ALLFLAGS);
384 	else
385 		*str = '\0';
386 
387 	/*
388 	 * Display the semi-permanent lwp flags first.
389 	 */
390 	if (arg & PR_DAEMON)		/* daemons are always detached so */
391 		(void) strcat(str, "|DAEMON");
392 	else if (arg & PR_DETACH)	/* report detach only if non-daemon */
393 		(void) strcat(str, "|DETACH");
394 
395 	if (arg & PR_STOPPED)
396 		(void) strcat(str, "|STOPPED");
397 	if (arg & PR_ISTOP)
398 		(void) strcat(str, "|ISTOP");
399 	if (arg & PR_DSTOP)
400 		(void) strcat(str, "|DSTOP");
401 #if 0		/* displayed elsewhere */
402 	if (arg & PR_ASLEEP)
403 		(void) strcat(str, "|ASLEEP");
404 #endif
405 	if (arg & PR_PCINVAL)
406 		(void) strcat(str, "|PCINVAL");
407 	if (arg & PR_STEP)
408 		(void) strcat(str, "|STEP");
409 	if (arg & PR_AGENT)
410 		(void) strcat(str, "|AGENT");
411 	if (arg & PR_ISSYS)
412 		(void) strcat(str, "|ISSYS");
413 	if (arg & PR_VFORKP)
414 		(void) strcat(str, "|VFORKP");
415 	if (arg & PR_ORPHAN)
416 		(void) strcat(str, "|ORPHAN");
417 	if (arg & PR_NOSIGCHLD)
418 		(void) strcat(str, "|NOSIGCHLD");
419 	if (arg & PR_WAITPID)
420 		(void) strcat(str, "|WAITPID");
421 	if (arg & PR_FORK)
422 		(void) strcat(str, "|FORK");
423 	if (arg & PR_RLC)
424 		(void) strcat(str, "|RLC");
425 	if (arg & PR_KLC)
426 		(void) strcat(str, "|KLC");
427 	if (arg & PR_ASYNC)
428 		(void) strcat(str, "|ASYNC");
429 	if (arg & PR_BPTADJ)
430 		(void) strcat(str, "|BPTADJ");
431 	if (arg & PR_MSACCT)
432 		(void) strcat(str, "|MSACCT");
433 	if (arg & PR_MSFORK)
434 		(void) strcat(str, "|MSFORK");
435 	if (arg & PR_PTRACE)
436 		(void) strcat(str, "|PTRACE");
437 
438 	if (*str == '|')
439 		str++;
440 
441 	return (str);
442 }
443 
444 static char *
445 prwhy(int why)
446 {
447 	static char buf[20];
448 	char *str;
449 
450 	switch (why) {
451 	case PR_REQUESTED:
452 		str = "PR_REQUESTED";
453 		break;
454 	case PR_SIGNALLED:
455 		str = "PR_SIGNALLED";
456 		break;
457 	case PR_SYSENTRY:
458 		str = "PR_SYSENTRY";
459 		break;
460 	case PR_SYSEXIT:
461 		str = "PR_SYSEXIT";
462 		break;
463 	case PR_JOBCONTROL:
464 		str = "PR_JOBCONTROL";
465 		break;
466 	case PR_FAULTED:
467 		str = "PR_FAULTED";
468 		break;
469 	case PR_SUSPENDED:
470 		str = "PR_SUSPENDED";
471 		break;
472 	default:
473 		str = buf;
474 		(void) sprintf(str, "%d", why);
475 		break;
476 	}
477 
478 	return (str);
479 }
480 
481 static char *
482 prwhat(int why, int what)
483 {
484 	static char buf[32];
485 	char *str;
486 
487 	switch (why) {
488 	case PR_SIGNALLED:
489 	case PR_JOBCONTROL:
490 		str = proc_signame(what, buf, sizeof (buf));
491 		break;
492 	case PR_SYSENTRY:
493 	case PR_SYSEXIT:
494 		str = proc_sysname(what, buf, sizeof (buf));
495 		break;
496 	case PR_FAULTED:
497 		str = proc_fltname(what, buf, sizeof (buf));
498 		break;
499 	default:
500 		(void) sprintf(str = buf, "%d", what);
501 		break;
502 	}
503 
504 	return (str);
505 }
506 
507 #if defined(__sparc)
508 static const char * const regname[NPRGREG] = {
509 	" %g0", " %g1", " %g2", " %g3", " %g4", " %g5", " %g6", " %g7",
510 	" %o0", " %o1", " %o2", " %o3", " %o4", " %o5", " %sp", " %o7",
511 	" %l0", " %l1", " %l2", " %l3", " %l4", " %l5", " %l6", " %l7",
512 	" %i0", " %i1", " %i2", " %i3", " %i4", " %i5", " %fp", " %i7",
513 #ifdef __sparcv9
514 	"%ccr", " %pc", "%npc", "  %y", "%asi", "%fprs"
515 #else
516 	"%psr", " %pc", "%npc", "  %y", "%wim", "%tbr"
517 #endif
518 };
519 #endif	/* __sparc */
520 
521 #if defined(__amd64)
522 static const char * const regname[NPRGREG] = {
523 	"%r15", "%r14", "%r13", "%r12", "%r11", "%r10", " %r9", " %r8",
524 	"%rdi", "%rsi", "%rbp", "%rbx", "%rdx", "%rcx", "%rax", "%trapno",
525 	"%err", "%rip", " %cs", "%rfl", "%rsp", " %ss", " %fs", " %gs",
526 	" %es", " %ds", "%fsbase", "%gsbase"
527 };
528 
529 static const char * const regname32[NPRGREG32] = {
530 	" %gs", " %fs", " %es", " %ds", "%edi", "%esi", "%ebp", "%esp",
531 	"%ebx", "%edx", "%ecx", "%eax", "%trapno", "%err", "%eip", " %cs",
532 	"%efl", "%uesp", " %ss"
533 };
534 
535 /* XX64 Do we want to expose this through libproc */
536 void
537 prgregset_n_to_32(const prgreg_t *src, prgreg32_t *dst)
538 {
539 	bzero(dst, NPRGREG32 * sizeof (prgreg32_t));
540 	dst[GS] = src[REG_GS];
541 	dst[FS] = src[REG_FS];
542 	dst[DS] = src[REG_DS];
543 	dst[ES] = src[REG_ES];
544 	dst[EDI] = src[REG_RDI];
545 	dst[ESI] = src[REG_RSI];
546 	dst[EBP] = src[REG_RBP];
547 	dst[EBX] = src[REG_RBX];
548 	dst[EDX] = src[REG_RDX];
549 	dst[ECX] = src[REG_RCX];
550 	dst[EAX] = src[REG_RAX];
551 	dst[TRAPNO] = src[REG_TRAPNO];
552 	dst[ERR] = src[REG_ERR];
553 	dst[EIP] = src[REG_RIP];
554 	dst[CS] = src[REG_CS];
555 	dst[EFL] = src[REG_RFL];
556 	dst[UESP] = src[REG_RSP];
557 	dst[SS] = src[REG_SS];
558 }
559 
560 #elif defined(__i386)
561 static const char * const regname[NPRGREG] = {
562 	" %gs", " %fs", " %es", " %ds", "%edi", "%esi", "%ebp", "%esp",
563 	"%ebx", "%edx", "%ecx", "%eax", "%trapno", "%err", "%eip", " %cs",
564 	"%efl", "%uesp", " %ss"
565 };
566 #endif /* __i386 */
567 
568 #if defined(__amd64) && defined(_LP64)
569 static void
570 dumpregs32(const prgregset_t reg)
571 {
572 	prgregset32_t reg32;
573 	int i;
574 
575 	prgregset_n_to_32(reg, reg32);
576 
577 	for (i = 0; i < NPRGREG32; i++) {
578 		(void) printf("  %s = 0x%.8X",
579 		    regname32[i], reg32[i]);
580 		if ((i+1) % 4 == 0)
581 			(void) putchar('\n');
582 	}
583 	if (i % 4 != 0)
584 		(void) putchar('\n');
585 }
586 #endif
587 
588 static void
589 dumpregs(const prgregset_t reg, int is64)
590 {
591 	int width = is64? 16 : 8;
592 	int cols = is64? 2 : 4;
593 	int i;
594 
595 #if defined(__amd64) && defined(_LP64)
596 	if (!is64) {
597 		dumpregs32(reg);
598 		return;
599 	}
600 #endif
601 
602 	for (i = 0; i < NPRGREG; i++) {
603 		(void) printf("  %s = 0x%.*lX",
604 		    regname[i], width, (long)reg[i]);
605 		if ((i+1) % cols == 0)
606 			(void) putchar('\n');
607 	}
608 	if (i % cols != 0)
609 		(void) putchar('\n');
610 }
611 
612 #if defined(__sparc) && defined(_ILP32)
613 static void
614 dumpregs_v8p(const prgregset_t reg, const prxregset_t *xreg, int is64)
615 {
616 	static const uint32_t zero[8] = { 0 };
617 	int gr, xr, cols = 2;
618 	uint64_t xval;
619 
620 	if (memcmp(xreg->pr_un.pr_v8p.pr_xg, zero, sizeof (zero)) == 0 &&
621 	    memcmp(xreg->pr_un.pr_v8p.pr_xo, zero, sizeof (zero)) == 0) {
622 		dumpregs(reg, is64);
623 		return;
624 	}
625 
626 	for (gr = R_G0, xr = XR_G0; gr <= R_G7; gr++, xr++) {
627 		xval = (uint64_t)xreg->pr_un.pr_v8p.pr_xg[xr] << 32 |
628 		    (uint64_t)(uint32_t)reg[gr];
629 		(void) printf("  %s = 0x%.16" PRIX64, regname[gr], xval);
630 		if ((gr + 1) % cols == 0)
631 			(void) putchar('\n');
632 	}
633 
634 	for (gr = R_O0, xr = XR_O0; gr <= R_O7; gr++, xr++) {
635 		xval = (uint64_t)xreg->pr_un.pr_v8p.pr_xo[xr] << 32 |
636 		    (uint64_t)(uint32_t)reg[gr];
637 		(void) printf("  %s = 0x%.16" PRIX64, regname[gr], xval);
638 		if ((gr + 1) % cols == 0)
639 			(void) putchar('\n');
640 	}
641 
642 	for (gr = R_L0; gr < NPRGREG; gr++) {
643 		(void) printf("  %s =         0x%.8lX",
644 		    regname[gr], (long)reg[gr]);
645 		if ((gr + 1) % cols == 0)
646 			(void) putchar('\n');
647 	}
648 
649 	if (gr % cols != 0)
650 		(void) putchar('\n');
651 }
652 #endif	/* __sparc && _ILP32 */
653