]> git.proxmox.com Git - ceph.git/blob - ceph/src/pmdk/src/tools/pmempool/output.c
import ceph 16.2.7
[ceph.git] / ceph / src / pmdk / src / tools / pmempool / output.c
1 // SPDX-License-Identifier: BSD-3-Clause
2 /* Copyright 2014-2020, Intel Corporation */
3
4 /*
5 * output.c -- definitions of output printing related functions
6 */
7
8 #include <assert.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <stdint.h>
14 #include <ctype.h>
15 #include <err.h>
16 #include <endian.h>
17 #include <inttypes.h>
18 #include <float.h>
19 #include "feature.h"
20 #include "common.h"
21 #include "output.h"
22
23 #define _STR(s) #s
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
28 /*
29 * 2 chars + space per byte +
30 * space after 8 bytes and terminating NULL
31 */
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 '-'
36 #define MAX_INDENT 32
37 #define INDENT_CHAR ' '
38
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;
43 static FILE *out_fh;
44 static const char *out_prefix;
45
46 #define STR_MAX 256
47
48 /*
49 * outv_check -- verify verbosity level
50 */
51 int
52 outv_check(int vlevel)
53 {
54 return vlevel && (out_vlevel >= vlevel);
55 }
56
57 /*
58 * out_set_col_width -- set column width
59 *
60 * See: outv_field() function
61 */
62 void
63 out_set_col_width(unsigned col_width)
64 {
65 out_column_width = col_width;
66 }
67
68 /*
69 * out_set_vlevel -- set verbosity level
70 */
71 void
72 out_set_vlevel(int vlevel)
73 {
74 out_vlevel = vlevel;
75 if (out_fh == NULL)
76 out_fh = stdout;
77 }
78
79 /*
80 * out_set_prefix -- set prefix to output format
81 */
82 void
83 out_set_prefix(const char *prefix)
84 {
85 out_prefix = prefix;
86 }
87
88 /*
89 * out_set_stream -- set output stream
90 */
91 void
92 out_set_stream(FILE *stream)
93 {
94 out_fh = stream;
95
96 memset(out_indent_str, INDENT_CHAR, MAX_INDENT);
97 }
98
99 /*
100 * outv_err -- print error message
101 */
102 void
103 outv_err(const char *fmt, ...)
104 {
105 va_list ap;
106 va_start(ap, fmt);
107 outv_err_vargs(fmt, ap);
108 va_end(ap);
109 }
110
111 /*
112 * outv_err_vargs -- print error message
113 */
114 void
115 outv_err_vargs(const char *fmt, va_list ap)
116 {
117 char *_str = strdup(fmt);
118 if (!_str)
119 err(1, "strdup");
120 char *str = _str;
121
122 fprintf(stderr, "error: ");
123 int errstr = str[0] == '!';
124 if (errstr)
125 str++;
126
127 char *nl = strchr(str, '\n');
128 if (nl)
129 *nl = '\0';
130
131 vfprintf(stderr, str, ap);
132 if (errstr)
133 fprintf(stderr, ": %s", strerror(errno));
134 fprintf(stderr, "\n");
135
136 free(_str);
137 }
138
139 /*
140 * outv_indent -- change indentation level by factor
141 */
142 void
143 outv_indent(int vlevel, int i)
144 {
145 if (!outv_check(vlevel))
146 return;
147
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;
154
155 out_indent_str[out_indent_level] = '\0';
156 }
157
158 /*
159 * _out_prefix -- print prefix if defined
160 */
161 static void
162 _out_prefix(void)
163 {
164 if (out_prefix)
165 fprintf(out_fh, "%s: ", out_prefix);
166 }
167
168 /*
169 * _out_indent -- print indent
170 */
171 static void
172 _out_indent(void)
173 {
174 fprintf(out_fh, "%s", out_indent_str);
175 }
176
177 /*
178 * outv -- print message taking into account verbosity level
179 */
180 void
181 outv(int vlevel, const char *fmt, ...)
182 {
183 va_list ap;
184
185 if (!outv_check(vlevel))
186 return;
187
188 _out_prefix();
189 _out_indent();
190 va_start(ap, fmt);
191 vfprintf(out_fh, fmt, ap);
192 va_end(ap);
193 }
194
195 /*
196 * outv_nl -- print new line without indentation
197 */
198 void
199 outv_nl(int vlevel)
200 {
201 if (!outv_check(vlevel))
202 return;
203
204 _out_prefix();
205 fprintf(out_fh, "\n");
206 }
207
208 void
209 outv_title(int vlevel, const char *fmt, ...)
210 {
211 va_list ap;
212 if (!outv_check(vlevel))
213 return;
214
215 fprintf(out_fh, "\n");
216 _out_prefix();
217 _out_indent();
218 va_start(ap, fmt);
219 vfprintf(out_fh, fmt, ap);
220 va_end(ap);
221 fprintf(out_fh, ":\n");
222 }
223
224 /*
225 * outv_field -- print field name and value in specified format
226 *
227 * Field name will have fixed width which can be changed by
228 * out_set_column_width() function.
229 * vlevel - verbosity level
230 * field - field name
231 * fmt - format form value
232 */
233 void
234 outv_field(int vlevel, const char *field, const char *fmt, ...)
235 {
236 va_list ap;
237
238 if (!outv_check(vlevel))
239 return;
240
241 _out_prefix();
242 _out_indent();
243 va_start(ap, fmt);
244 fprintf(out_fh, "%-*s : ", out_column_width, field);
245 vfprintf(out_fh, fmt, ap);
246 fprintf(out_fh, "\n");
247 va_end(ap);
248 }
249
250 /*
251 * out_get_percentage -- return percentage string
252 */
253 const char *
254 out_get_percentage(double perc)
255 {
256 static char str_buff[STR_MAX] = {0, };
257 int ret = 0;
258
259 if (perc > 0.0 && perc < 0.0001) {
260 ret = util_snprintf(str_buff, STR_MAX, "%e %%", perc);
261 if (ret < 0)
262 return "";
263 } else {
264 int decimal = 0;
265 if (perc >= 100.0 || perc < DBL_EPSILON)
266 decimal = 0;
267 else
268 decimal = 6;
269
270 ret = util_snprintf(str_buff, STR_MAX, "%.*f %%", decimal,
271 perc);
272 if (ret < 0)
273 return "";
274 }
275
276 return str_buff;
277 }
278
279 /*
280 * out_get_size_str -- return size string
281 *
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.
285 */
286 const char *
287 out_get_size_str(uint64_t size, int human)
288 {
289 static char str_buff[STR_MAX] = {0, };
290 char units[] = {
291 'K', 'M', 'G', 'T', '\0'
292 };
293 const int nunits = sizeof(units) / sizeof(units[0]);
294 int ret = 0;
295
296 if (!human) {
297 ret = util_snprintf(str_buff, STR_MAX, "%"PRIu64, size);
298 } else {
299 int i = -1;
300 double dsize = (double)size;
301 uint64_t csize = size;
302
303 while (csize >= 1024 && i < nunits) {
304 csize /= 1024;
305 dsize /= 1024.0;
306 i++;
307 }
308
309 if (i >= 0 && i < nunits)
310 if (human == 1)
311 ret = util_snprintf(str_buff, STR_MAX,
312 "%.1f%c", dsize, units[i]);
313 else
314 ret = util_snprintf(str_buff, STR_MAX,
315 "%.1f%c [%" PRIu64"]", dsize,
316 units[i], size);
317 else
318 ret = util_snprintf(str_buff, STR_MAX, "%"PRIu64,
319 size);
320 }
321
322 if (ret < 0)
323 return "";
324
325 return str_buff;
326 }
327
328 /*
329 * out_get_uuid_str -- returns uuid in human readable format
330 */
331 const char *
332 out_get_uuid_str(uuid_t uuid)
333 {
334 static char uuid_str[UUID_STR_MAX] = {0, };
335
336 int ret = util_uuid_to_string(uuid, uuid_str);
337 if (ret != 0) {
338 outv(2, "failed to covert uuid to string");
339 return NULL;
340 }
341 return uuid_str;
342 }
343
344 /*
345 * out_get_time_str -- returns time in human readable format
346 */
347 const char *
348 out_get_time_str(time_t time)
349 {
350 static char str_buff[STR_MAX] = {0, };
351 struct tm *tm = util_localtime(&time);
352
353 if (tm) {
354 strftime(str_buff, STR_MAX, TIME_STR_FMT, tm);
355 } else {
356 int ret = util_snprintf(str_buff, STR_MAX, "unknown");
357 if (ret < 0)
358 return "";
359 }
360
361 return str_buff;
362 }
363
364 /*
365 * out_get_ascii_str -- get string with printable ASCII dump buffer
366 *
367 * Convert non-printable ASCII characters to dot '.'
368 * See: util_get_printable_ascii() function.
369 */
370 static int
371 out_get_ascii_str(char *str, size_t str_len, const uint8_t *datap, size_t len)
372 {
373 int c = 0;
374 size_t i;
375 char pch;
376
377 if (str_len < len)
378 return -1;
379
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);
383 if (t < 0)
384 return -1;
385 c += t;
386 }
387
388 return c;
389 }
390
391 /*
392 * out_get_hex_str -- get string with hexadecimal dump of buffer
393 *
394 * Hexadecimal bytes in format %02x, each one followed by space,
395 * additional space after every 8th byte.
396 */
397 static int
398 out_get_hex_str(char *str, size_t str_len, const uint8_t *datap, size_t len)
399 {
400 int c = 0;
401 size_t i;
402 int t;
403
404 if (str_len < (3 * len + 1))
405 return -1;
406
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, " ");
411 if (t < 0)
412 return -1;
413 c += t;
414 }
415 t = util_snprintf(str + c, str_len - (size_t)c, "%02x ",
416 datap[i]);
417 if (t < 0)
418 return -1;
419 c += t;
420 }
421
422 return c;
423 }
424
425 /*
426 * outv_hexdump -- print buffer in canonical hex+ASCII format
427 *
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.
432 */
433 void
434 outv_hexdump(int vlevel, const void *addr, size_t len, size_t offset, int sep)
435 {
436 if (!outv_check(vlevel) || len <= 0)
437 return;
438
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, };
442 size_t curr = 0;
443 size_t prev = 0;
444 int repeated = 0;
445 int n = 0;
446
447 while (len) {
448 size_t curr_len = min(len, HEXDUMP_ROW_WIDTH);
449
450 /*
451 * Check if current row is the same as the previous one
452 * don't check it for first and last rows.
453 */
454 if (len != curr_len && curr &&
455 !memcmp(datap + prev, datap + curr, curr_len)) {
456 if (!repeated) {
457 /* print star only for the first repeated */
458 fprintf(out_fh, "*\n");
459 repeated = 1;
460 }
461 } else {
462 repeated = 0;
463
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);
470
471 if (ra && rh)
472 n = fprintf(out_fh, "%08zx %-*s|%-*s|\n",
473 curr + offset,
474 HEXDUMP_ROW_HEX_LEN, row_hex_str,
475 HEXDUMP_ROW_WIDTH, row_ascii_str);
476 prev = curr;
477 }
478
479 len -= curr_len;
480 curr += curr_len;
481 }
482
483 if (sep && n) {
484 while (--n)
485 fprintf(out_fh, "%c", SEPARATOR_CHAR);
486 fprintf(out_fh, "\n");
487 }
488 }
489
490 /*
491 * out_get_checksum -- return checksum string with result
492 */
493 const char *
494 out_get_checksum(void *addr, size_t len, uint64_t *csump, size_t skip_off)
495 {
496 static char str_buff[STR_MAX] = {0, };
497 int ret = 0;
498
499 uint64_t csum = util_checksum_compute(addr, len, csump, skip_off);
500
501 if (*csump == htole64(csum))
502 ret = util_snprintf(str_buff, STR_MAX, "0x%" PRIx64" [OK]",
503 le64toh(csum));
504 else
505 ret = util_snprintf(str_buff, STR_MAX,
506 "0x%" PRIx64 " [wrong! should be: 0x%" PRIx64 "]",
507 le64toh(*csump), le64toh(csum));
508
509 if (ret < 0)
510 return "";
511
512 return str_buff;
513 }
514
515 /*
516 * out_get_btt_map_entry -- return BTT map entry with flags strings
517 */
518 const char *
519 out_get_btt_map_entry(uint32_t map)
520 {
521 static char str_buff[STR_MAX] = {0, };
522
523 int is_init = (map & ~BTT_MAP_ENTRY_LBA_MASK) == 0;
524 int is_zero = (map & ~BTT_MAP_ENTRY_LBA_MASK) ==
525 BTT_MAP_ENTRY_ZERO;
526 int is_error = (map & ~BTT_MAP_ENTRY_LBA_MASK) ==
527 BTT_MAP_ENTRY_ERROR;
528 int is_normal = (map & ~BTT_MAP_ENTRY_LBA_MASK) ==
529 BTT_MAP_ENTRY_NORMAL;
530
531 uint32_t lba = map & BTT_MAP_ENTRY_LBA_MASK;
532
533 int ret = util_snprintf(str_buff, STR_MAX, "0x%08x state: %s", lba,
534 is_init ? "init" :
535 is_zero ? "zero" :
536 is_error ? "error" :
537 is_normal ? "normal" : "unknown");
538
539 if (ret < 0)
540 return "";
541
542 return str_buff;
543 }
544
545 /*
546 * out_get_pool_type_str -- get pool type string
547 */
548 const char *
549 out_get_pool_type_str(pmem_pool_type_t type)
550 {
551 switch (type) {
552 case PMEM_POOL_TYPE_LOG:
553 return "log";
554 case PMEM_POOL_TYPE_BLK:
555 return "blk";
556 case PMEM_POOL_TYPE_OBJ:
557 return "obj";
558 case PMEM_POOL_TYPE_BTT:
559 return "btt";
560 default:
561 return "unknown";
562 }
563 }
564
565 /*
566 * out_get_pool_signature -- return signature of specified pool type
567 */
568 const char *
569 out_get_pool_signature(pmem_pool_type_t type)
570 {
571 switch (type) {
572 case PMEM_POOL_TYPE_LOG:
573 return LOG_HDR_SIG;
574 case PMEM_POOL_TYPE_BLK:
575 return BLK_HDR_SIG;
576 case PMEM_POOL_TYPE_OBJ:
577 return OBJ_HDR_SIG;
578 default:
579 return NULL;
580 }
581 }
582
583 /*
584 * out_get_chunk_type_str -- get chunk type string
585 */
586 const char *
587 out_get_chunk_type_str(enum chunk_type type)
588 {
589 switch (type) {
590 case CHUNK_TYPE_FOOTER:
591 return "footer";
592 case CHUNK_TYPE_FREE:
593 return "free";
594 case CHUNK_TYPE_USED:
595 return "used";
596 case CHUNK_TYPE_RUN:
597 return "run";
598 case CHUNK_TYPE_UNKNOWN:
599 default:
600 return "unknown";
601 }
602 }
603
604 /*
605 * out_get_chunk_flags -- get names of set flags for chunk header
606 */
607 const char *
608 out_get_chunk_flags(uint16_t flags)
609 {
610 if (flags & CHUNK_FLAG_COMPACT_HEADER)
611 return "compact header";
612 else if (flags & CHUNK_FLAG_HEADER_NONE)
613 return "header none";
614
615 return "";
616 }
617
618 /*
619 * out_get_zone_magic_str -- get zone magic string with additional
620 * information about correctness of the magic value
621 */
622 const char *
623 out_get_zone_magic_str(uint32_t magic)
624 {
625 static char str_buff[STR_MAX] = {0, };
626
627 const char *correct = NULL;
628 switch (magic) {
629 case 0:
630 correct = "uninitialized";
631 break;
632 case ZONE_HEADER_MAGIC:
633 correct = "OK";
634 break;
635 default:
636 correct = "wrong! should be " STR(ZONE_HEADER_MAGIC);
637 break;
638 }
639
640 int ret = util_snprintf(str_buff, STR_MAX, "0x%08x [%s]", magic,
641 correct);
642
643 if (ret < 0)
644 return "";
645
646 return str_buff;
647 }
648
649 /*
650 * out_get_pmemoid_str -- get PMEMoid string
651 */
652 const char *
653 out_get_pmemoid_str(PMEMoid oid, uint64_t uuid_lo)
654 {
655 static char str_buff[STR_MAX] = {0, };
656 int free_cor = 0;
657 int ret = 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);
662 if (ret < 0)
663 err(1, "snprintf: %d", ret);
664 correct = strdup(str_buff);
665 if (!correct)
666 err(1, "Cannot allocate memory for PMEMoid string\n");
667 free_cor = 1;
668 }
669
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);
673
674 if (free_cor)
675 free(correct);
676
677 if (ret < 0)
678 err(1, "snprintf: %d", ret);
679
680 return str_buff;
681 }
682
683 /*
684 * out_get_arch_machine_class_str -- get a string representation of the machine
685 * class
686 */
687 const char *
688 out_get_arch_machine_class_str(uint8_t machine_class)
689 {
690
691 switch (machine_class) {
692 case PMDK_MACHINE_CLASS_64:
693 return "64";
694 default:
695 return "unknown";
696 }
697 }
698
699 /*
700 * out_get_arch_data_str -- get a string representation of the data endianness
701 */
702 const char *
703 out_get_arch_data_str(uint8_t data)
704 {
705 switch (data) {
706 case PMDK_DATA_LE:
707 return "2's complement, little endian";
708 case PMDK_DATA_BE:
709 return "2's complement, big endian";
710 default:
711 return "unknown";
712 }
713 }
714
715 /*
716 * out_get_arch_machine_str -- get a string representation of the machine type
717 */
718 const char *
719 out_get_arch_machine_str(uint16_t machine)
720 {
721 static char str_buff[STR_MAX] = {0, };
722 switch (machine) {
723 case PMDK_MACHINE_X86_64:
724 return "AMD X86-64";
725 case PMDK_MACHINE_AARCH64:
726 return "Aarch64";
727 case PMDK_MACHINE_PPC64:
728 return "PPC64";
729 default:
730 break;
731 }
732
733 int ret = util_snprintf(str_buff, STR_MAX, "unknown %u", machine);
734 if (ret < 0)
735 return "unknown";
736 return str_buff;
737 }
738
739 /*
740 * out_get_last_shutdown_str -- get a string representation of the finish state
741 */
742 const char *
743 out_get_last_shutdown_str(uint8_t dirty)
744 {
745 if (dirty)
746 return "dirty";
747 else
748 return "clean";
749 }
750
751 /*
752 * out_get_alignment_descr_str -- get alignment descriptor string
753 */
754 const char *
755 out_get_alignment_desc_str(uint64_t ad, uint64_t valid_ad)
756 {
757 static char str_buff[STR_MAX] = {0, };
758 int ret = 0;
759
760 if (ad == valid_ad)
761 ret = util_snprintf(str_buff, STR_MAX, "0x%016"PRIx64"[OK]",
762 ad);
763 else
764 ret = util_snprintf(str_buff, STR_MAX, "0x%016"PRIx64" "
765 "[wrong! should be 0x%016"PRIx64"]", ad, valid_ad);
766
767 if (ret < 0)
768 return "";
769
770 return str_buff;
771 }
772
773 /*
774 * out_concat -- concatenate the new element to the list of strings
775 *
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 ", ".
778 */
779 static int
780 out_concat(char *str_buff, int *curr, int *count, const char *str)
781 {
782 ASSERTne(str_buff, NULL);
783 ASSERTne(curr, NULL);
784 ASSERTne(str, NULL);
785
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);
789 if (ret < 0)
790 return -1;
791 *curr += ret;
792 if (count)
793 ++(*count);
794 return 0;
795 }
796
797 /*
798 * out_get_incompat_features_str -- (internal) get a string with names of
799 * incompatibility flags
800 */
801 const char *
802 out_get_incompat_features_str(uint32_t incompat)
803 {
804 static char str_buff[STR_MAX] = {0};
805 features_t features = {POOL_FEAT_ZERO, incompat, POOL_FEAT_ZERO};
806 int ret = 0;
807
808 if (incompat == 0) {
809 /* print the value only */
810 return "0x0";
811 } else {
812 /* print the value and the left square bracket */
813 ret = util_snprintf(str_buff, STR_MAX, "0x%x [", incompat);
814 if (ret < 0) {
815 ERR("snprintf for incompat features: %d", ret);
816 return "<error>";
817 }
818
819 /* print names of known options */
820 int count = 0;
821 int curr = ret;
822 features_t found;
823 const char *feat;
824
825 while (((feat = util_feature2str(features, &found))) != NULL) {
826 util_feature_disable(&features, found);
827 ret = out_concat(str_buff, &curr, &count, feat);
828 if (ret < 0)
829 return "";
830 }
831
832 /* check if any unknown flags are set */
833 if (!util_feature_is_zero(features)) {
834 if (out_concat(str_buff, &curr, &count,
835 "?UNKNOWN_FLAG?"))
836 return "";
837 }
838
839 /* print the right square bracket */
840 if (out_concat(str_buff, &curr, NULL, "]"))
841 return "";
842 }
843 return str_buff;
844 }