Print this page
2574 mdb needs ::printf
Reviewed by: Joshua M. Clulow <josh@sysmgr.org>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Reviewed by: Adam Leventhal <ahl@delphix.com>
Approved by: ?

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/mdb/common/mdb/mdb_print.c
          +++ new/usr/src/cmd/mdb/common/mdb/mdb_print.c
↓ open down ↓ 36 lines elided ↑ open up ↑
  37   37  #include <mdb/mdb_debug.h>
  38   38  #include <mdb/mdb_fmt.h>
  39   39  #include <mdb/mdb_ctf.h>
  40   40  #include <mdb/mdb_ctf_impl.h>
  41   41  #include <mdb/mdb.h>
  42   42  #include <mdb/mdb_tab.h>
  43   43  
  44   44  #include <sys/isa_defs.h>
  45   45  #include <sys/param.h>
  46   46  #include <sys/sysmacros.h>
       47 +#include <netinet/in.h>
  47   48  #include <strings.h>
  48   49  #include <libctf.h>
  49   50  #include <ctype.h>
  50   51  
  51   52  typedef struct holeinfo {
  52   53          ulong_t hi_offset;              /* expected offset */
  53   54          uchar_t hi_isunion;             /* represents a union */
  54   55  } holeinfo_t;
  55   56  
  56   57  typedef struct printarg {
↓ open down ↓ 1623 lines elided ↑ open up ↑
1680 1681          ctf_encoding_t e;
1681 1682  
1682 1683          union {
1683 1684                  uint64_t i8;
1684 1685                  uint32_t i4;
1685 1686                  uint16_t i2;
1686 1687                  uint8_t i1;
1687 1688          } u;
1688 1689  
1689 1690          if (mdb_ctf_type_resolve(id, &base) == -1) {
1690      -                mdb_warn("could not resolve type\n");
     1691 +                mdb_warn("could not resolve type");
1691 1692                  return (-1);
1692 1693          }
1693 1694  
1694 1695          /*
1695 1696           * If the user gives -a, then always print out the address of the
1696 1697           * member.
1697 1698           */
1698 1699          if ((pap->pa_flags & PA_SHOWADDR)) {
1699 1700                  mdb_printf("%#lr\n", addr);
1700 1701                  return (0);
↓ open down ↓ 295 lines elided ↑ open up ↑
1996 1997                          return (-1);
1997 1998                  }
1998 1999                  (void) mdb_ctf_type_resolve(id, &rid);
1999 2000  
2000 2001                  pap->pa_addr += off / NBBY;
2001 2002  
2002 2003                  start = end;
2003 2004                  delim = parse_delimiter(&start);
2004 2005          }
2005 2006  
2006      -
2007 2007          *idp = id;
2008 2008          *offp = off;
2009 2009  
2010 2010          return (0);
2011 2011  }
2012 2012  
2013 2013  int
2014 2014  cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2015 2015      const mdb_arg_t *argv)
2016 2016  {
↓ open down ↓ 438 lines elided ↑ open up ↑
2455 2455              "-t         show type of object\n"
2456 2456              "-x         output values in hexadecimal\n"
2457 2457              "\n"
2458 2458              "type may be omitted if the C type of addr can be inferred.\n"
2459 2459              "\n"
2460 2460              "Members may be specified with standard C syntax using the\n"
2461 2461              "array indexing operator \"[index]\", structure member\n"
2462 2462              "operator \".\", or structure pointer operator \"->\".\n"
2463 2463              "\n"
2464 2464              "Offsets must use the $[ expression ] syntax\n");
     2465 +}
     2466 +
     2467 +static int
     2468 +printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt,
     2469 +    boolean_t sign)
     2470 +{
     2471 +        ssize_t size;
     2472 +        mdb_ctf_id_t base;
     2473 +        ctf_encoding_t e;
     2474 +
     2475 +        union {
     2476 +                uint64_t ui8;
     2477 +                uint32_t ui4;
     2478 +                uint16_t ui2;
     2479 +                uint8_t ui1;
     2480 +                int64_t i8;
     2481 +                int32_t i4;
     2482 +                int16_t i2;
     2483 +                int8_t i1;
     2484 +        } u;
     2485 +
     2486 +        if (mdb_ctf_type_resolve(id, &base) == -1) {
     2487 +                mdb_warn("could not resolve type");
     2488 +                return (DCMD_ABORT);
     2489 +        }
     2490 +
     2491 +        if (mdb_ctf_type_kind(base) != CTF_K_INTEGER) {
     2492 +                mdb_warn("expected integer type\n");
     2493 +                return (DCMD_ABORT);
     2494 +        }
     2495 +
     2496 +        if (mdb_ctf_type_encoding(base, &e) != 0) {
     2497 +                mdb_warn("could not get type encoding");
     2498 +                return (DCMD_ABORT);
     2499 +        }
     2500 +
     2501 +        if (sign)
     2502 +                sign = e.cte_format & CTF_INT_SIGNED;
     2503 +
     2504 +        size = e.cte_bits / NBBY;
     2505 +
     2506 +        /*
     2507 +         * Check to see if our life has been complicated by the presence of
     2508 +         * a bitfield.  If it has, we will print it using logic that is only
     2509 +         * slightly different than that found in print_bitfield(), above.  (In
     2510 +         * particular, see the comments there for an explanation of the
     2511 +         * endianness differences in this code.)
     2512 +         */
     2513 +        if (size > 8 || (e.cte_bits % NBBY) != 0 ||
     2514 +            (size & (size - 1)) != 0) {
     2515 +                uint64_t mask = (1ULL << e.cte_bits) - 1;
     2516 +                uint64_t value = 0;
     2517 +                uint8_t *buf = (uint8_t *)&value;
     2518 +                uint8_t shift;
     2519 +
     2520 +                /*
     2521 +                 * Round our size up one byte.
     2522 +                 */
     2523 +                size = (e.cte_bits + (NBBY - 1)) / NBBY;
     2524 +
     2525 +                if (e.cte_bits > sizeof (value) * NBBY - 1) {
     2526 +                        mdb_printf("invalid bitfield size %u", e.cte_bits);
     2527 +                        return (DCMD_ABORT);
     2528 +                }
     2529 +
     2530 +#ifdef _BIG_ENDIAN
     2531 +                buf += sizeof (value) - size;
     2532 +                off += e.cte_bits;
     2533 +#endif
     2534 +
     2535 +                if (mdb_vread(buf, size, addr) == -1) {
     2536 +                        mdb_warn("failed to read %lu bytes at %p", size, addr);
     2537 +                        return (DCMD_ERR);
     2538 +                }
     2539 +
     2540 +                shift = off % NBBY;
     2541 +#ifdef _BIG_ENDIAN
     2542 +                shift = NBBY - shift;
     2543 +#endif
     2544 +
     2545 +                /*
     2546 +                 * If we have a bit offset within the byte, shift it down.
     2547 +                 */
     2548 +                if (off % NBBY != 0)
     2549 +                        value >>= shift;
     2550 +                value &= mask;
     2551 +
     2552 +                if (sign) {
     2553 +                        int sshift = sizeof (value) * NBBY - e.cte_bits;
     2554 +                        value = ((int64_t)value << sshift) >> sshift;
     2555 +                }
     2556 +
     2557 +                mdb_printf(fmt, value);
     2558 +                return (0);
     2559 +        }
     2560 +
     2561 +        if (mdb_vread(&u.i8, size, addr) == -1) {
     2562 +                mdb_warn("failed to read %lu bytes at %p", (ulong_t)size, addr);
     2563 +                return (DCMD_ERR);
     2564 +        }
     2565 +
     2566 +        switch (size) {
     2567 +        case sizeof (uint8_t):
     2568 +                mdb_printf(fmt, (uint64_t)(sign ? u.i1 : u.ui1));
     2569 +                break;
     2570 +        case sizeof (uint16_t):
     2571 +                mdb_printf(fmt, (uint64_t)(sign ? u.i2 : u.ui2));
     2572 +                break;
     2573 +        case sizeof (uint32_t):
     2574 +                mdb_printf(fmt, (uint64_t)(sign ? u.i4 : u.ui4));
     2575 +                break;
     2576 +        case sizeof (uint64_t):
     2577 +                mdb_printf(fmt, (uint64_t)(sign ? u.i8 : u.ui8));
     2578 +                break;
     2579 +        }
     2580 +
     2581 +        return (0);
     2582 +}
     2583 +
     2584 +static int
     2585 +printf_int(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
     2586 +{
     2587 +        return (printf_signed(id, addr, off, fmt, B_TRUE));
     2588 +}
     2589 +
     2590 +static int
     2591 +printf_uint(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
     2592 +{
     2593 +        return (printf_signed(id, addr, off, fmt, B_FALSE));
     2594 +}
     2595 +
     2596 +/*ARGSUSED*/
     2597 +static int
     2598 +printf_uint32(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
     2599 +{
     2600 +        mdb_ctf_id_t base;
     2601 +        ctf_encoding_t e;
     2602 +        uint32_t value;
     2603 +
     2604 +        if (mdb_ctf_type_resolve(id, &base) == -1) {
     2605 +                mdb_warn("could not resolve type\n");
     2606 +                return (DCMD_ABORT);
     2607 +        }
     2608 +
     2609 +        if (mdb_ctf_type_kind(base) != CTF_K_INTEGER ||
     2610 +            mdb_ctf_type_encoding(base, &e) != 0 ||
     2611 +            e.cte_bits / NBBY != sizeof (value)) {
     2612 +                mdb_warn("expected 32-bit integer type\n");
     2613 +                return (DCMD_ABORT);
     2614 +        }
     2615 +
     2616 +        if (mdb_vread(&value, sizeof (value), addr) == -1) {
     2617 +                mdb_warn("failed to read 32-bit value at %p", addr);
     2618 +                return (DCMD_ERR);
     2619 +        }
     2620 +
     2621 +        mdb_printf(fmt, value);
     2622 +
     2623 +        return (0);
     2624 +}
     2625 +
     2626 +/*ARGSUSED*/
     2627 +static int
     2628 +printf_ptr(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
     2629 +{
     2630 +        uintptr_t value;
     2631 +        mdb_ctf_id_t base;
     2632 +
     2633 +        if (mdb_ctf_type_resolve(id, &base) == -1) {
     2634 +                mdb_warn("could not resolve type\n");
     2635 +                return (DCMD_ABORT);
     2636 +        }
     2637 +
     2638 +        if (mdb_ctf_type_kind(base) != CTF_K_POINTER) {
     2639 +                mdb_warn("expected pointer type\n");
     2640 +                return (DCMD_ABORT);
     2641 +        }
     2642 +
     2643 +        if (mdb_vread(&value, sizeof (value), addr) == -1) {
     2644 +                mdb_warn("failed to read pointer at %llx", addr);
     2645 +                return (DCMD_ERR);
     2646 +        }
     2647 +
     2648 +        mdb_printf(fmt, value);
     2649 +
     2650 +        return (0);
     2651 +}
     2652 +
     2653 +/*ARGSUSED*/
     2654 +static int
     2655 +printf_string(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
     2656 +{
     2657 +        mdb_ctf_id_t base;
     2658 +        mdb_ctf_arinfo_t r;
     2659 +        char buf[1024];
     2660 +        ssize_t size;
     2661 +
     2662 +        if (mdb_ctf_type_resolve(id, &base) == -1) {
     2663 +                mdb_warn("could not resolve type");
     2664 +                return (DCMD_ABORT);
     2665 +        }
     2666 +
     2667 +        if (mdb_ctf_type_kind(base) == CTF_K_POINTER) {
     2668 +                uintptr_t value;
     2669 +
     2670 +                if (mdb_vread(&value, sizeof (value), addr) == -1) {
     2671 +                        mdb_warn("failed to read pointer at %llx", addr);
     2672 +                        return (DCMD_ERR);
     2673 +                }
     2674 +
     2675 +                if (mdb_readstr(buf, sizeof (buf) - 1, value) < 0) {
     2676 +                        mdb_warn("failed to read string at %llx", value);
     2677 +                        return (DCMD_ERR);
     2678 +                }
     2679 +
     2680 +                mdb_printf(fmt, buf);
     2681 +                return (0);
     2682 +        }
     2683 +
     2684 +        if (mdb_ctf_type_kind(base) != CTF_K_ARRAY) {
     2685 +                mdb_warn("exepected pointer or array type\n");
     2686 +                return (DCMD_ABORT);
     2687 +        }
     2688 +
     2689 +        if (mdb_ctf_array_info(base, &r) == -1 ||
     2690 +            mdb_ctf_type_resolve(r.mta_contents, &base) == -1 ||
     2691 +            (size = mdb_ctf_type_size(base)) == -1) {
     2692 +                mdb_warn("can't determine array type");
     2693 +                return (DCMD_ABORT);
     2694 +        }
     2695 +
     2696 +        if (size != 1) {
     2697 +                mdb_warn("string format specifier requires "
     2698 +                    "an array of characters\n");
     2699 +                return (DCMD_ABORT);
     2700 +        }
     2701 +
     2702 +        bzero(buf, sizeof (buf));
     2703 +
     2704 +        if (mdb_vread(buf, MIN(r.mta_nelems, sizeof (buf) - 1), addr) == -1) {
     2705 +                mdb_warn("failed to read array at %p", addr);
     2706 +                return (DCMD_ERR);
     2707 +        }
     2708 +
     2709 +        mdb_printf(fmt, buf);
     2710 +
     2711 +        return (0);
     2712 +}
     2713 +
     2714 +/*ARGSUSED*/
     2715 +static int
     2716 +printf_ipv6(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
     2717 +{
     2718 +        mdb_ctf_id_t base;
     2719 +        mdb_ctf_id_t ipv6_type, ipv6_base;
     2720 +        in6_addr_t ipv6;
     2721 +
     2722 +        if (mdb_ctf_lookup_by_name("in6_addr_t", &ipv6_type) == -1) {
     2723 +                mdb_warn("could not resolve in6_addr_t type\n");
     2724 +                return (DCMD_ABORT);
     2725 +        }
     2726 +
     2727 +        if (mdb_ctf_type_resolve(id, &base) == -1) {
     2728 +                mdb_warn("could not resolve type\n");
     2729 +                return (DCMD_ABORT);
     2730 +        }
     2731 +
     2732 +        if (mdb_ctf_type_resolve(ipv6_type, &ipv6_base) == -1) {
     2733 +                mdb_warn("could not resolve in6_addr_t type\n");
     2734 +                return (DCMD_ABORT);
     2735 +        }
     2736 +
     2737 +        if (mdb_ctf_type_cmp(base, ipv6_base) != 0) {
     2738 +                mdb_warn("requires argument of type in6_addr_t\n");
     2739 +                return (DCMD_ABORT);
     2740 +        }
     2741 +
     2742 +        if (mdb_vread(&ipv6, sizeof (ipv6), addr) == -1) {
     2743 +                mdb_warn("couldn't read in6_addr_t at %p", addr);
     2744 +                return (DCMD_ERR);
     2745 +        }
     2746 +
     2747 +        mdb_printf(fmt, &ipv6);
     2748 +
     2749 +        return (0);
     2750 +}
     2751 +
     2752 +/*
     2753 + * To validate the format string specified to ::printf, we run the format
     2754 + * string through a very simple state machine that restricts us to a subset
     2755 + * of mdb_printf() functionality.
     2756 + */
     2757 +enum {
     2758 +        PRINTF_NOFMT = 1,               /* no current format specifier */
     2759 +        PRINTF_PERC,                    /* processed '%' */
     2760 +        PRINTF_FMT,                     /* processing format specifier */
     2761 +        PRINTF_LEFT,                    /* processed '-', expecting width */
     2762 +        PRINTF_WIDTH,                   /* processing width */
     2763 +        PRINTF_QUES                     /* processed '?', expecting format */
     2764 +};
     2765 +
     2766 +int
     2767 +cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
     2768 +{
     2769 +        char type[MDB_SYM_NAMLEN];
     2770 +        int i, nfmts = 0, ret;
     2771 +        mdb_ctf_id_t id;
     2772 +        const char *fmt, *member;
     2773 +        char **fmts, *last, *dest, f;
     2774 +        int (**funcs)(mdb_ctf_id_t, uintptr_t, ulong_t, char *);
     2775 +        int state = PRINTF_NOFMT;
     2776 +        printarg_t pa;
     2777 +
     2778 +        if (!(flags & DCMD_ADDRSPEC))
     2779 +                return (DCMD_USAGE);
     2780 +
     2781 +        bzero(&pa, sizeof (pa));
     2782 +        pa.pa_as = MDB_TGT_AS_VIRT;
     2783 +        pa.pa_realtgt = pa.pa_tgt = mdb.m_target;
     2784 +
     2785 +        if (argc == 0 || argv[0].a_type != MDB_TYPE_STRING) {
     2786 +                mdb_warn("expected a format string\n");
     2787 +                return (DCMD_USAGE);
     2788 +        }
     2789 +
     2790 +        /*
     2791 +         * Our first argument is a format string; rip it apart and run it
     2792 +         * through our state machine to validate that our input is within the
     2793 +         * subset of mdb_printf() format strings that we allow.
     2794 +         */
     2795 +        fmt = argv[0].a_un.a_str;
     2796 +        /*
     2797 +         * 'dest' must be large enough to hold a copy of the format string,
     2798 +         * plus a NUL and up to 2 additional characters for each conversion
     2799 +         * in the format string.  This gives us a bloat factor of 5/2 ~= 3.
     2800 +         *   e.g. "%d" (strlen of 2) --> "%lld\0" (need 5 bytes)
     2801 +         */
     2802 +        dest = mdb_zalloc(strlen(fmt) * 3, UM_SLEEP | UM_GC);
     2803 +        fmts = mdb_zalloc(strlen(fmt) * sizeof (char *), UM_SLEEP | UM_GC);
     2804 +        funcs = mdb_zalloc(strlen(fmt) * sizeof (void *), UM_SLEEP | UM_GC);
     2805 +        last = dest;
     2806 +
     2807 +        for (i = 0; fmt[i] != '\0'; i++) {
     2808 +                *dest++ = f = fmt[i];
     2809 +
     2810 +                switch (state) {
     2811 +                case PRINTF_NOFMT:
     2812 +                        state = f == '%' ? PRINTF_PERC : PRINTF_NOFMT;
     2813 +                        break;
     2814 +
     2815 +                case PRINTF_PERC:
     2816 +                        state = f == '-' ? PRINTF_LEFT :
     2817 +                            f >= '0' && f <= '9' ? PRINTF_WIDTH :
     2818 +                            f == '?' ? PRINTF_QUES :
     2819 +                            f == '%' ? PRINTF_NOFMT : PRINTF_FMT;
     2820 +                        break;
     2821 +
     2822 +                case PRINTF_LEFT:
     2823 +                        state = f >= '0' && f <= '9' ? PRINTF_WIDTH :
     2824 +                            f == '?' ? PRINTF_QUES : PRINTF_FMT;
     2825 +                        break;
     2826 +
     2827 +                case PRINTF_WIDTH:
     2828 +                        state = f >= '0' && f <= '9' ? PRINTF_WIDTH :
     2829 +                            PRINTF_FMT;
     2830 +                        break;
     2831 +
     2832 +                case PRINTF_QUES:
     2833 +                        state = PRINTF_FMT;
     2834 +                        break;
     2835 +                }
     2836 +
     2837 +                if (state != PRINTF_FMT)
     2838 +                        continue;
     2839 +
     2840 +                dest--;
     2841 +
     2842 +                /*
     2843 +                 * Now check that we have one of our valid format characters.
     2844 +                 */
     2845 +                switch (f) {
     2846 +                case 'a':
     2847 +                case 'A':
     2848 +                case 'p':
     2849 +                        funcs[nfmts] = printf_ptr;
     2850 +                        break;
     2851 +
     2852 +                case 'd':
     2853 +                case 'q':
     2854 +                case 'R':
     2855 +                        funcs[nfmts] = printf_int;
     2856 +                        *dest++ = 'l';
     2857 +                        *dest++ = 'l';
     2858 +                        break;
     2859 +
     2860 +                case 'I':
     2861 +                        funcs[nfmts] = printf_uint32;
     2862 +                        break;
     2863 +
     2864 +                case 'N':
     2865 +                        funcs[nfmts] = printf_ipv6;
     2866 +                        break;
     2867 +
     2868 +                case 'o':
     2869 +                case 'r':
     2870 +                case 'u':
     2871 +                case 'x':
     2872 +                case 'X':
     2873 +                        funcs[nfmts] = printf_uint;
     2874 +                        *dest++ = 'l';
     2875 +                        *dest++ = 'l';
     2876 +                        break;
     2877 +
     2878 +                case 's':
     2879 +                        funcs[nfmts] = printf_string;
     2880 +                        break;
     2881 +
     2882 +                case 'Y':
     2883 +                        funcs[nfmts] = sizeof (time_t) == sizeof (int) ?
     2884 +                            printf_uint32 : printf_uint;
     2885 +                        break;
     2886 +
     2887 +                default:
     2888 +                        mdb_warn("illegal format string at or near "
     2889 +                            "'%c' (position %d)\n", f, i + 1);
     2890 +                        return (DCMD_ABORT);
     2891 +                }
     2892 +
     2893 +                *dest++ = f;
     2894 +                *dest++ = '\0';
     2895 +                fmts[nfmts++] = last;
     2896 +                last = dest;
     2897 +                state = PRINTF_NOFMT;
     2898 +        }
     2899 +
     2900 +        argc--;
     2901 +        argv++;
     2902 +
     2903 +        /*
     2904 +         * Now we expect a type name.
     2905 +         */
     2906 +        if ((ret = args_to_typename(&argc, &argv, type, sizeof (type))) != 0)
     2907 +                return (ret);
     2908 +
     2909 +        argv++;
     2910 +        argc--;
     2911 +
     2912 +        if (mdb_ctf_lookup_by_name(type, &id) != 0) {
     2913 +                mdb_warn("failed to look up type %s", type);
     2914 +                return (DCMD_ABORT);
     2915 +        }
     2916 +
     2917 +        if (argc == 0) {
     2918 +                mdb_warn("at least one member must be specified\n");
     2919 +                return (DCMD_USAGE);
     2920 +        }
     2921 +
     2922 +        if (argc != nfmts) {
     2923 +                mdb_warn("%s format specifiers (found %d, expected %d)\n",
     2924 +                    argc > nfmts ? "missing" : "extra", nfmts, argc);
     2925 +                return (DCMD_ABORT);
     2926 +        }
     2927 +
     2928 +        for (i = 0; i < argc; i++) {
     2929 +                mdb_ctf_id_t mid;
     2930 +                ulong_t off;
     2931 +                int ignored;
     2932 +
     2933 +                if (argv[i].a_type != MDB_TYPE_STRING) {
     2934 +                        mdb_warn("expected only type member arguments\n");
     2935 +                        return (DCMD_ABORT);
     2936 +                }
     2937 +
     2938 +                if (strcmp((member = argv[i].a_un.a_str), ".") == 0) {
     2939 +                        /*
     2940 +                         * We allow "." to be specified to denote the current
     2941 +                         * value of dot.
     2942 +                         */
     2943 +                        if (funcs[i] != printf_ptr && funcs[i] != printf_uint &&
     2944 +                            funcs[i] != printf_int) {
     2945 +                                mdb_warn("expected integer or pointer format "
     2946 +                                    "specifier for '.'\n");
     2947 +                                return (DCMD_ABORT);
     2948 +                        }
     2949 +
     2950 +                        mdb_printf(fmts[i], mdb_get_dot());
     2951 +                        continue;
     2952 +                }
     2953 +
     2954 +                pa.pa_addr = addr;
     2955 +
     2956 +                if (parse_member(&pa, member, id, &mid, &off, &ignored) != 0)
     2957 +                        return (DCMD_ABORT);
     2958 +
     2959 +                if ((ret = funcs[i](mid, pa.pa_addr, off, fmts[i])) != 0) {
     2960 +                        mdb_warn("failed to print member '%s'\n", member);
     2961 +                        return (ret);
     2962 +                }
     2963 +        }
     2964 +
     2965 +        mdb_printf("%s", last);
     2966 +
     2967 +        return (DCMD_OK);
     2968 +}
     2969 +
     2970 +static char _mdb_printf_help[] =
     2971 +"The format string argument is a printf(3C)-like format string that is a\n"
     2972 +"subset of the format strings supported by mdb_printf().  The type argument\n"
     2973 +"is the name of a type to be used to interpret the memory referenced by dot.\n"
     2974 +"The member should either be a field in the specified structure, or the\n"
     2975 +"special member '.', denoting the value of dot (and treated as a pointer).\n"
     2976 +"The number of members must match the number of format specifiers in the\n"
     2977 +"format string.\n"
     2978 +"\n"
     2979 +"The following format specifiers are recognized by ::printf:\n"
     2980 +"\n"
     2981 +"  %%    Prints the '%' symbol.\n"
     2982 +"  %a    Prints the member in symbolic form.\n"
     2983 +"  %d    Prints the member as a decimal integer.  If the member is a signed\n"
     2984 +"        integer type, the output will be signed.\n"
     2985 +"  %I    Prints the member a IPv4 address (must be a 32-bit integer type).\n"
     2986 +"  %N    Prints the member an IPv6 address (must be of type in6_addr_t).\n"
     2987 +"  %o    Prints the member as an unsigned octal integer.\n"
     2988 +"  %p    Prints the member as a pointer, in hexadecimal.\n"
     2989 +"  %q    Prints the member in signed octal.  Honk if you ever use this!\n"
     2990 +"  %r    Prints the member as an unsigned value in the current output radix.\n"
     2991 +"  %R    Prints the member as a signed value in the current output radix.\n"
     2992 +"  %s    Prints the member as a string (requires a pointer or an array of\n"
     2993 +"        characters).\n"
     2994 +"  %u    Prints the member as an unsigned decimal integer.\n"
     2995 +"  %x    Prints the member in hexadecimal.\n"
     2996 +"  %X    Prints the member in hexadecimal, using the characters A-F as the\n"
     2997 +"        digits for the values 10-15.\n"
     2998 +"  %Y    Prints the member as a time_t as the string "
     2999 +            "'year month day HH:MM:SS'.\n"
     3000 +"\n"
     3001 +"The following field width specifiers are recognized by ::printf:\n"
     3002 +"\n"
     3003 +"  %n    Field width is set to the specified decimal value.\n"
     3004 +"  %?    Field width is set to the maximum width of a hexadecimal pointer\n"
     3005 +"        value.  This is 8 in an ILP32 environment, and 16 in an LP64\n"
     3006 +"        environment.\n"
     3007 +"\n"
     3008 +"The following flag specifers are recognized by ::printf:\n"
     3009 +"\n"
     3010 +"  %-    Left-justify the output within the specified field width.  If the\n"
     3011 +"        width of the output is less than the specified field width, the\n"
     3012 +"        output will be padded with blanks on the right-hand side.  Without\n"
     3013 +"        %-, values are right-justified by default.\n"
     3014 +"\n"
     3015 +"  %0    Zero-fill the output field if the output is right-justified and the\n"
     3016 +"        width of the output is less than the specified field width.  Without\n"
     3017 +"        %0, right-justified values are prepended with blanks in order to\n"
     3018 +"        fill the field.\n"
     3019 +"\n"
     3020 +"Examples: \n"
     3021 +"\n"
     3022 +"  ::walk proc | "
     3023 +        "::printf \"%-6d %s\\n\" proc_t p_pidp->pid_id p_user.u_psargs\n"
     3024 +"  ::walk thread | "
     3025 +        "::printf \"%?p %3d %a\\n\" kthread_t . t_pri t_startpc\n"
     3026 +"  ::walk zone | "
     3027 +        "::printf \"%-40s %20s\\n\" zone_t zone_name zone_nodename\n"
     3028 +"  ::walk ire | "
     3029 +        "::printf \"%Y %I\\n\" ire_t ire_create_time ire_u.ire4_u.ire4_addr\n"
     3030 +"\n";
     3031 +
     3032 +void
     3033 +printf_help(void)
     3034 +{
     3035 +        mdb_printf("%s", _mdb_printf_help);
2465 3036  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX