6 Output Formatting

DTrace provides the built-in printf and printaformatting functions, which you can use from your D programs to format output. The D compiler provides features that are not found in the C library's printf() routine, so be sure to read this chapter even if you are already familiar with printf.

This chapter also discusses the formatting behavior of the trace function and the default output format that is used by the dtrace command to display aggregations.

printf Action

The printf action combines the ability to trace data, as if by the trace function, but with the ability to output the data and other text in a specific format that you describe. The printf function directs DTrace to trace the data associated with each argument after the first argument and then format the results using the rules described by the first printf argument, known as a format string. The format string is a regular string that contains any number of format conversions, each beginning with a % character, that describe how to format the corresponding argument. The first conversion in the format string corresponds to the second printf argument, the second conversion to the third argument, and so on. All of the text between conversions is printed verbatim. The character following the % conversion character describes the format to use for the corresponding argument.

Unlike the C library's printf() function, DTrace's printf function is a built-in function that is recognized by the D compiler. The D compiler provides several useful services for the DTrace printf function that are not found in printf(), including the following:

  • The D compiler compares the arguments to the conversions in the format string. If an argument's type is incompatible with the format conversion, the D compiler provides an error message explaining the problem.

  • The D compiler does not require the use of size prefixes with printf format conversions. The C printf routine requires that you indicate the size of arguments by adding prefixes such as %ld for long, or %lld for long long. The D compiler is aware of the size and type of your arguments, so these prefixes are not required in your D printf statements.

  • DTrace provides additional format characters that are useful for debugging and observability. For example, the %a format conversion can be used to print a pointer as a symbol name and offset.

To implement these features, you must specify the format string in the DTrace printf function as a string constant in your D program. Format strings cannot be dynamic variables of type string.

Conversion Specifications

Each conversion specification in the format string is introduced by the % character, after which the following information appears in sequence:

  • Zero or more flags (in any order), that modify the meaning of the conversion specification, as described in Flag Specifiers.

  • An optional minimum field width. If the converted value has fewer bytes than the field width, the value is padded with spaces on the left, by default, or on the right, if the left-adjustment flag (-) is specified. The field width can also be specified as an asterisk (*), in which case the field width is set dynamically, based on the value of an additional argument of type int.

  • An optional precision specifier that indicates the following:

    • The minimum number of digits to appear for the d, i, o, u, x, and X conversions— the field is padded with leading zeroes—the number of digits to appear after the radix character for the e, E, and f conversions.

    • The maximum number of significant digits for the g and G conversions.

    • Or the maximum number of bytes to be printed from a string by the s conversion.

    The precision specifier takes the form of a period (.), followed by either an asterisk (*), as described in Width and Precision Specifiers, or a decimal digit string.

  • An optional sequence of size prefixes that indicate the size of the corresponding argument. Size prefixes are not required in D, but are provided for compatibility with the C printf() function.

  • A conversion specifier that indicates the type of conversion to be applied to the argument.

The C printf() function also supports conversion specifications of the form %n$, where n is a decimal integer. Note that the DTrace printf function does not support this type of conversion specification.

Flag Specifiers

The printf conversion flags are enabled by specifying one or more of the following characters, which can appear in any order, as described in the following table.
Flag Specifier Description

'

The integer portion of the result of a decimal conversion (%d, %f, %g, %G, %i, or %u) is formatted with thousands of grouping characters by using the non-monetary grouping character. Some locales, including the POSIX C locale, do not provide non-monetary grouping characters for use with this flag. (The relevant locale is the locale in which dtrace is running.)

-

The result of the conversion is left-justified within the field. The conversion is right-justified if this flag is not specified.

+

The result of signed conversion always begins with a sign (+ or -). If this flag is not specified, the conversion begins with a sign only when a negative value is converted.

space

If the first character of a signed conversion is not a sign or if a signed conversion results in no characters, a space is placed before the result. If the space and + flags both appear, the space flag is ignored.

#

The value is converted to an alternate form if an alternate form is defined for the selected conversion. The alternate formats for conversions are described along with the corresponding conversion.

0

For d, e, E, f, g, G, i, o, u, x, and X conversions, leading zeroes (following any indication of sign or base) are used to pad the field width and no space padding is performed. If the 0 and - flags both appear, the 0 flag is ignored. For d, i, o, u, x and X conversions, if a precision is specified, the 0 flag is ignored. If the 0 and ' flags both appear, the grouping characters are inserted before the zero padding.

Width and Precision Specifiers

The minimum field width can be specified as a decimal-digit string following any flag specifier, in which case the field width is set to the specified number of columns. The field width can also be specified as asterisk (*) in which case an additional argument of type int is accessed to determine the field width.

For example, to print an integer x in a field width determined by the value of the int variable w, you would write the following D statement:

printf("%*d", w, x);

The field width can also be specified with a ? character to indicate that the field width should be set based on the number of characters required to format an address (in hexadecimal) in the data model of the operating system kernel. The width is set to 8, if the kernel is using the 32-bit data model, or to 16, if the kernel is using the 64-bit data model. The precision for the conversion can be specified as a decimal digit string following a period (.), or by an asterisk (*) following a period. If an asterisk is used to specify the precision, an additional argument of type int before the conversion argument provides the precision. If both width and precision are specified as asterisks, the order of arguments to printf for the conversion should appear in the following order: width, precision, value.

