#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <assert.h>
#include <sys/param.h>
#ifndef __FreeBSD__
#define __USE_UNIX98
#endif
#include <unistd.h>
#include <sys/mman.h>
#include "common.h"
#include "output.h"
#include "out.h"
#include "info.h"
#include "set.h"
#include "file.h"
#define DEFAULT_CHUNK_TYPES\
((1<<CHUNK_TYPE_FREE)|\
(1<<CHUNK_TYPE_USED)|\
(1<<CHUNK_TYPE_RUN))
#define DEFAULT_LANE_SECTIONS\
((1<<LANE_SECTION_ALLOCATOR)|\
(1<<LANE_SECTION_TRANSACTION)|\
(1<<LANE_SECTION_LIST))
#define GET_ALIGNMENT(ad, x)\
(1 + (((ad) >> (ALIGNMENT_DESC_BITS * (x))) & ((1 << ALIGNMENT_DESC_BITS) - 1)))
#define UNDEF_REPLICA UINT_MAX
#define UNDEF_PART UINT_MAX
static const struct pmempool_info_args pmempool_info_args_default = {
.col_width = 24,
.human = false,
.force = false,
.type = PMEM_POOL_TYPE_UNKNOWN,
.vlevel = VERBOSE_DEFAULT,
.vdata = VERBOSE_SILENT,
.vhdrdump = VERBOSE_SILENT,
.vstats = VERBOSE_SILENT,
.log = {
.walk = 0,
},
.blk = {
.vmap = VERBOSE_SILENT,
.vflog = VERBOSE_SILENT,
.vbackup = VERBOSE_SILENT,
.skip_zeros = false,
.skip_error = false,
.skip_no_flag = false,
},
.obj = {
.vlanes = VERBOSE_SILENT,
.vroot = VERBOSE_SILENT,
.vobjects = VERBOSE_SILENT,
.valloc = VERBOSE_SILENT,
.voobhdr = VERBOSE_SILENT,
.vheap = VERBOSE_SILENT,
.vzonehdr = VERBOSE_SILENT,
.vchunkhdr = VERBOSE_SILENT,
.vbitmap = VERBOSE_SILENT,
.lane_sections = DEFAULT_LANE_SECTIONS,
.lanes_recovery = false,
.ignore_empty_obj = false,
.chunk_types = DEFAULT_CHUNK_TYPES,
.replica = 0,
},
};
static const struct option long_options[] = {
{"version", no_argument, NULL, 'V' | OPT_ALL},
{"verbose", no_argument, NULL, 'v' | OPT_ALL},
{"help", no_argument, NULL, 'h' | OPT_ALL},
{"human", no_argument, NULL, 'n' | OPT_ALL},
{"force", required_argument, NULL, 'f' | OPT_ALL},
{"data", no_argument, NULL, 'd' | OPT_ALL},
{"headers-hex", no_argument, NULL, 'x' | OPT_ALL},
{"stats", no_argument, NULL, 's' | OPT_ALL},
{"range", required_argument, NULL, 'r' | OPT_ALL},
{"walk", required_argument, NULL, 'w' | OPT_LOG},
{"skip-zeros", no_argument, NULL, 'z' | OPT_BLK | OPT_BTT},
{"skip-error", no_argument, NULL, 'e' | OPT_BLK | OPT_BTT},
{"skip-no-flag", no_argument, NULL, 'u' | OPT_BLK | OPT_BTT},
{"map", no_argument, NULL, 'm' | OPT_BLK | OPT_BTT},
{"flog", no_argument, NULL, 'g' | OPT_BLK | OPT_BTT},
{"backup", no_argument, NULL, 'B' | OPT_BLK | OPT_BTT},
{"lanes", no_argument, NULL, 'l' | OPT_OBJ},
{"recovery", no_argument, NULL, 'R' | OPT_OBJ},
{"section", required_argument, NULL, 'S' | OPT_OBJ},
{"object-store", no_argument, NULL, 'O' | OPT_OBJ},
{"types", required_argument, NULL, 't' | OPT_OBJ},
{"no-empty", no_argument, NULL, 'E' | OPT_OBJ},
{"alloc-header", no_argument, NULL, 'A' | OPT_OBJ},
{"oob-header", no_argument, NULL, 'a' | OPT_OBJ},
{"root", no_argument, NULL, 'o' | OPT_OBJ},
{"heap", no_argument, NULL, 'H' | OPT_OBJ},
{"zones", no_argument, NULL, 'Z' | OPT_OBJ},
{"chunks", no_argument, NULL, 'C' | OPT_OBJ},
{"chunk-type", required_argument, NULL, 'T' | OPT_OBJ},
{"bitmap", no_argument, NULL, 'b' | OPT_OBJ},
{"replica", required_argument, NULL, 'p' | OPT_OBJ},
{NULL, 0, NULL, 0 },
};
static const struct option_requirement option_requirements[] = {
{
.opt = 'r',
.type = PMEM_POOL_TYPE_LOG,
.req = OPT_REQ0('d')
},
{
.opt = 'r',
.type = PMEM_POOL_TYPE_BLK | PMEM_POOL_TYPE_BTT,
.req = OPT_REQ0('d') | OPT_REQ1('m')
},
{
.opt = 'z',
.type = PMEM_POOL_TYPE_BLK | PMEM_POOL_TYPE_BTT,
.req = OPT_REQ0('d') | OPT_REQ1('m')
},
{
.opt = 'e',
.type = PMEM_POOL_TYPE_BLK | PMEM_POOL_TYPE_BTT,
.req = OPT_REQ0('d') | OPT_REQ1('m')
},
{
.opt = 'u',
.type = PMEM_POOL_TYPE_BLK | PMEM_POOL_TYPE_BTT,
.req = OPT_REQ0('d') | OPT_REQ1('m')
},
{
.opt = 'r',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O') | OPT_REQ1('Z') |
OPT_REQ2('C') | OPT_REQ3('l'),
},
{
.opt = 'R',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('l')
},
{
.opt = 'S',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('l')
},
{
.opt = 'E',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O')
},
{
.opt = 'T',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('C')
},
{
.opt = 'b',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('H')
},
{
.opt = 'b',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('C')
},
{
.opt = 'A',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O') | OPT_REQ1('l') | OPT_REQ2('o')
},
{
.opt = 'a',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O') | OPT_REQ1('l') | OPT_REQ2('o')
},
{
.opt = 't',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O') | OPT_REQ1('s'),
},
{
.opt = 'C',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O') | OPT_REQ1('H') | OPT_REQ2('s'),
},
{
.opt = 'Z',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O') | OPT_REQ1('H') | OPT_REQ2('s'),
},
{
.opt = 'd',
.type = PMEM_POOL_TYPE_OBJ,
.req = OPT_REQ0('O') | OPT_REQ1('o'),
},
{ 0, 0, 0}
};
static const char *help_str =
"Show information about pmem pool from specified file.\n"
"\n"
"Common options:\n"
" -h, --help Print this help and exit.\n"
" -V, --version Print version and exit.\n"
" -v, --verbose Increase verbisity level.\n"
" -f, --force blk|log|obj|btt Force parsing a pool of specified type.\n"
" -n, --human Print sizes in human readable format.\n"
" -x, --headers-hex Hexdump all headers.\n"
" -d, --data Dump log data and blocks.\n"
" -s, --stats Print statistics.\n"
" -r, --range <range> Range of blocks/chunks/objects.\n"
"\n"
"Options for PMEMLOG:\n"
" -w, --walk <size> Chunk size.\n"
"\n"
"Options for PMEMBLK:\n"
" -m, --map Print BTT Map entries.\n"
" -g, --flog Print BTT FLOG entries.\n"
" -B, --backup Print BTT Info header backup.\n"
" -z, --skip-zeros Skip blocks marked with zero flag.\n"
" -e, --skip-error Skip blocks marked with error flag.\n"
" -u, --skip-no-flag Skip blocks not marked with any flag.\n"
"\n"
"Options for PMEMOBJ:\n"
" -l, --lanes [<range>] Print lanes from specified range.\n"
" -R, --recovery Print only lanes which need recovery.\n"
" -S, --section tx,allocator,list Print only specified sections.\n"
" -O, --object-store Print object store.\n"
" -t, --types <range> Specify objects' type numbers range.\n"
" -E, --no-empty Print only non-empty object store lists.\n"
" -o, --root Print root object information\n"
" -A, --alloc-header Print allocation header for objects in\n"
" object store.\n"
" -a, --oob-header Print OOB header\n"
" -H, --heap Print heap header.\n"
" -Z, --zones [<range>] Print zones header. If range is specified\n"
" and --object|-O option is specified prints\n"
" objects from specified zones only.\n"
" -C, --chunks [<range>] Print zones header. If range is specified\n"
" and --object|-O option is specified prints\n"
" objects from specified zones only.\n"
" -T, --chunk-type used,free,run,footer\n"
" Print only specified type(s) of chunk.\n"
" [requires --chunks|-C]\n"
" -b, --bitmap Print chunk run's bitmap in graphical\n"
" format. [requires --chunks|-C]\n"
" -p, --replica <num> Print info from specified replica\n"
"For complete documentation see %s-info(1) manual page.\n"
;
static void
print_usage(char *appname)
{
printf("Usage: %s info [<args>] <file>\n", appname);
}
static void
print_version(char *appname)
{
printf("%s %s\n", appname, SRCVERSION);
}
void
pmempool_info_help(char *appname)
{
print_usage(appname);
print_version(appname);
printf(help_str, appname);
}
static int
parse_args(char *appname, int argc, char *argv[],
struct pmempool_info_args *argsp,
struct options *opts)
{
int opt;
if (argc == 1) {
print_usage(appname);
return -1;
}
struct ranges *rangesp = &argsp->ranges;
while ((opt = util_options_getopt(argc, argv,
"vhnf:ezuF:L:c:dmxVw:gBsr:lRS:OECZHT:bot:aAp:",
opts)) != -1) {
switch (opt) {
case 'v':
argsp->vlevel = VERBOSE_MAX;
break;
case 'V':
print_version(appname);
exit(EXIT_SUCCESS);
case 'h':
pmempool_info_help(appname);
exit(EXIT_SUCCESS);
case 'n':
argsp->human = true;
break;
case 'f':
argsp->type = pmem_pool_type_parse_str(optarg);
if (argsp->type == PMEM_POOL_TYPE_UNKNOWN) {
outv_err("'%s' -- unknown pool type\n", optarg);
return -1;
}
argsp->force = true;
break;
case 'e':
argsp->blk.skip_error = true;
break;
case 'z':
argsp->blk.skip_zeros = true;
break;
case 'u':
argsp->blk.skip_no_flag = true;
break;
case 'r':
if (util_parse_ranges(optarg, rangesp,
ENTIRE_UINT64)) {
outv_err("'%s' -- cannot parse range(s)\n",
optarg);
return -1;
}
if (rangesp == &argsp->ranges)
argsp->use_range = 1;
break;
case 'd':
argsp->vdata = VERBOSE_DEFAULT;
break;
case 'm':
argsp->blk.vmap = VERBOSE_DEFAULT;
break;
case 'g':
argsp->blk.vflog = VERBOSE_DEFAULT;
break;
case 'B':
argsp->blk.vbackup = VERBOSE_DEFAULT;
break;
case 'x':
argsp->vhdrdump = VERBOSE_DEFAULT;
break;
case 's':
argsp->vstats = VERBOSE_DEFAULT;
break;
case 'w':
argsp->log.walk = (size_t)atoll(optarg);
if (argsp->log.walk == 0) {
outv_err("'%s' -- invalid chunk size\n",
optarg);
return -1;
}
break;
case 'l':
argsp->obj.vlanes = VERBOSE_DEFAULT;
rangesp = &argsp->obj.lane_ranges;
break;
case 'R':
argsp->obj.lanes_recovery = true;
break;
case 'S':
argsp->obj.lane_sections = 0;
if (util_parse_lane_sections(optarg,
&argsp->obj.lane_sections)) {
outv_err("'%s' -- cannot parse"
" lane section(s)\n", optarg);
return -1;
}
break;
case 'O':
argsp->obj.vobjects = VERBOSE_DEFAULT;
rangesp = &argsp->ranges;
break;
case 'a':
argsp->obj.voobhdr = VERBOSE_DEFAULT;
break;
case 'A':
argsp->obj.valloc = VERBOSE_DEFAULT;
break;
case 'E':
argsp->obj.ignore_empty_obj = true;
break;
case 'Z':
argsp->obj.vzonehdr = VERBOSE_DEFAULT;
rangesp = &argsp->obj.zone_ranges;
break;
case 'C':
argsp->obj.vchunkhdr = VERBOSE_DEFAULT;
rangesp = &argsp->obj.chunk_ranges;
break;
case 'H':
argsp->obj.vheap = VERBOSE_DEFAULT;
break;
case 'T':
argsp->obj.chunk_types = 0;
if (util_parse_chunk_types(optarg,
&argsp->obj.chunk_types) ||
(argsp->obj.chunk_types &
(1 << CHUNK_TYPE_UNKNOWN))) {
outv_err("'%s' -- cannot parse chunk type(s)\n",
optarg);
return -1;
}
break;
case 'o':
argsp->obj.vroot = VERBOSE_DEFAULT;
break;
case 't':
if (util_parse_ranges(optarg,
&argsp->obj.type_ranges, ENTIRE_UINT64)) {
outv_err("'%s' -- cannot parse range(s)\n",
optarg);
return -1;
}
break;
case 'b':
argsp->obj.vbitmap = VERBOSE_DEFAULT;
break;
case 'p':
{
char *endptr;
long long ll = strtoll(optarg, &endptr, 10);
if ((endptr && *endptr != '\0') || errno) {
outv_err("'%s' -- invalid replica number",
optarg);
return -1;
}
argsp->obj.replica = (size_t)ll;
break;
}
default:
print_usage(appname);
return -1;
}
}
if (optind < argc) {
argsp->file = argv[optind];
} else {
print_usage(appname);
return -1;
}
if (!argsp->use_range)
util_ranges_add(&argsp->ranges, ENTIRE_UINT64);
if (util_ranges_empty(&argsp->obj.type_ranges))
util_ranges_add(&argsp->obj.type_ranges, ENTIRE_UINT64);
if (util_ranges_empty(&argsp->obj.lane_ranges))
util_ranges_add(&argsp->obj.lane_ranges, ENTIRE_UINT64);
if (util_ranges_empty(&argsp->obj.zone_ranges))
util_ranges_add(&argsp->obj.zone_ranges, ENTIRE_UINT64);
if (util_ranges_empty(&argsp->obj.chunk_ranges))
util_ranges_add(&argsp->obj.chunk_ranges, ENTIRE_UINT64);
return 0;
}
int
pmempool_info_read(struct pmem_info *pip, void *buff, size_t nbytes,
uint64_t off)
{
return pool_set_file_read(pip->pfile, buff, nbytes, off);
}
static int
pmempool_info_part(struct pmem_info *pip, unsigned repn, unsigned partn, int v)
{
const char *path = NULL;
if (repn != UNDEF_REPLICA && partn != UNDEF_PART) {
outv(v, "part %u:\n", partn);
struct pool_set_part *part =
&pip->pfile->poolset->replica[repn]->part[partn];
path = part->path;
} else {
outv(v, "Part file:\n");
path = pip->file_name;
}
outv_field(v, "path", "%s", path);
int is_dev_dax = util_file_is_device_dax(path);
const char *type_str = is_dev_dax ? "device dax" : "regular file";
outv_field(v, "type", "%s", type_str);
ssize_t size = util_file_get_size(path);
if (size < 0) {
outv_err("couldn't get size of %s", path);
return -1;
}
outv_field(v, "size", "%s", out_get_size_str((size_t)size,
pip->args.human));
if (is_dev_dax) {
size_t alignment = util_file_device_dax_alignment(path);
outv_field(v, "alignment", "%s", out_get_size_str(alignment,
pip->args.human));
}
return 0;
}
static void
pmempool_info_directory(struct pool_set_directory *d,
int v)
{
outv(v, "Directory %s:\n", d->path);
outv_field(v, "reservation size", "%lu", d->resvsize);
}
static int
pmempool_info_replica(struct pmem_info *pip, unsigned repn, int v)
{
struct pool_replica *rep = pip->pfile->poolset->replica[repn];
outv(v, "Replica %u%s - %s", repn,
repn == 0 ? " (master)" : "",
rep->remote == NULL ? "local" : "remote");
if (rep->remote) {
outv(v, ":\n");
outv_field(v, "node", "%s", rep->remote->node_addr);
outv_field(v, "pool set", "%s", rep->remote->pool_desc);
return 0;
}
outv(v, ", %u part(s):\n", rep->nparts);
for (unsigned p = 0; p < rep->nparts; ++p) {
if (pmempool_info_part(pip, repn, p, v))
return -1;
}
if (pip->pfile->poolset->directory_based) {
size_t nd = VEC_SIZE(&rep->directory);
outv(v, "%lu %s:\n", nd, nd == 1 ? "Directory" : "Directories");
struct pool_set_directory *d;
VEC_FOREACH_BY_PTR(d, &rep->directory) {
pmempool_info_directory(d, v);
}
}
return 0;
}
static int
pmempool_info_poolset(struct pmem_info *pip, int v)
{
ASSERTeq(pip->params.is_poolset, 1);
if (pip->pfile->poolset->directory_based)
outv(v, "Directory-based Poolset structure:\n");
else
outv(v, "Poolset structure:\n");
outv_field(v, "Number of replicas", "%u",
pip->pfile->poolset->nreplicas);
for (unsigned r = 0; r < pip->pfile->poolset->nreplicas; ++r) {
if (pmempool_info_replica(pip, r, v))
return -1;
}
if (pip->pfile->poolset->options > 0) {
outv_title(v, "Poolset options");
if (pip->pfile->poolset->options & OPTION_NO_HDRS)
outv(v, "%s", "NOHDRS\n");
}
return 0;
}
static int
pmempool_info_pool_hdr(struct pmem_info *pip, int v)
{
static const char *alignment_desc_str[] = {
" char",
" short",
" int",
" long",
" long long",
" size_t",
" os_off_t",
" float",
" double",
" long double",
" void *",
};
static const size_t alignment_desc_n =
sizeof(alignment_desc_str) / sizeof(alignment_desc_str[0]);
int ret = 0;
struct pool_hdr *hdr = malloc(sizeof(struct pool_hdr));
if (!hdr)
err(1, "Cannot allocate memory for pool_hdr");
if (pmempool_info_read(pip, hdr, sizeof(*hdr), 0)) {
outv_err("cannot read pool header\n");
free(hdr);
return -1;
}
struct arch_flags arch_flags;
util_get_arch_flags(&arch_flags);
outv_title(v, "POOL Header");
outv_hexdump(pip->args.vhdrdump, hdr, sizeof(*hdr), 0, 1);
util_convert2h_hdr_nocheck(hdr);
outv_field(v, "Signature", "%.*s%s", POOL_HDR_SIG_LEN,
hdr->signature,
pip->params.is_part ?
" [part file]" : "");
outv_field(v, "Major", "%d", hdr->major);
outv_field(v, "Mandatory features", "%s",
out_get_incompat_features_str(hdr->incompat_features));
outv_field(v, "Not mandatory features", "0x%x", hdr->compat_features);
outv_field(v, "Forced RO", "0x%x", hdr->ro_compat_features);
outv_field(v, "Pool set UUID", "%s",
out_get_uuid_str(hdr->poolset_uuid));
outv_field(v, "UUID", "%s", out_get_uuid_str(hdr->uuid));
outv_field(v, "Previous part UUID", "%s",
out_get_uuid_str(hdr->prev_part_uuid));
outv_field(v, "Next part UUID", "%s",
out_get_uuid_str(hdr->next_part_uuid));
outv_field(v, "Previous replica UUID", "%s",
out_get_uuid_str(hdr->prev_repl_uuid));
outv_field(v, "Next replica UUID", "%s",
out_get_uuid_str(hdr->next_repl_uuid));
outv_field(v, "Creation Time", "%s",
out_get_time_str((time_t)hdr->crtime));
uint64_t ad = hdr->arch_flags.alignment_desc;
uint64_t cur_ad = arch_flags.alignment_desc;
outv_field(v, "Alignment Descriptor", "%s",
out_get_alignment_desc_str(ad, cur_ad));
for (size_t i = 0; i < alignment_desc_n; i++) {
uint64_t a = GET_ALIGNMENT(ad, i);
if (ad == cur_ad) {
outv_field(v + 1, alignment_desc_str[i],
"%2lu", a);
} else {
uint64_t av = GET_ALIGNMENT(cur_ad, i);
if (a == av) {
outv_field(v + 1, alignment_desc_str[i],
"%2lu [OK]", a);
} else {
outv_field(v + 1, alignment_desc_str[i],
"%2lu [wrong! should be %2lu]", a, av);
}
}
}
outv_field(v, "Class", "%s",
out_get_arch_machine_class_str(
hdr->arch_flags.machine_class));
outv_field(v, "Data", "%s",
out_get_arch_data_str(hdr->arch_flags.data));
outv_field(v, "Machine", "%s",
out_get_arch_machine_str(hdr->arch_flags.machine));
outv_field(v, "Checksum", "%s", out_get_checksum(hdr, sizeof(*hdr),
&hdr->checksum));
free(hdr);
return ret;
}
static int
pmempool_info_file(struct pmem_info *pip, const char *file_name)
{
int ret = 0;
pip->file_name = file_name;
if (pip->args.force) {
pip->type = pip->args.type;
} else {
if (pmem_pool_parse_params(file_name, &pip->params, 1)) {
if (errno)
perror(file_name);
else
outv_err("%s: cannot determine type of pool\n",
file_name);
return -1;
}
pip->type = pip->params.type;
}
if (PMEM_POOL_TYPE_UNKNOWN == pip->type) {
outv_err("%s: unknown pool type -- '%s'\n", file_name,
pip->params.signature);
return -1;
} else if (!pip->args.force && !pip->params.is_checksum_ok) {
outv_err("%s: invalid checksum\n", file_name);
return -1;
} else {
if (util_options_verify(pip->opts, pip->type))
return -1;
pip->pfile = pool_set_file_open(file_name, 0, !pip->args.force);
if (!pip->pfile) {
perror(file_name);
return -1;
}
if (pip->type != PMEM_POOL_TYPE_BTT) {
struct pool_set *ps = pip->pfile->poolset;
for (unsigned r = 0; r < ps->nreplicas; ++r) {
if (ps->replica[r]->remote == NULL &&
mprotect(ps->replica[r]->part[0].addr,
ps->replica[r]->repsize,
PROT_READ) < 0) {
outv_err(
"%s: failed to change pool protection",
pip->pfile->fname);
ret = -1;
goto out_close;
}
}
}
if (pip->args.obj.replica) {
size_t nreplicas = pool_set_file_nreplicas(pip->pfile);
if (nreplicas == 1) {
outv_err("only master replica available");
ret = -1;
goto out_close;
}
if (pip->args.obj.replica >= nreplicas) {
outv_err("replica number out of range"
" (valid range is: 0-%" PRIu64 ")",
nreplicas - 1);
ret = -1;
goto out_close;
}
if (pool_set_file_set_replica(pip->pfile,
pip->args.obj.replica)) {
outv_err("setting replica number failed");
ret = -1;
goto out_close;
}
}
if (pip->type != PMEM_POOL_TYPE_BTT) {
if (pip->params.is_poolset &&
pmempool_info_poolset(pip,
VERBOSE_DEFAULT)) {
ret = -1;
goto out_close;
}
if (!pip->params.is_poolset &&
pmempool_info_part(pip, UNDEF_REPLICA,
UNDEF_PART, VERBOSE_DEFAULT)) {
ret = -1;
goto out_close;
}
if (pmempool_info_pool_hdr(pip, VERBOSE_DEFAULT)) {
ret = -1;
goto out_close;
}
}
if (pip->params.is_part) {
ret = 0;
goto out_close;
}
switch (pip->type) {
case PMEM_POOL_TYPE_LOG:
ret = pmempool_info_log(pip);
break;
case PMEM_POOL_TYPE_BLK:
ret = pmempool_info_blk(pip);
break;
case PMEM_POOL_TYPE_OBJ:
ret = pmempool_info_obj(pip);
break;
case PMEM_POOL_TYPE_BTT:
ret = pmempool_info_btt(pip);
break;
case PMEM_POOL_TYPE_UNKNOWN:
default:
ret = -1;
break;
}
out_close:
pool_set_file_close(pip->pfile);
}
return ret;
}
static struct pmem_info *
pmempool_info_alloc(void)
{
struct pmem_info *pip = malloc(sizeof(struct pmem_info));
if (!pip)
err(1, "Cannot allocate memory for pmempool info context");
if (pip) {
memset(pip, 0, sizeof(*pip));
memcpy(&pip->args, &pmempool_info_args_default,
sizeof(pip->args));
pip->opts = util_options_alloc(long_options,
sizeof(long_options) /
sizeof(long_options[0]),
option_requirements);
LIST_INIT(&pip->args.ranges.head);
LIST_INIT(&pip->args.obj.type_ranges.head);
LIST_INIT(&pip->args.obj.lane_ranges.head);
LIST_INIT(&pip->args.obj.zone_ranges.head);
LIST_INIT(&pip->args.obj.chunk_ranges.head);
TAILQ_INIT(&pip->obj.stats.type_stats);
}
return pip;
}
static void
pmempool_info_free(struct pmem_info *pip)
{
if (pip->obj.stats.zone_stats)
free(pip->obj.stats.zone_stats);
util_options_free(pip->opts);
util_ranges_clear(&pip->args.ranges);
util_ranges_clear(&pip->args.obj.type_ranges);
util_ranges_clear(&pip->args.obj.zone_ranges);
util_ranges_clear(&pip->args.obj.chunk_ranges);
util_ranges_clear(&pip->args.obj.lane_ranges);
while (!TAILQ_EMPTY(&pip->obj.stats.type_stats)) {
struct pmem_obj_type_stats *type =
TAILQ_FIRST(&pip->obj.stats.type_stats);
TAILQ_REMOVE(&pip->obj.stats.type_stats, type, next);
free(type);
}
free(pip);
}
int
pmempool_info_func(char *appname, int argc, char *argv[])
{
int ret = 0;
struct pmem_info *pip = pmempool_info_alloc();
if ((ret = parse_args(appname, argc, argv, &pip->args,
pip->opts)) == 0) {
out_set_vlevel(pip->args.vlevel);
out_set_col_width(pip->args.col_width);
ret = pmempool_info_file(pip, pip->args.file);
}
pmempool_info_free(pip);
return ret;
}