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