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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright (c) 2011 by Delphix. All rights reserved. 27 */ 28 /* 29 * Copyright (c) 2013, Joyent, Inc. All rights reserved. 30 */ 31 32 /* 33 * DTrace print() action 34 * 35 * This file contains the post-processing logic for the print() action. The 36 * print action behaves identically to trace() in that it generates a 37 * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type 38 * string stored in the DOF string table (similar to printf formats). We 39 * take the result of the trace action and post-process it in the fashion of 40 * MDB's ::print dcmd. 41 * 42 * This implementation differs from MDB's in the following ways: 43 * 44 * - We do not expose any options or flags. The behavior of print() is 45 * equivalent to "::print -tn". 46 * 47 * - MDB will display "holes" in structures (unused padding between 48 * members). 49 * 50 * - When printing arrays of structures, MDB will leave a trailing ',' 51 * after the last element. 52 * 53 * - MDB will print time_t types as date and time. 54 * 55 * - MDB will detect when an enum is actually the OR of several flags, 56 * and print it out with the constituent flags separated. 57 * 58 * - For large arrays, MDB will print the first few members and then 59 * print a "..." continuation line. 60 * 61 * - MDB will break and wrap arrays at 80 columns. 62 * 63 * - MDB prints out floats and doubles by hand, as it must run in kmdb 64 * context. We're able to leverage the printf() format strings, 65 * but the result is a slightly different format. 66 */ 67 68 #include <sys/sysmacros.h> 69 #include <strings.h> 70 #include <stdlib.h> 71 #include <alloca.h> 72 #include <assert.h> 73 #include <ctype.h> 74 #include <errno.h> 75 #include <limits.h> 76 #include <sys/socket.h> 77 #include <netdb.h> 78 #include <netinet/in.h> 79 #include <arpa/inet.h> 80 #include <arpa/nameser.h> 81 82 #include <dt_module.h> 83 #include <dt_printf.h> 84 #include <dt_string.h> 85 #include <dt_impl.h> 86 87 /* determines whether the given integer CTF encoding is a character */ 88 #define CTF_IS_CHAR(e) \ 89 (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \ 90 (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY) 91 /* determines whether the given CTF kind is a struct or union */ 92 #define CTF_IS_STRUCTLIKE(k) \ 93 ((k) == CTF_K_STRUCT || (k) == CTF_K_UNION) 94 95 /* 96 * Print structure passed down recursively through printing algorithm. 97 */ 98 typedef struct dt_printarg { 99 dtrace_hdl_t *pa_dtp; /* libdtrace handle */ 100 caddr_t pa_addr; /* base address of trace data */ 101 ctf_file_t *pa_ctfp; /* CTF container */ 102 int pa_depth; /* member depth */ 103 int pa_nest; /* nested array depth */ 104 FILE *pa_file; /* output file */ 105 } dt_printarg_t; 106 107 static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *); 108 109 /* 110 * Safe version of ctf_type_name() that will fall back to just "<ctfid>" if it 111 * can't resolve the type. 112 */ 113 static void 114 dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen) 115 { 116 if (ctf_type_name(ctfp, id, buf, buflen) == NULL) 117 (void) snprintf(buf, buflen, "<%ld>", id); 118 } 119 120 /* 121 * Print any necessary trailing braces for structures or unions. We don't get 122 * invoked when a struct or union ends, so we infer the need to print braces 123 * based on the depth the last time we printed something and the new depth. 124 */ 125 static void 126 dt_print_trailing_braces(dt_printarg_t *pap, int depth) 127 { 128 int d; 129 130 for (d = pap->pa_depth; d > depth; d--) { 131 (void) fprintf(pap->pa_file, "%*s}%s", 132 (d + pap->pa_nest - 1) * 4, "", 133 d == depth + 1 ? "" : "\n"); 134 } 135 } 136 137 /* 138 * Print the appropriate amount of indentation given the current depth and 139 * array nesting. 140 */ 141 static void 142 dt_print_indent(dt_printarg_t *pap) 143 { 144 (void) fprintf(pap->pa_file, "%*s", 145 (pap->pa_depth + pap->pa_nest) * 4, ""); 146 } 147 148 /* 149 * Print a bitfield. It's worth noting that the D compiler support for 150 * bitfields is currently broken; printing "D`user_desc_t" (pulled in by the 151 * various D provider files) will produce incorrect results compared to 152 * "genunix`user_desc_t". 153 */ 154 static void 155 print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep) 156 { 157 FILE *fp = pap->pa_file; 158 caddr_t addr = pap->pa_addr + off / NBBY; 159 uint64_t mask = (1ULL << ep->cte_bits) - 1; 160 uint64_t value = 0; 161 size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY; 162 uint8_t *buf = (uint8_t *)&value; 163 uint8_t shift; 164 165 /* 166 * On big-endian machines, we need to adjust the buf pointer to refer 167 * to the lowest 'size' bytes in 'value', and we need to shift based on 168 * the offset from the end of the data, not the offset of the start. 169 */ 170 #if BYTE_ORDER == _BIG_ENDIAN 171 buf += sizeof (value) - size; 172 off += ep->cte_bits; 173 #endif 174 bcopy(addr, buf, size); 175 shift = off % NBBY; 176 177 /* 178 * Offsets are counted from opposite ends on little- and 179 * big-endian machines. 180 */ 181 #if BYTE_ORDER == _BIG_ENDIAN 182 shift = NBBY - shift; 183 #endif 184 185 /* 186 * If the bits we want do not begin on a byte boundary, shift the data 187 * right so that the value is in the lowest 'cte_bits' of 'value'. 188 */ 189 if (off % NBBY != 0) 190 value >>= shift; 191 value &= mask; 192 193 (void) fprintf(fp, "%#llx", (u_longlong_t)value); 194 } 195 196 /* 197 * Dump the contents of memory as a fixed-size integer in hex. 198 */ 199 static void 200 dt_print_hex(FILE *fp, caddr_t addr, size_t size) 201 { 202 switch (size) { 203 case sizeof (uint8_t): 204 (void) fprintf(fp, "%#x", *(uint8_t *)addr); 205 break; 206 case sizeof (uint16_t): 207 /* LINTED - alignment */ 208 (void) fprintf(fp, "%#x", *(uint16_t *)addr); 209 break; 210 case sizeof (uint32_t): 211 /* LINTED - alignment */ 212 (void) fprintf(fp, "%#x", *(uint32_t *)addr); 213 break; 214 case sizeof (uint64_t): 215 (void) fprintf(fp, "%#llx", 216 /* LINTED - alignment */ 217 (unsigned long long)*(uint64_t *)addr); 218 break; 219 default: 220 (void) fprintf(fp, "<invalid size %u>", (uint_t)size); 221 } 222 } 223 224 /* 225 * Print an integer type. Before dumping the contents via dt_print_hex(), we 226 * first check the encoding to see if it's part of a bitfield or a character. 227 */ 228 static void 229 dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap) 230 { 231 FILE *fp = pap->pa_file; 232 ctf_file_t *ctfp = pap->pa_ctfp; 233 ctf_encoding_t e; 234 size_t size; 235 caddr_t addr = pap->pa_addr + off / NBBY; 236 237 if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) { 238 (void) fprintf(fp, "<unknown encoding>"); 239 return; 240 } 241 242 /* 243 * This comes from MDB - it's not clear under what circumstances this 244 * would be found. 245 */ 246 if (e.cte_format & CTF_INT_VARARGS) { 247 (void) fprintf(fp, "..."); 248 return; 249 } 250 251 /* 252 * We print this as a bitfield if the bit encoding indicates it's not 253 * an even power of two byte size, or is larger than 8 bytes. 254 */ 255 size = e.cte_bits / NBBY; 256 if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) { 257 print_bitfield(pap, off, &e); 258 return; 259 } 260 261 /* 262 * If this is a character, print it out as such. 263 */ 264 if (CTF_IS_CHAR(e)) { 265 char c = *(char *)addr; 266 if (isprint(c)) 267 (void) fprintf(fp, "'%c'", c); 268 else if (c == 0) 269 (void) fprintf(fp, "'\\0'"); 270 else 271 (void) fprintf(fp, "'\\%03o'", c); 272 return; 273 } 274 275 dt_print_hex(fp, addr, size); 276 } 277 278 /* 279 * Print a floating point (float, double, long double) value. 280 */ 281 /* ARGSUSED */ 282 static void 283 dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap) 284 { 285 FILE *fp = pap->pa_file; 286 ctf_file_t *ctfp = pap->pa_ctfp; 287 ctf_encoding_t e; 288 caddr_t addr = pap->pa_addr + off / NBBY; 289 290 if (ctf_type_encoding(ctfp, base, &e) == 0) { 291 if (e.cte_format == CTF_FP_SINGLE && 292 e.cte_bits == sizeof (float) * NBBY) { 293 /* LINTED - alignment */ 294 (void) fprintf(fp, "%+.7e", *((float *)addr)); 295 } else if (e.cte_format == CTF_FP_DOUBLE && 296 e.cte_bits == sizeof (double) * NBBY) { 297 /* LINTED - alignment */ 298 (void) fprintf(fp, "%+.7e", *((double *)addr)); 299 } else if (e.cte_format == CTF_FP_LDOUBLE && 300 e.cte_bits == sizeof (long double) * NBBY) { 301 /* LINTED - alignment */ 302 (void) fprintf(fp, "%+.16LE", *((long double *)addr)); 303 } else { 304 (void) fprintf(fp, "<unknown encoding>"); 305 } 306 } 307 } 308 309 /* 310 * A pointer is generally printed as a fixed-size integer. If we have a 311 * function pointer, we try to look up its name. 312 */ 313 static void 314 dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap) 315 { 316 FILE *fp = pap->pa_file; 317 ctf_file_t *ctfp = pap->pa_ctfp; 318 caddr_t addr = pap->pa_addr + off / NBBY; 319 size_t size = ctf_type_size(ctfp, base); 320 ctf_id_t bid = ctf_type_reference(ctfp, base); 321 uint64_t pc; 322 dtrace_syminfo_t dts; 323 GElf_Sym sym; 324 325 if (bid == CTF_ERR || ctf_type_kind(ctfp, bid) != CTF_K_FUNCTION) { 326 dt_print_hex(fp, addr, size); 327 } else { 328 /* LINTED - alignment */ 329 pc = *((uint64_t *)addr); 330 if (dtrace_lookup_by_addr(pap->pa_dtp, pc, &sym, &dts) != 0) { 331 dt_print_hex(fp, addr, size); 332 } else { 333 (void) fprintf(fp, "%s`%s", dts.dts_object, 334 dts.dts_name); 335 } 336 } 337 } 338 339 /* 340 * Print out an array. This is somewhat complex, as we must manually visit 341 * each member, and recursively invoke ctf_type_visit() for each member. If 342 * the members are non-structs, then we print them out directly: 343 * 344 * [ 0x14, 0x2e, 0 ] 345 * 346 * If they are structs, then we print out the necessary leading and trailing 347 * braces, to end up with: 348 * 349 * [ 350 * type { 351 * ... 352 * }, 353 * type { 354 * ... 355 * } 356 * ] 357 * 358 * We also use a heuristic to detect whether the array looks like a character 359 * array. If the encoding indicates it's a character, and we have all 360 * printable characters followed by a null byte, then we display it as a 361 * string: 362 * 363 * [ "string" ] 364 */ 365 static void 366 dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) 367 { 368 FILE *fp = pap->pa_file; 369 ctf_file_t *ctfp = pap->pa_ctfp; 370 caddr_t addr = pap->pa_addr + off / NBBY; 371 ctf_arinfo_t car; 372 ssize_t eltsize; 373 ctf_encoding_t e; 374 int i; 375 boolean_t isstring; 376 int kind; 377 ctf_id_t rtype; 378 379 if (ctf_array_info(ctfp, base, &car) == CTF_ERR) { 380 (void) fprintf(fp, "%p", (void *)addr); 381 return; 382 } 383 384 if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 || 385 (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR || 386 (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) { 387 (void) fprintf(fp, "<invalid type %lu>", car.ctr_contents); 388 return; 389 } 390 391 /* see if this looks like a string */ 392 isstring = B_FALSE; 393 if (kind == CTF_K_INTEGER && 394 ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) { 395 char c; 396 for (i = 0; i < car.ctr_nelems; i++) { 397 c = *((char *)addr + eltsize * i); 398 if (!isprint(c) || c == '\0') 399 break; 400 } 401 402 if (i != car.ctr_nelems && c == '\0') 403 isstring = B_TRUE; 404 } 405 406 /* 407 * As a slight aesthetic optimization, if we are a top-level type, then 408 * don't bother printing out the brackets. This lets print("foo") look 409 * like: 410 * 411 * string "foo" 412 * 413 * As D will internally represent this as a char[256] array. 414 */ 415 if (!isstring || pap->pa_depth != 0) 416 (void) fprintf(fp, "[ "); 417 418 if (isstring) 419 (void) fprintf(fp, "\""); 420 421 for (i = 0; i < car.ctr_nelems; i++) { 422 if (isstring) { 423 char c = *((char *)addr + eltsize * i); 424 if (c == '\0') 425 break; 426 (void) fprintf(fp, "%c", c); 427 } else { 428 /* 429 * Recursively invoke ctf_type_visit() on each member. 430 * We setup a new printarg struct with 'pa_nest' set to 431 * indicate that we are within a nested array. 432 */ 433 dt_printarg_t pa = *pap; 434 pa.pa_nest += pap->pa_depth + 1; 435 pa.pa_depth = 0; 436 pa.pa_addr = addr + eltsize * i; 437 (void) ctf_type_visit(ctfp, car.ctr_contents, 438 dt_print_member, &pa); 439 440 dt_print_trailing_braces(&pa, 0); 441 if (i != car.ctr_nelems - 1) 442 (void) fprintf(fp, ", "); 443 else if (CTF_IS_STRUCTLIKE(kind)) 444 (void) fprintf(fp, "\n"); 445 } 446 } 447 448 if (isstring) 449 (void) fprintf(fp, "\""); 450 451 if (!isstring || pap->pa_depth != 0) { 452 if (CTF_IS_STRUCTLIKE(kind)) 453 dt_print_indent(pap); 454 else 455 (void) fprintf(fp, " "); 456 (void) fprintf(fp, "]"); 457 } 458 } 459 460 /* 461 * This isued by both structs and unions to print the leading brace. 462 */ 463 /* ARGSUSED */ 464 static void 465 dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap) 466 { 467 (void) fprintf(pap->pa_file, "{"); 468 } 469 470 /* 471 * For enums, we try to print the enum name, and fall back to the value if it 472 * can't be determined. We do not do any fancy flag processing like mdb. 473 */ 474 /* ARGSUSED */ 475 static void 476 dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap) 477 { 478 FILE *fp = pap->pa_file; 479 ctf_file_t *ctfp = pap->pa_ctfp; 480 const char *ename; 481 ssize_t size; 482 caddr_t addr = pap->pa_addr + off / NBBY; 483 int value = 0; 484 485 /* 486 * The C standard says that an enum will be at most the sizeof (int). 487 * But if all the values are less than that, the compiler can use a 488 * smaller size. Thanks standards. 489 */ 490 size = ctf_type_size(ctfp, base); 491 switch (size) { 492 case sizeof (uint8_t): 493 value = *(uint8_t *)addr; 494 break; 495 case sizeof (uint16_t): 496 value = *(uint16_t *)addr; 497 break; 498 case sizeof (int32_t): 499 value = *(int32_t *)addr; 500 break; 501 default: 502 (void) fprintf(fp, "<invalid enum size %u>", (uint_t)size); 503 return; 504 } 505 506 if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) 507 (void) fprintf(fp, "%s", ename); 508 else 509 (void) fprintf(fp, "%d", value); 510 } 511 512 /* 513 * Forward declaration. There's not much to do here without the complete 514 * type information, so just print out this fact and drive on. 515 */ 516 /* ARGSUSED */ 517 static void 518 dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap) 519 { 520 (void) fprintf(pap->pa_file, "<forward decl>"); 521 } 522 523 typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *); 524 525 static dt_printarg_f *const dt_printfuncs[] = { 526 dt_print_int, /* CTF_K_INTEGER */ 527 dt_print_float, /* CTF_K_FLOAT */ 528 dt_print_ptr, /* CTF_K_POINTER */ 529 dt_print_array, /* CTF_K_ARRAY */ 530 dt_print_ptr, /* CTF_K_FUNCTION */ 531 dt_print_structlike, /* CTF_K_STRUCT */ 532 dt_print_structlike, /* CTF_K_UNION */ 533 dt_print_enum, /* CTF_K_ENUM */ 534 dt_print_tag /* CTF_K_FORWARD */ 535 }; 536 537 /* 538 * Print one member of a structure. This callback is invoked from 539 * ctf_type_visit() recursively. 540 */ 541 static int 542 dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth, 543 void *data) 544 { 545 char type[DT_TYPE_NAMELEN]; 546 int kind; 547 dt_printarg_t *pap = data; 548 FILE *fp = pap->pa_file; 549 ctf_file_t *ctfp = pap->pa_ctfp; 550 boolean_t arraymember; 551 boolean_t brief; 552 ctf_encoding_t e; 553 ctf_id_t rtype; 554 555 dt_print_trailing_braces(pap, depth); 556 /* 557 * dt_print_trailing_braces() doesn't include the trailing newline; add 558 * it here if necessary. 559 */ 560 if (depth < pap->pa_depth) 561 (void) fprintf(fp, "\n"); 562 pap->pa_depth = depth; 563 564 if ((rtype = ctf_type_resolve(ctfp, id)) == CTF_ERR || 565 (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR || 566 kind < CTF_K_INTEGER || kind > CTF_K_FORWARD) { 567 dt_print_indent(pap); 568 (void) fprintf(fp, "%s = <invalid type %lu>", name, id); 569 return (0); 570 } 571 572 dt_print_type_name(ctfp, id, type, sizeof (type)); 573 574 arraymember = (pap->pa_nest != 0 && depth == 0); 575 brief = (arraymember && !CTF_IS_STRUCTLIKE(kind)); 576 577 if (!brief) { 578 /* 579 * If this is a direct array member and a struct (otherwise 580 * brief would be true), then print a trailing newline, as the 581 * array printing code doesn't include it because it might be a 582 * simple type. 583 */ 584 if (arraymember) 585 (void) fprintf(fp, "\n"); 586 dt_print_indent(pap); 587 588 /* always print the type */ 589 (void) fprintf(fp, "%s", type); 590 if (name[0] != '\0') { 591 /* 592 * For aesthetics, we don't include a space between the 593 * type name and member name if the type is a pointer. 594 * This will give us "void *foo =" instead of "void * 595 * foo =". Unions also have the odd behavior that the 596 * type name is returned as "union ", with a trailing 597 * space, so we also avoid printing a space if the type 598 * name already ends with a space. 599 */ 600 if (type[strlen(type) - 1] != '*' && 601 type[strlen(type) -1] != ' ') { 602 (void) fprintf(fp, " "); 603 } 604 (void) fprintf(fp, "%s", name); 605 606 /* 607 * If this looks like a bitfield, or is an integer not 608 * aligned on a byte boundary, print the number of 609 * bits after the name. 610 */ 611 if (kind == CTF_K_INTEGER && 612 ctf_type_encoding(ctfp, id, &e) == 0) { 613 ulong_t bits = e.cte_bits; 614 ulong_t size = bits / NBBY; 615 616 if (bits % NBBY != 0 || 617 off % NBBY != 0 || 618 size > 8 || 619 size != ctf_type_size(ctfp, id)) { 620 (void) fprintf(fp, " :%lu", bits); 621 } 622 } 623 624 (void) fprintf(fp, " ="); 625 } 626 (void) fprintf(fp, " "); 627 } 628 629 dt_printfuncs[kind - 1](rtype, off, pap); 630 631 /* direct simple array members are not separated by newlines */ 632 if (!brief) 633 (void) fprintf(fp, "\n"); 634 635 return (0); 636 } 637 638 /* 639 * Main print function invoked by dt_consume_cpu(). 640 */ 641 int 642 dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, 643 caddr_t addr, size_t len) 644 { 645 const char *s; 646 char *object; 647 dt_printarg_t pa; 648 ctf_id_t id; 649 dt_module_t *dmp; 650 ctf_file_t *ctfp; 651 int libid; 652 653 /* 654 * Split the fully-qualified type ID (module`id). This should 655 * always be the format, but if for some reason we don't find the 656 * expected value, return 0 to fall back to the generic trace() 657 * behavior. In the case of userland CTF modules this will actually be 658 * of the format (module`lib`id). This is due to the fact that those 659 * modules have multiple CTF containers which `lib` identifies. 660 */ 661 for (s = typename; *s != '\0' && *s != '`'; s++) 662 ; 663 664 if (*s != '`') 665 return (0); 666 667 object = alloca(s - typename + 1); 668 bcopy(typename, object, s - typename); 669 object[s - typename] = '\0'; 670 dmp = dt_module_lookup_by_name(dtp, object); 671 if (dmp == NULL) 672 return (0); 673 674 if (dmp->dm_pid != 0) { 675 libid = atoi(s + 1); 676 s = strchr(s + 1, '`'); 677 if (s == NULL || libid > dmp->dm_nctflibs) 678 return (0); 679 ctfp = dmp->dm_libctfp[libid]; 680 } else { 681 ctfp = dt_module_getctf(dtp, dmp); 682 } 683 684 id = atoi(s + 1); 685 686 /* 687 * Try to get the CTF kind for this id. If something has gone horribly 688 * wrong and we can't resolve the ID, bail out and let trace() do the 689 * work. 690 */ 691 if (ctfp == NULL || ctf_type_kind(ctfp, id) == CTF_ERR) 692 return (0); 693 694 /* setup the print structure and kick off the main print routine */ 695 pa.pa_dtp = dtp; 696 pa.pa_addr = addr; 697 pa.pa_ctfp = ctfp; 698 pa.pa_nest = 0; 699 pa.pa_depth = 0; 700 pa.pa_file = fp; 701 (void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa); 702 703 dt_print_trailing_braces(&pa, 0); 704 705 return (len); 706 } 707