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: ?


  27  * Copyright (c) 2012 by Delphix. All rights reserved.
  28  * Copyright (c) 2012 Joyent, Inc. All rights reserved.
  29  */
  30 
  31 #include <mdb/mdb_modapi.h>
  32 #include <mdb/mdb_target.h>
  33 #include <mdb/mdb_argvec.h>
  34 #include <mdb/mdb_string.h>
  35 #include <mdb/mdb_stdlib.h>
  36 #include <mdb/mdb_err.h>
  37 #include <mdb/mdb_debug.h>
  38 #include <mdb/mdb_fmt.h>
  39 #include <mdb/mdb_ctf.h>
  40 #include <mdb/mdb_ctf_impl.h>
  41 #include <mdb/mdb.h>
  42 #include <mdb/mdb_tab.h>
  43 
  44 #include <sys/isa_defs.h>
  45 #include <sys/param.h>
  46 #include <sys/sysmacros.h>

  47 #include <strings.h>
  48 #include <libctf.h>
  49 #include <ctype.h>
  50 
  51 typedef struct holeinfo {
  52         ulong_t hi_offset;              /* expected offset */
  53         uchar_t hi_isunion;             /* represents a union */
  54 } holeinfo_t;
  55 
  56 typedef struct printarg {
  57         mdb_tgt_t *pa_tgt;              /* current target */
  58         mdb_tgt_t *pa_realtgt;          /* real target (for -i) */
  59         mdb_tgt_t *pa_immtgt;           /* immediate target (for -i) */
  60         mdb_tgt_as_t pa_as;             /* address space to use for i/o */
  61         mdb_tgt_addr_t pa_addr;         /* base address for i/o */
  62         ulong_t pa_armemlim;            /* limit on array elements to print */
  63         ulong_t pa_arstrlim;            /* limit on array chars to print */
  64         const char *pa_delim;           /* element delimiter string */
  65         const char *pa_prefix;          /* element prefix string */
  66         const char *pa_suffix;          /* element suffix string */


1670  */
1671 static int
1672 pipe_print(mdb_ctf_id_t id, ulong_t off, void *data)
1673 {
1674         printarg_t *pap = data;
1675         ssize_t size;
1676         static const char *const fsp[] = { "%#r", "%#r", "%#r", "%#llr" };
1677         uintptr_t value;
1678         uintptr_t addr = pap->pa_addr + off / NBBY;
1679         mdb_ctf_id_t base;
1680         ctf_encoding_t e;
1681 
1682         union {
1683                 uint64_t i8;
1684                 uint32_t i4;
1685                 uint16_t i2;
1686                 uint8_t i1;
1687         } u;
1688 
1689         if (mdb_ctf_type_resolve(id, &base) == -1) {
1690                 mdb_warn("could not resolve type\n");
1691                 return (-1);
1692         }
1693 
1694         /*
1695          * If the user gives -a, then always print out the address of the
1696          * member.
1697          */
1698         if ((pap->pa_flags & PA_SHOWADDR)) {
1699                 mdb_printf("%#lr\n", addr);
1700                 return (0);
1701         }
1702 
1703 again:
1704         switch (mdb_ctf_type_kind(base)) {
1705         case CTF_K_POINTER:
1706                 if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
1707                     &value, sizeof (value), addr) != sizeof (value)) {
1708                         mdb_warn("failed to read pointer at %p", addr);
1709                         return (-1);
1710                 }


1986                  * name is at least one character long.
1987                  */
1988                 for (end = start + 1; isalnum(*end) || *end == '_'; end++)
1989                         continue;
1990 
1991                 (void) mdb_snprintf(member, end - start + 1, "%s", start);
1992 
1993                 if (mdb_ctf_member_info(rid, member, &off, &id) != 0) {
1994                         mdb_warn("failed to find member %s of %s", member,
1995                             mdb_ctf_type_name(id, buf, sizeof (buf)));
1996                         return (-1);
1997                 }
1998                 (void) mdb_ctf_type_resolve(id, &rid);
1999 
2000                 pap->pa_addr += off / NBBY;
2001 
2002                 start = end;
2003                 delim = parse_delimiter(&start);
2004         }
2005 
2006 
2007         *idp = id;
2008         *offp = off;
2009 
2010         return (0);
2011 }
2012 
2013 int
2014 cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2015     const mdb_arg_t *argv)
2016 {
2017         char tn[MDB_SYM_NAMLEN];
2018         char member[64];
2019         int i, dummy, delim, kind;
2020         int ret = 0;
2021         mdb_ctf_id_t id, rid;
2022         mdb_ctf_arinfo_t ar;
2023         char *start, *end;
2024         ulong_t dul;
2025 
2026         /*


2445             "-c limit   limit the length of character arrays\n"
2446             "-d         output values in decimal\n"
2447             "-h         print holes in structures\n"
2448             "-i         interpret address as data of the given type\n"
2449             "-L         unlimit the length of standard arrays\n"
2450             "-l limit   limit the length of standard arrays\n"
2451             "-n         don't print pointers as symbol offsets\n"
2452             "-p         interpret address as a physical memory address\n"
2453             "-s depth   limit the recursion depth\n"
2454             "-T         show type and <<base type>> of object\n"
2455             "-t         show type of object\n"
2456             "-x         output values in hexadecimal\n"
2457             "\n"
2458             "type may be omitted if the C type of addr can be inferred.\n"
2459             "\n"
2460             "Members may be specified with standard C syntax using the\n"
2461             "array indexing operator \"[index]\", structure member\n"
2462             "operator \".\", or structure pointer operator \"->\".\n"
2463             "\n"
2464             "Offsets must use the $[ expression ] syntax\n");



























































































































































































































































































































































































































































































































































































2465 }


  27  * Copyright (c) 2012 by Delphix. All rights reserved.
  28  * Copyright (c) 2012 Joyent, Inc. All rights reserved.
  29  */
  30 
  31 #include <mdb/mdb_modapi.h>
  32 #include <mdb/mdb_target.h>
  33 #include <mdb/mdb_argvec.h>
  34 #include <mdb/mdb_string.h>
  35 #include <mdb/mdb_stdlib.h>
  36 #include <mdb/mdb_err.h>
  37 #include <mdb/mdb_debug.h>
  38 #include <mdb/mdb_fmt.h>
  39 #include <mdb/mdb_ctf.h>
  40 #include <mdb/mdb_ctf_impl.h>
  41 #include <mdb/mdb.h>
  42 #include <mdb/mdb_tab.h>
  43 
  44 #include <sys/isa_defs.h>
  45 #include <sys/param.h>
  46 #include <sys/sysmacros.h>
  47 #include <netinet/in.h>
  48 #include <strings.h>
  49 #include <libctf.h>
  50 #include <ctype.h>
  51 
  52 typedef struct holeinfo {
  53         ulong_t hi_offset;              /* expected offset */
  54         uchar_t hi_isunion;             /* represents a union */
  55 } holeinfo_t;
  56 
  57 typedef struct printarg {
  58         mdb_tgt_t *pa_tgt;              /* current target */
  59         mdb_tgt_t *pa_realtgt;          /* real target (for -i) */
  60         mdb_tgt_t *pa_immtgt;           /* immediate target (for -i) */
  61         mdb_tgt_as_t pa_as;             /* address space to use for i/o */
  62         mdb_tgt_addr_t pa_addr;         /* base address for i/o */
  63         ulong_t pa_armemlim;            /* limit on array elements to print */
  64         ulong_t pa_arstrlim;            /* limit on array chars to print */
  65         const char *pa_delim;           /* element delimiter string */
  66         const char *pa_prefix;          /* element prefix string */
  67         const char *pa_suffix;          /* element suffix string */


1671  */
1672 static int
1673 pipe_print(mdb_ctf_id_t id, ulong_t off, void *data)
1674 {
1675         printarg_t *pap = data;
1676         ssize_t size;
1677         static const char *const fsp[] = { "%#r", "%#r", "%#r", "%#llr" };
1678         uintptr_t value;
1679         uintptr_t addr = pap->pa_addr + off / NBBY;
1680         mdb_ctf_id_t base;
1681         ctf_encoding_t e;
1682 
1683         union {
1684                 uint64_t i8;
1685                 uint32_t i4;
1686                 uint16_t i2;
1687                 uint8_t i1;
1688         } u;
1689 
1690         if (mdb_ctf_type_resolve(id, &base) == -1) {
1691                 mdb_warn("could not resolve type");
1692                 return (-1);
1693         }
1694 
1695         /*
1696          * If the user gives -a, then always print out the address of the
1697          * member.
1698          */
1699         if ((pap->pa_flags & PA_SHOWADDR)) {
1700                 mdb_printf("%#lr\n", addr);
1701                 return (0);
1702         }
1703 
1704 again:
1705         switch (mdb_ctf_type_kind(base)) {
1706         case CTF_K_POINTER:
1707                 if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
1708                     &value, sizeof (value), addr) != sizeof (value)) {
1709                         mdb_warn("failed to read pointer at %p", addr);
1710                         return (-1);
1711                 }


1987                  * name is at least one character long.
1988                  */
1989                 for (end = start + 1; isalnum(*end) || *end == '_'; end++)
1990                         continue;
1991 
1992                 (void) mdb_snprintf(member, end - start + 1, "%s", start);
1993 
1994                 if (mdb_ctf_member_info(rid, member, &off, &id) != 0) {
1995                         mdb_warn("failed to find member %s of %s", member,
1996                             mdb_ctf_type_name(id, buf, sizeof (buf)));
1997                         return (-1);
1998                 }
1999                 (void) mdb_ctf_type_resolve(id, &rid);
2000 
2001                 pap->pa_addr += off / NBBY;
2002 
2003                 start = end;
2004                 delim = parse_delimiter(&start);
2005         }
2006 

2007         *idp = id;
2008         *offp = off;
2009 
2010         return (0);
2011 }
2012 
2013 int
2014 cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2015     const mdb_arg_t *argv)
2016 {
2017         char tn[MDB_SYM_NAMLEN];
2018         char member[64];
2019         int i, dummy, delim, kind;
2020         int ret = 0;
2021         mdb_ctf_id_t id, rid;
2022         mdb_ctf_arinfo_t ar;
2023         char *start, *end;
2024         ulong_t dul;
2025 
2026         /*


2445             "-c limit   limit the length of character arrays\n"
2446             "-d         output values in decimal\n"
2447             "-h         print holes in structures\n"
2448             "-i         interpret address as data of the given type\n"
2449             "-L         unlimit the length of standard arrays\n"
2450             "-l limit   limit the length of standard arrays\n"
2451             "-n         don't print pointers as symbol offsets\n"
2452             "-p         interpret address as a physical memory address\n"
2453             "-s depth   limit the recursion depth\n"
2454             "-T         show type and <<base type>> of object\n"
2455             "-t         show type of object\n"
2456             "-x         output values in hexadecimal\n"
2457             "\n"
2458             "type may be omitted if the C type of addr can be inferred.\n"
2459             "\n"
2460             "Members may be specified with standard C syntax using the\n"
2461             "array indexing operator \"[index]\", structure member\n"
2462             "operator \".\", or structure pointer operator \"->\".\n"
2463             "\n"
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);
3036 }