Size Prefixes

Size prefixes are required in ANSI C programs that use printf() to indicate the size and type of the conversion argument. The D compiler performs this processing for your printf calls automatically, so size prefixes are not required. Although size prefixes are provided for C compatibility, their use is explicitly discouraged in D programs because they bind your code to a particular data model when using derived types.

For example, if a typedef is redefined to different integer base types depending on the data model, it is not possible to use a single C conversion that works in both data models without explicitly knowing the two underlying types and including a cast expression or defining multiple format strings. The D compiler solves this problem automatically by enabling you to omit size prefixes and automatically determining the argument size.

Size prefixes can be placed just prior to the format conversion name and after any flags, widths, and precision specifiers and are as follows:

  • An optional h specifies that a following d, i, o, u, x, or X conversion applies to a short or unsigned short.

  • An optional l specifies that a following d, i, o, u, x, or X conversion applies to a long or unsigned long.

  • An optional ll specifies that a following d, i, o, u, x, or X conversion applies to a long long or unsigned long long.

  • An optional L specifies that a following e, E, f, g, or G conversion applies to a long double.

  • An optional l specifies that a following c conversion applies to a wint_t argument, and that a following s conversion character applies to a pointer to a wchar_t argument.

Conversion Formats

Each conversion character sequence results in fetching zero or more arguments. If insufficient arguments are provided for the format string, if the format string is exhausted and arguments remain, or if an undefined conversion format is specified, then the D compiler issues an appropriate error message. The following table describes the conversion character sequences.
Conversion Characters Description

a

The pointer or uintptr_t argument is printed as a kernel symbol name in the form module'symbol-name, plus an optional hexadecimal byte offset. If the value does not fall within the range that is defined by a known kernel symbol, the value is printed as a hexadecimal integer.

A

Identical to %a, but is used for user symbols.

c

The char, short, or int argument is printed as an ASCII character.

C

The char, short, or int argument is printed as an ASCII character if the character is a printable ASCII character. If the character is not a printable character, it is printed by using the corresponding escape sequence, as shown in Table 2-6.

d

The char, int, long, long long, or short argument is printed as a decimal (base 10) integer. If the argument is signed, it is printed as a signed value. If the argument is unsigned, it is printed as an unsigned value. This conversion has the same meaning as i.

e, E

The double, float, or long double argument is converted to the style [-]d.ddde[+-]dd , where there is one digit before the radix character and the number of digits that follow is equal to the precision. The radix character is non-zero if the argument is non-zero. If the precision is not specified, the default precision value is 6. If the precision is 0 and the # flag is not specified, no radix character appears. The E conversion format produces a number with E introducing the exponent, instead of e. The exponent always contains at least two digits. The value is rounded up to the appropriate number of digits.

f

The double, float, or long double argument is converted to the style [-]ddd.ddd , where the number of digits after the radix character is equal to the precision specification. If the precision is not specified, the default precision value is 6. If the precision is 0 and the # flag is not specified, no radix character appears. If a radix character appears, at least one digit appears before it. The value is rounded up to the appropriate number of digits.

g, G

The double, float, or long double argument is printed in the style f or e (or in style E in the case of a G conversion character), with the precision specifying the number of significant digits. If an explicit precision is 0, it is taken as 1. The style that is used depends on the value converted: style e (or E) is used only if the exponent resulting from the conversion is less than -4, or greater than or equal to the precision. Trailing zeroes are removed from the fractional part of the result. A radix character appears only if it is followed by a digit. If the # flag is specified, trailing zeroes are not removed from the result.

i

The char, int, long, long long, or short argument is printed as a decimal (base 10) integer. If the argument is signed, it is printed as a signed value. If the argument is unsigned, it is printed as an unsigned value. This conversion has the same meaning as d.

k

The stack argument is printed as if by a call to trace() and handles kernel-level stacks. This argument is valid only with printa because stack cannot be called from a D expression, as a D program context is required.

o

The char, int, long, long long, and short argument is printed as an unsigned octal (base 8) integer. Arguments that are signed or unsigned may be used with this conversion. If the # flag is specified, the precision of the result is increased to force the first digit of the result to be a zero, if necessary.

p

The pointer or uintptr_t argument is printed as a hexadecimal (base 16) integer. D accepts pointer arguments of any type. If the # flag is specified, a non-zero result has 0x prepended to it.

s

The argument must be an array of char or a string. Bytes from the array or string are read up to a terminating null character or the end of the data and interpreted and printed as ASCII characters. If the precision is not specified, it is taken to be infinite so that all characters up to the first null character are printed. If the precision is specified, only the portion of the character array that is displayed in the corresponding number of screen columns is printed. If an argument of type char * is to be formatted, it should be cast to string or prefixed with the D stringof operator to indicate that DTrace should trace the bytes of the string and format them.

S

