1 // SPDX-License-Identifier: BSD-3-Clause
2 /* Copyright 2014-2020, Intel Corporation */
5 * output.c -- definitions of output printing related functions
24 #define STR(s) _STR(s)
25 #define TIME_STR_FMT "%a %b %d %Y %H:%M:%S"
26 #define UUID_STR_MAX 37
27 #define HEXDUMP_ROW_WIDTH 16
29 * 2 chars + space per byte +
30 * space after 8 bytes and terminating NULL
32 #define HEXDUMP_ROW_HEX_LEN (HEXDUMP_ROW_WIDTH * 3 + 1 + 1)
33 /* 1 printable char per byte + terminating NULL */
34 #define HEXDUMP_ROW_ASCII_LEN (HEXDUMP_ROW_WIDTH + 1)
35 #define SEPARATOR_CHAR '-'
37 #define INDENT_CHAR ' '
39 static char out_indent_str
[MAX_INDENT
+ 1];
40 static int out_indent_level
;
41 static int out_vlevel
;
42 static unsigned out_column_width
= 20;
44 static const char *out_prefix
;
49 * outv_check -- verify verbosity level
52 outv_check(int vlevel
)
54 return vlevel
&& (out_vlevel
>= vlevel
);
58 * out_set_col_width -- set column width
60 * See: outv_field() function
63 out_set_col_width(unsigned col_width
)
65 out_column_width
= col_width
;
69 * out_set_vlevel -- set verbosity level
72 out_set_vlevel(int vlevel
)
80 * out_set_prefix -- set prefix to output format
83 out_set_prefix(const char *prefix
)
89 * out_set_stream -- set output stream
92 out_set_stream(FILE *stream
)
96 memset(out_indent_str
, INDENT_CHAR
, MAX_INDENT
);
100 * outv_err -- print error message
103 outv_err(const char *fmt
, ...)
107 outv_err_vargs(fmt
, ap
);
112 * outv_err_vargs -- print error message
115 outv_err_vargs(const char *fmt
, va_list ap
)
117 char *_str
= strdup(fmt
);
122 fprintf(stderr
, "error: ");
123 int errstr
= str
[0] == '!';
127 char *nl
= strchr(str
, '\n');
131 vfprintf(stderr
, str
, ap
);
133 fprintf(stderr
, ": %s", strerror(errno
));
134 fprintf(stderr
, "\n");
140 * outv_indent -- change indentation level by factor
143 outv_indent(int vlevel
, int i
)
145 if (!outv_check(vlevel
))
148 out_indent_str
[out_indent_level
] = INDENT_CHAR
;
149 out_indent_level
+= i
;
150 if (out_indent_level
< 0)
151 out_indent_level
= 0;
152 if (out_indent_level
> MAX_INDENT
)
153 out_indent_level
= MAX_INDENT
;
155 out_indent_str
[out_indent_level
] = '\0';
159 * _out_prefix -- print prefix if defined
165 fprintf(out_fh
, "%s: ", out_prefix
);
169 * _out_indent -- print indent
174 fprintf(out_fh
, "%s", out_indent_str
);
178 * outv -- print message taking into account verbosity level
181 outv(int vlevel
, const char *fmt
, ...)
185 if (!outv_check(vlevel
))
191 vfprintf(out_fh
, fmt
, ap
);
196 * outv_nl -- print new line without indentation
201 if (!outv_check(vlevel
))
205 fprintf(out_fh
, "\n");
209 outv_title(int vlevel
, const char *fmt
, ...)
212 if (!outv_check(vlevel
))
215 fprintf(out_fh
, "\n");
219 vfprintf(out_fh
, fmt
, ap
);
221 fprintf(out_fh
, ":\n");
225 * outv_field -- print field name and value in specified format
227 * Field name will have fixed width which can be changed by
228 * out_set_column_width() function.
229 * vlevel - verbosity level
231 * fmt - format form value
234 outv_field(int vlevel
, const char *field
, const char *fmt
, ...)
238 if (!outv_check(vlevel
))
244 fprintf(out_fh
, "%-*s : ", out_column_width
, field
);
245 vfprintf(out_fh
, fmt
, ap
);
246 fprintf(out_fh
, "\n");
251 * out_get_percentage -- return percentage string
254 out_get_percentage(double perc
)
256 static char str_buff
[STR_MAX
] = {0, };
259 if (perc
> 0.0 && perc
< 0.0001) {
260 ret
= util_snprintf(str_buff
, STR_MAX
, "%e %%", perc
);
265 if (perc
>= 100.0 || perc
< DBL_EPSILON
)
270 ret
= util_snprintf(str_buff
, STR_MAX
, "%.*f %%", decimal
,
280 * out_get_size_str -- return size string
282 * human - if 1 return size in human-readable format
283 * if 2 return size in bytes and human-readable format
284 * otherwise return size in bytes.
287 out_get_size_str(uint64_t size
, int human
)
289 static char str_buff
[STR_MAX
] = {0, };
291 'K', 'M', 'G', 'T', '\0'
293 const int nunits
= sizeof(units
) / sizeof(units
[0]);
297 ret
= util_snprintf(str_buff
, STR_MAX
, "%"PRIu64
, size
);
300 double dsize
= (double)size
;
301 uint64_t csize
= size
;
303 while (csize
>= 1024 && i
< nunits
) {
309 if (i
>= 0 && i
< nunits
)
311 ret
= util_snprintf(str_buff
, STR_MAX
,
312 "%.1f%c", dsize
, units
[i
]);
314 ret
= util_snprintf(str_buff
, STR_MAX
,
315 "%.1f%c [%" PRIu64
"]", dsize
,
318 ret
= util_snprintf(str_buff
, STR_MAX
, "%"PRIu64
,
329 * out_get_uuid_str -- returns uuid in human readable format
332 out_get_uuid_str(uuid_t uuid
)
334 static char uuid_str
[UUID_STR_MAX
] = {0, };
336 int ret
= util_uuid_to_string(uuid
, uuid_str
);
338 outv(2, "failed to covert uuid to string");
345 * out_get_time_str -- returns time in human readable format
348 out_get_time_str(time_t time
)
350 static char str_buff
[STR_MAX
] = {0, };
351 struct tm
*tm
= util_localtime(&time
);
354 strftime(str_buff
, STR_MAX
, TIME_STR_FMT
, tm
);
356 int ret
= util_snprintf(str_buff
, STR_MAX
, "unknown");
365 * out_get_ascii_str -- get string with printable ASCII dump buffer
367 * Convert non-printable ASCII characters to dot '.'
368 * See: util_get_printable_ascii() function.
371 out_get_ascii_str(char *str
, size_t str_len
, const uint8_t *datap
, size_t len
)
380 for (i
= 0; i
< len
; i
++) {
381 pch
= util_get_printable_ascii((char)datap
[i
]);
382 int t
= util_snprintf(str
+ c
, str_len
- (size_t)c
, "%c", pch
);
392 * out_get_hex_str -- get string with hexadecimal dump of buffer
394 * Hexadecimal bytes in format %02x, each one followed by space,
395 * additional space after every 8th byte.
398 out_get_hex_str(char *str
, size_t str_len
, const uint8_t *datap
, size_t len
)
404 if (str_len
< (3 * len
+ 1))
407 for (i
= 0; i
< len
; i
++) {
408 /* add space after n*8 byte */
409 if (i
&& (i
% 8) == 0) {
410 t
= util_snprintf(str
+ c
, str_len
- (size_t)c
, " ");
415 t
= util_snprintf(str
+ c
, str_len
- (size_t)c
, "%02x ",
426 * outv_hexdump -- print buffer in canonical hex+ASCII format
428 * Print offset in hexadecimal,
429 * sixteen space-separated, two column, hexadecimal bytes,
430 * followed by the same sixteen bytes converted to printable ASCII characters
431 * enclosed in '|' characters.
434 outv_hexdump(int vlevel
, const void *addr
, size_t len
, size_t offset
, int sep
)
436 if (!outv_check(vlevel
) || len
<= 0)
439 const uint8_t *datap
= (uint8_t *)addr
;
440 uint8_t row_hex_str
[HEXDUMP_ROW_HEX_LEN
] = {0, };
441 uint8_t row_ascii_str
[HEXDUMP_ROW_ASCII_LEN
] = {0, };
448 size_t curr_len
= min(len
, HEXDUMP_ROW_WIDTH
);
451 * Check if current row is the same as the previous one
452 * don't check it for first and last rows.
454 if (len
!= curr_len
&& curr
&&
455 !memcmp(datap
+ prev
, datap
+ curr
, curr_len
)) {
457 /* print star only for the first repeated */
458 fprintf(out_fh
, "*\n");
464 /* row with hexadecimal bytes */
465 int rh
= out_get_hex_str((char *)row_hex_str
,
466 HEXDUMP_ROW_HEX_LEN
, datap
+ curr
, curr_len
);
467 /* row with printable ascii chars */
468 int ra
= out_get_ascii_str((char *)row_ascii_str
,
469 HEXDUMP_ROW_ASCII_LEN
, datap
+ curr
, curr_len
);
472 n
= fprintf(out_fh
, "%08zx %-*s|%-*s|\n",
474 HEXDUMP_ROW_HEX_LEN
, row_hex_str
,
475 HEXDUMP_ROW_WIDTH
, row_ascii_str
);
485 fprintf(out_fh
, "%c", SEPARATOR_CHAR
);
486 fprintf(out_fh
, "\n");
491 * out_get_checksum -- return checksum string with result
494 out_get_checksum(void *addr
, size_t len
, uint64_t *csump
, size_t skip_off
)
496 static char str_buff
[STR_MAX
] = {0, };
499 uint64_t csum
= util_checksum_compute(addr
, len
, csump
, skip_off
);
501 if (*csump
== htole64(csum
))
502 ret
= util_snprintf(str_buff
, STR_MAX
, "0x%" PRIx64
" [OK]",
505 ret
= util_snprintf(str_buff
, STR_MAX
,
506 "0x%" PRIx64
" [wrong! should be: 0x%" PRIx64
"]",
507 le64toh(*csump
), le64toh(csum
));
516 * out_get_btt_map_entry -- return BTT map entry with flags strings
519 out_get_btt_map_entry(uint32_t map
)
521 static char str_buff
[STR_MAX
] = {0, };
523 int is_init
= (map
& ~BTT_MAP_ENTRY_LBA_MASK
) == 0;
524 int is_zero
= (map
& ~BTT_MAP_ENTRY_LBA_MASK
) ==
526 int is_error
= (map
& ~BTT_MAP_ENTRY_LBA_MASK
) ==
528 int is_normal
= (map
& ~BTT_MAP_ENTRY_LBA_MASK
) ==
529 BTT_MAP_ENTRY_NORMAL
;
531 uint32_t lba
= map
& BTT_MAP_ENTRY_LBA_MASK
;
533 int ret
= util_snprintf(str_buff
, STR_MAX
, "0x%08x state: %s", lba
,
537 is_normal
? "normal" : "unknown");
546 * out_get_pool_type_str -- get pool type string
549 out_get_pool_type_str(pmem_pool_type_t type
)
552 case PMEM_POOL_TYPE_LOG
:
554 case PMEM_POOL_TYPE_BLK
:
556 case PMEM_POOL_TYPE_OBJ
:
558 case PMEM_POOL_TYPE_BTT
:
566 * out_get_pool_signature -- return signature of specified pool type
569 out_get_pool_signature(pmem_pool_type_t type
)
572 case PMEM_POOL_TYPE_LOG
:
574 case PMEM_POOL_TYPE_BLK
:
576 case PMEM_POOL_TYPE_OBJ
:
584 * out_get_chunk_type_str -- get chunk type string
587 out_get_chunk_type_str(enum chunk_type type
)
590 case CHUNK_TYPE_FOOTER
:
592 case CHUNK_TYPE_FREE
:
594 case CHUNK_TYPE_USED
:
598 case CHUNK_TYPE_UNKNOWN
:
605 * out_get_chunk_flags -- get names of set flags for chunk header
608 out_get_chunk_flags(uint16_t flags
)
610 if (flags
& CHUNK_FLAG_COMPACT_HEADER
)
611 return "compact header";
612 else if (flags
& CHUNK_FLAG_HEADER_NONE
)
613 return "header none";
619 * out_get_zone_magic_str -- get zone magic string with additional
620 * information about correctness of the magic value
623 out_get_zone_magic_str(uint32_t magic
)
625 static char str_buff
[STR_MAX
] = {0, };
627 const char *correct
= NULL
;
630 correct
= "uninitialized";
632 case ZONE_HEADER_MAGIC
:
636 correct
= "wrong! should be " STR(ZONE_HEADER_MAGIC
);
640 int ret
= util_snprintf(str_buff
, STR_MAX
, "0x%08x [%s]", magic
,
650 * out_get_pmemoid_str -- get PMEMoid string
653 out_get_pmemoid_str(PMEMoid oid
, uint64_t uuid_lo
)
655 static char str_buff
[STR_MAX
] = {0, };
658 char *correct
= "OK";
659 if (oid
.pool_uuid_lo
&& oid
.pool_uuid_lo
!= uuid_lo
) {
660 ret
= util_snprintf(str_buff
, STR_MAX
,
661 "wrong! should be 0x%016"PRIx64
, uuid_lo
);
663 err(1, "snprintf: %d", ret
);
664 correct
= strdup(str_buff
);
666 err(1, "Cannot allocate memory for PMEMoid string\n");
670 ret
= util_snprintf(str_buff
, STR_MAX
,
671 "off: 0x%016"PRIx64
" pool_uuid_lo: 0x%016"
672 PRIx64
" [%s]", oid
.off
, oid
.pool_uuid_lo
, correct
);
678 err(1, "snprintf: %d", ret
);
684 * out_get_arch_machine_class_str -- get a string representation of the machine
688 out_get_arch_machine_class_str(uint8_t machine_class
)
691 switch (machine_class
) {
692 case PMDK_MACHINE_CLASS_64
:
700 * out_get_arch_data_str -- get a string representation of the data endianness
703 out_get_arch_data_str(uint8_t data
)
707 return "2's complement, little endian";
709 return "2's complement, big endian";
716 * out_get_arch_machine_str -- get a string representation of the machine type
719 out_get_arch_machine_str(uint16_t machine
)
721 static char str_buff
[STR_MAX
] = {0, };
723 case PMDK_MACHINE_X86_64
:
725 case PMDK_MACHINE_AARCH64
:
727 case PMDK_MACHINE_PPC64
:
733 int ret
= util_snprintf(str_buff
, STR_MAX
, "unknown %u", machine
);
740 * out_get_last_shutdown_str -- get a string representation of the finish state
743 out_get_last_shutdown_str(uint8_t dirty
)
752 * out_get_alignment_descr_str -- get alignment descriptor string
755 out_get_alignment_desc_str(uint64_t ad
, uint64_t valid_ad
)
757 static char str_buff
[STR_MAX
] = {0, };
761 ret
= util_snprintf(str_buff
, STR_MAX
, "0x%016"PRIx64
"[OK]",
764 ret
= util_snprintf(str_buff
, STR_MAX
, "0x%016"PRIx64
" "
765 "[wrong! should be 0x%016"PRIx64
"]", ad
, valid_ad
);
774 * out_concat -- concatenate the new element to the list of strings
776 * If concatenation is successful it increments current position in the output
777 * string and number of elements in the list. Elements are separated with ", ".
780 out_concat(char *str_buff
, int *curr
, int *count
, const char *str
)
782 ASSERTne(str_buff
, NULL
);
783 ASSERTne(curr
, NULL
);
786 const char *separator
= (count
!= NULL
&& *count
> 0) ? ", " : "";
787 int ret
= util_snprintf(str_buff
+ *curr
,
788 (size_t)(STR_MAX
- *curr
), "%s%s", separator
, str
);
798 * out_get_incompat_features_str -- (internal) get a string with names of
799 * incompatibility flags
802 out_get_incompat_features_str(uint32_t incompat
)
804 static char str_buff
[STR_MAX
] = {0};
805 features_t features
= {POOL_FEAT_ZERO
, incompat
, POOL_FEAT_ZERO
};
809 /* print the value only */
812 /* print the value and the left square bracket */
813 ret
= util_snprintf(str_buff
, STR_MAX
, "0x%x [", incompat
);
815 ERR("snprintf for incompat features: %d", ret
);
819 /* print names of known options */
825 while (((feat
= util_feature2str(features
, &found
))) != NULL
) {
826 util_feature_disable(&features
, found
);
827 ret
= out_concat(str_buff
, &curr
, &count
, feat
);
832 /* check if any unknown flags are set */
833 if (!util_feature_is_zero(features
)) {
834 if (out_concat(str_buff
, &curr
, &count
,
839 /* print the right square bracket */
840 if (out_concat(str_buff
, &curr
, NULL
, "]"))