The argument must be an array of char or string. The argument is processed as if by the %s conversion, but any ASCII characters that are not printable are replaced by the corresponding escape sequence, as described in Table 2-6.

u

The char, int, long, long long, or short argument is printed as an unsigned decimal (base 10) integer. Arguments that are signed or unsigned can be used with this conversion. The result is always formatted as unsigned.

wc

The int argument is converted to a wide character (wchar_t) and the resulting wide character is printed.

ws

The argument must be an array of wchar_t. Bytes from the array are read up to a terminating null character or the end of the data and interpreted and printed as wide characters. If the precision is not specified, it is taken to be infinite, so all wide characters up to the first null character are printed. If the precision is specified, only that portion of the wide character array that is displayed in the corresponding number of screen columns is printed.

x, X

The char, int, long, long long, or short argument is printed as an unsigned hexadecimal (base 16) integer. Arguments that are signed or unsigned may be used with this conversion. If the x form of the conversion is used, the letter digits abcdef are used. If the X form of the conversion is used, the letter digits ABCDEF are used. If the # flag is specified, a non-zero result has 0x (for %x) or 0X (for %X) that is prepended to it.

Y

The uint64_t argument is interpreted to be the number of nanoseconds, since 00:00 Universal Coordinated Time, January 1, 1970, and is printed in the following format: "%Y %a %b %e %T %Z". The current number of nanoseconds since 00:00 UTC, January 1, 1970 is available as the walltimestamp variable.

%

Print a literal % character. No argument is converted. The entire conversion specification must be %%.

printa Action

The printa action enables you to format the results of aggregations in a D program. The function is invoked by using one of following two forms:

printa(@aggregation-name); 
printa(format-string, @aggregation-name);

If the first form of the function is used, the dtrace command takes a consistent snapshot of the aggregation data and produces output that is equivalent to the default output format used for aggregations. See Aggregations. If the second form of the function is used, the dtrace command takes a consistent snapshot of the aggregation data and produces output according to the conversions that are specified in the format string, according to the following rules:

  • The format conversions must match the tuple signature that is used to create the aggregation. Each tuple element can only appear once. For example, if you aggregate a count by using the following D statements:

    @a["hello", 123] = count(); 
    @a["goodbye", 456] = count();

    Then, you add the D statement printa(format-string, @a) to a probe clause, dtrace takes a snapshot of the aggregation data and produces output as though you entered these statements:

    printf(format-string, "hello", 123); 
    printf(format-string, "goodbye", 456);

    Then, continue similarly on for each tuple defined in the aggregation.

  • Unlike printf, the format string that you use for printa does not need to include all elements of the tuple: you can have a tuple of length 3 and only one format conversion. Therefore, you can omit any tuple keys from your printa output by changing your aggregation declaration to move the keys you want to omit to the end of the tuple and then omit any corresponding conversion specifiers for them in the printa format string.

  • The aggregation result is included in the output by using the additional @ format flag character, which is only valid when used with printa. The @ flag can be combined with any appropriate format conversion specifier. Also, the flag can appear more than once in a format string so that your tuple result can appear anywhere in the output, as well as appear more than once. The set of conversion specifiers that can be used with each aggregating function are implied by the aggregating function's result type. The aggregation result types are listed in the following table.

Aggregation Result Type

avg

uint64_t

count

uint64_t

llquantize

int64_t

lquantize

int64_t

max

uint64_t

min

uint64_t

quantize

int64_t

sum

uint64_t

For example, to format the results of avg, you can apply the %d, %i, %o, %u, or %x format conversions. The quantize, lquantize, and llquantize functions format their results as an ASCII table rather than as a single value.

The following D program shows an example of printa using the profile provider to sample the value of caller, then formatting the results as a simple table. Type the following source code and save it in a file named printa.d:

profile:::tick-1000
{
  @myagg[caller] = count();
}

END
{
  printa("%@8u %a\n", @myagg);
}

If you use the dtrace command to execute this program, wait a few seconds, then press Ctrl-C. You should see output similar to the following:

# dtrace -qs printa.d
                  ^C
       1 vmlinux`do_syscall_64+0x2f
       1 vmlinux`___bpf_prog_run+0x528
       1 vmlinux`page_frag_free+0x3e
       1 vmlinux`__legitimize_mnt
       1 vmlinux`seq_printf+0x1b
       1 vmlinux`selinux_sb_show_options+0x39
       1 vmlinux`strchr+0x1f
       1 ip6_tables`ip6t_do_table+0xbb
       2 vmlinux`__raw_callee_save___pv_queued_spin_unlock+0x10
      14 libata`__dta_ata_sff_pio_task_1036+0x9e
   12975 vmlinux`native_safe_halt+0x6

trace Default Format

If you use trace rather than printf to capture data, the dtrace command formats the results by using a default output format. If the data is 1, 2, 4, or 8 bytes in size, the result is formatted as a decimal integer value. If the data is any other size, and is a sequence of printable characters if interpreted as a sequence of bytes, it is printed as an ASCII string. If the data is any other size, and is not a sequence of printable characters, it is printed as a series of byte values that is formatted as hexadecimal integers.