summaryrefslogtreecommitdiffstats
path: root/src/pmdk/src/tools/pmempool/info_obj.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdk/src/tools/pmempool/info_obj.c')
-rw-r--r--src/pmdk/src/tools/pmempool/info_obj.c962
1 files changed, 962 insertions, 0 deletions
diff --git a/src/pmdk/src/tools/pmempool/info_obj.c b/src/pmdk/src/tools/pmempool/info_obj.c
new file mode 100644
index 000000000..6dc13d3bf
--- /dev/null
+++ b/src/pmdk/src/tools/pmempool/info_obj.c
@@ -0,0 +1,962 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/* Copyright 2014-2019, Intel Corporation */
+
+/*
+ * info_obj.c -- pmempool info command source file for obj pool
+ */
+#include <stdlib.h>
+#include <stdbool.h>
+#include <err.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#include "alloc_class.h"
+
+#include "set.h"
+#include "common.h"
+#include "output.h"
+#include "info.h"
+#include "util.h"
+
+#define BITMAP_BUFF_SIZE 1024
+
+#define OFF_TO_PTR(pop, off) ((void *)((uintptr_t)(pop) + (off)))
+
+#define PTR_TO_OFF(pop, ptr) ((uintptr_t)(ptr) - (uintptr_t)(pop))
+
+/*
+ * lane_need_recovery -- return 1 if lane section needs recovery
+ */
+static int
+lane_need_recovery(struct pmem_info *pip, struct lane_layout *lane)
+{
+ return ulog_recovery_needed((struct ulog *)&lane->external, 1) ||
+ ulog_recovery_needed((struct ulog *)&lane->internal, 1) ||
+ ulog_recovery_needed((struct ulog *)&lane->undo, 0);
+}
+
+#define RUN_BITMAP_SEPARATOR_DISTANCE 8
+
+/*
+ * get_bitmap_str -- get bitmap single value string
+ */
+static const char *
+get_bitmap_str(uint64_t val, unsigned values)
+{
+ static char buff[BITMAP_BUFF_SIZE];
+
+ unsigned j = 0;
+ for (unsigned i = 0; i < values && j < BITMAP_BUFF_SIZE - 3; i++) {
+ buff[j++] = ((val & ((uint64_t)1 << i)) ? 'x' : '.');
+ if ((i + 1) % RUN_BITMAP_SEPARATOR_DISTANCE == 0)
+ buff[j++] = ' ';
+ }
+
+ buff[j] = '\0';
+
+ return buff;
+}
+
+/*
+ * pmem_obj_stats_get_type -- get stats for specified type number
+ */
+static struct pmem_obj_type_stats *
+pmem_obj_stats_get_type(struct pmem_obj_stats *stats, uint64_t type_num)
+{
+ struct pmem_obj_type_stats *type;
+ struct pmem_obj_type_stats *type_dest = NULL;
+ PMDK_TAILQ_FOREACH(type, &stats->type_stats, next) {
+ if (type->type_num == type_num)
+ return type;
+
+ if (!type_dest && type->type_num > type_num)
+ type_dest = type;
+ }
+
+ type = calloc(1, sizeof(*type));
+ if (!type) {
+ outv_err("cannot allocate memory for type stats\n");
+ exit(EXIT_FAILURE);
+ }
+
+ type->type_num = type_num;
+ if (type_dest)
+ PMDK_TAILQ_INSERT_BEFORE(type_dest, type, next);
+ else
+ PMDK_TAILQ_INSERT_TAIL(&stats->type_stats, type, next);
+
+ return type;
+}
+
+struct info_obj_redo_args {
+ int v;
+ size_t i;
+ struct pmem_info *pip;
+};
+
+/*
+ * info_obj_redo_entry - print redo log entry info
+ */
+static int
+info_obj_redo_entry(struct ulog_entry_base *e, void *arg,
+ const struct pmem_ops *p_ops)
+{
+ struct info_obj_redo_args *a = arg;
+ struct ulog_entry_val *ev;
+ struct ulog_entry_buf *eb;
+
+ switch (ulog_entry_type(e)) {
+ case ULOG_OPERATION_AND:
+ case ULOG_OPERATION_OR:
+ case ULOG_OPERATION_SET:
+ ev = (struct ulog_entry_val *)e;
+
+ outv(a->v, "%010zu: "
+ "Offset: 0x%016jx "
+ "Value: 0x%016jx ",
+ a->i++,
+ ulog_entry_offset(e),
+ ev->value);
+ break;
+ case ULOG_OPERATION_BUF_CPY:
+ case ULOG_OPERATION_BUF_SET:
+ eb = (struct ulog_entry_buf *)e;
+
+ outv(a->v, "%010zu: "
+ "Offset: 0x%016jx "
+ "Size: %s ",
+ a->i++,
+ ulog_entry_offset(e),
+ out_get_size_str(eb->size,
+ a->pip->args.human));
+ break;
+ default:
+ ASSERT(0); /* unreachable */
+ }
+
+ return 0;
+}
+
+/*
+ * info_obj_redo -- print ulog log entries
+ */
+static void
+info_obj_ulog(struct pmem_info *pip, int v, struct ulog *ulog,
+ const struct pmem_ops *ops)
+{
+ outv_title(v, "Log entries");
+
+ struct info_obj_redo_args args = {v, 0, pip};
+ ulog_foreach_entry(ulog, info_obj_redo_entry, &args, ops);
+}
+
+/*
+ * info_obj_alloc_hdr -- print allocation header
+ */
+static void
+info_obj_alloc_hdr(struct pmem_info *pip, int v,
+ const struct memory_block *m)
+{
+ outv_title(v, "Allocation Header");
+
+ outv_field(v, "Size", "%s", out_get_size_str(m->m_ops->get_user_size(m),
+ pip->args.human));
+ outv_field(v, "Extra", "%lu", m->m_ops->get_extra(m));
+ outv_field(v, "Flags", "0x%x", m->m_ops->get_flags(m));
+}
+
+/*
+ * info_obj_object_hdr -- print object headers and data
+ */
+static void
+info_obj_object_hdr(struct pmem_info *pip, int v, int vid,
+ const struct memory_block *m, uint64_t id)
+{
+ struct pmemobjpool *pop = pip->obj.pop;
+
+ void *data = m->m_ops->get_user_data(m);
+
+ outv_nl(vid);
+ outv_field(vid, "Object", "%lu", id);
+ outv_field(vid, "Offset", "0x%016lx", PTR_TO_OFF(pop, data));
+
+ int vahdr = v && pip->args.obj.valloc;
+ int voobh = v && pip->args.obj.voobhdr;
+
+ outv_indent(vahdr || voobh, 1);
+
+ info_obj_alloc_hdr(pip, vahdr, m);
+
+ outv_hexdump(v && pip->args.vdata, data,
+ m->m_ops->get_real_size(m),
+ PTR_TO_OFF(pip->obj.pop, data), 1);
+
+ outv_indent(vahdr || voobh, -1);
+
+}
+
+/*
+ * info_obj_lane_section -- print lane's section
+ */
+static void
+info_obj_lane(struct pmem_info *pip, int v, struct lane_layout *lane)
+{
+ struct pmem_ops p_ops;
+ p_ops.base = pip->obj.pop;
+
+ outv_title(v, "Undo Log");
+ outv_indent(v, 1);
+ info_obj_ulog(pip, v, (struct ulog *)&lane->undo, &p_ops);
+ outv_indent(v, -1);
+
+ outv_nl(v);
+ outv_title(v, "Internal Undo Log");
+ outv_indent(v, 1);
+ info_obj_ulog(pip, v, (struct ulog *)&lane->internal, &p_ops);
+ outv_indent(v, -1);
+
+ outv_title(v, "External Undo Log");
+ outv_indent(v, 1);
+ info_obj_ulog(pip, v, (struct ulog *)&lane->external, &p_ops);
+ outv_indent(v, -1);
+}
+
+/*
+ * info_obj_lanes -- print lanes structures
+ */
+static void
+info_obj_lanes(struct pmem_info *pip)
+{
+ int v = pip->args.obj.vlanes;
+
+ if (!outv_check(v))
+ return;
+
+ struct pmemobjpool *pop = pip->obj.pop;
+ /*
+ * Iterate through all lanes from specified range and print
+ * specified sections.
+ */
+ struct lane_layout *lanes = (void *)((char *)pip->obj.pop +
+ pop->lanes_offset);
+ struct range *curp = NULL;
+ FOREACH_RANGE(curp, &pip->args.obj.lane_ranges) {
+ for (uint64_t i = curp->first;
+ i <= curp->last && i < pop->nlanes; i++) {
+
+ /* For -R check print lane only if needs recovery */
+ if (pip->args.obj.lanes_recovery &&
+ !lane_need_recovery(pip, &lanes[i]))
+ continue;
+
+ outv_title(v, "Lane %" PRIu64, i);
+
+ outv_indent(v, 1);
+
+ info_obj_lane(pip, v, &lanes[i]);
+
+ outv_indent(v, -1);
+ }
+ }
+}
+
+/*
+ * info_obj_heap -- print pmemobj heap headers
+ */
+static void
+info_obj_heap(struct pmem_info *pip)
+{
+ int v = pip->args.obj.vheap;
+ struct pmemobjpool *pop = pip->obj.pop;
+ struct heap_layout *layout = OFF_TO_PTR(pop, pop->heap_offset);
+ struct heap_header *heap = &layout->header;
+
+ outv(v, "\nPMEMOBJ Heap Header:\n");
+ outv_hexdump(v && pip->args.vhdrdump, heap, sizeof(*heap),
+ pop->heap_offset, 1);
+
+ outv_field(v, "Signature", "%s", heap->signature);
+ outv_field(v, "Major", "%ld", heap->major);
+ outv_field(v, "Minor", "%ld", heap->minor);
+ outv_field(v, "Chunk size", "%s",
+ out_get_size_str(heap->chunksize, pip->args.human));
+ outv_field(v, "Chunks per zone", "%ld", heap->chunks_per_zone);
+ outv_field(v, "Checksum", "%s", out_get_checksum(heap, sizeof(*heap),
+ &heap->checksum, 0));
+}
+
+/*
+ * info_obj_zone -- print information about zone
+ */
+static void
+info_obj_zone_hdr(struct pmem_info *pip, int v, struct zone_header *zone)
+{
+ outv_hexdump(v && pip->args.vhdrdump, zone, sizeof(*zone),
+ PTR_TO_OFF(pip->obj.pop, zone), 1);
+ outv_field(v, "Magic", "%s", out_get_zone_magic_str(zone->magic));
+ outv_field(v, "Size idx", "%u", zone->size_idx);
+}
+
+/*
+ * info_obj_object -- print information about object
+ */
+static void
+info_obj_object(struct pmem_info *pip, const struct memory_block *m,
+ uint64_t objid)
+{
+ if (!util_ranges_contain(&pip->args.ranges, objid))
+ return;
+
+ uint64_t type_num = m->m_ops->get_extra(m);
+
+ if (!util_ranges_contain(&pip->args.obj.type_ranges, type_num))
+ return;
+
+ uint64_t real_size = m->m_ops->get_real_size(m);
+ pip->obj.stats.n_total_objects++;
+ pip->obj.stats.n_total_bytes += real_size;
+
+ struct pmem_obj_type_stats *type_stats =
+ pmem_obj_stats_get_type(&pip->obj.stats, type_num);
+
+ type_stats->n_objects++;
+ type_stats->n_bytes += real_size;
+
+ int vid = pip->args.obj.vobjects;
+ int v = pip->args.obj.vobjects;
+
+ outv_indent(v, 1);
+ info_obj_object_hdr(pip, v, vid, m, objid);
+ outv_indent(v, -1);
+}
+
+/*
+ * info_obj_run_bitmap -- print chunk run's bitmap
+ */
+static void
+info_obj_run_bitmap(int v, struct run_bitmap *b)
+{
+ /* print only used values for lower verbosity */
+ uint32_t i;
+ for (i = 0; i < b->nbits / RUN_BITS_PER_VALUE; i++)
+ outv(v, "%s\n", get_bitmap_str(b->values[i],
+ RUN_BITS_PER_VALUE));
+
+ unsigned mod = b->nbits % RUN_BITS_PER_VALUE;
+ if (mod != 0) {
+ outv(v, "%s\n", get_bitmap_str(b->values[i], mod));
+ }
+}
+
+/*
+ * info_obj_memblock_is_root -- (internal) checks whether the object is root
+ */
+static int
+info_obj_memblock_is_root(struct pmem_info *pip, const struct memory_block *m)
+{
+ uint64_t roff = pip->obj.pop->root_offset;
+ if (roff == 0)
+ return 0;
+
+ struct memory_block rm = memblock_from_offset(pip->obj.heap, roff);
+
+ return MEMORY_BLOCK_EQUALS(*m, rm);
+}
+
+/*
+ * info_obj_run_cb -- (internal) run object callback
+ */
+static int
+info_obj_run_cb(const struct memory_block *m, void *arg)
+{
+ struct pmem_info *pip = arg;
+
+ if (info_obj_memblock_is_root(pip, m))
+ return 0;
+
+ info_obj_object(pip, m, pip->obj.objid++);
+
+ return 0;
+}
+
+static struct pmem_obj_class_stats *
+info_obj_class_stats_get_or_insert(struct pmem_obj_zone_stats *stats,
+ uint64_t unit_size, uint64_t alignment,
+ uint32_t nallocs, uint16_t flags)
+{
+ struct pmem_obj_class_stats *cstats;
+ VEC_FOREACH_BY_PTR(cstats, &stats->class_stats) {
+ if (cstats->alignment == alignment &&
+ cstats->flags == flags &&
+ cstats->nallocs == nallocs &&
+ cstats->unit_size == unit_size)
+ return cstats;
+ }
+
+ struct pmem_obj_class_stats s = {0, 0, unit_size,
+ alignment, nallocs, flags};
+
+ if (VEC_PUSH_BACK(&stats->class_stats, s) != 0)
+ return NULL;
+
+ return &VEC_BACK(&stats->class_stats);
+}
+
+/*
+ * info_obj_chunk -- print chunk info
+ */
+static void
+info_obj_chunk(struct pmem_info *pip, uint64_t c, uint64_t z,
+ struct chunk_header *chunk_hdr, struct chunk *chunk,
+ struct pmem_obj_zone_stats *stats)
+{
+ int v = pip->args.obj.vchunkhdr;
+ outv(v, "\n");
+ outv_field(v, "Chunk", "%lu", c);
+
+ struct pmemobjpool *pop = pip->obj.pop;
+
+ outv_hexdump(v && pip->args.vhdrdump, chunk_hdr, sizeof(*chunk_hdr),
+ PTR_TO_OFF(pop, chunk_hdr), 1);
+
+ outv_field(v, "Type", "%s", out_get_chunk_type_str(chunk_hdr->type));
+ outv_field(v, "Flags", "0x%x %s", chunk_hdr->flags,
+ out_get_chunk_flags(chunk_hdr->flags));
+ outv_field(v, "Size idx", "%u", chunk_hdr->size_idx);
+
+ struct memory_block m = MEMORY_BLOCK_NONE;
+ m.zone_id = (uint32_t)z;
+ m.chunk_id = (uint32_t)c;
+ m.size_idx = (uint32_t)chunk_hdr->size_idx;
+ memblock_rebuild_state(pip->obj.heap, &m);
+
+ if (chunk_hdr->type == CHUNK_TYPE_USED ||
+ chunk_hdr->type == CHUNK_TYPE_FREE) {
+ VEC_FRONT(&stats->class_stats).n_units +=
+ chunk_hdr->size_idx;
+
+ if (chunk_hdr->type == CHUNK_TYPE_USED) {
+ VEC_FRONT(&stats->class_stats).n_used +=
+ chunk_hdr->size_idx;
+
+ /* skip root object */
+ if (!info_obj_memblock_is_root(pip, &m)) {
+ info_obj_object(pip, &m, pip->obj.objid++);
+ }
+ }
+ } else if (chunk_hdr->type == CHUNK_TYPE_RUN) {
+ struct chunk_run *run = (struct chunk_run *)chunk;
+
+ outv_hexdump(v && pip->args.vhdrdump, run,
+ sizeof(run->hdr.block_size) +
+ sizeof(run->hdr.alignment),
+ PTR_TO_OFF(pop, run), 1);
+
+ struct run_bitmap bitmap;
+ m.m_ops->get_bitmap(&m, &bitmap);
+
+ struct pmem_obj_class_stats *cstats =
+ info_obj_class_stats_get_or_insert(stats,
+ run->hdr.block_size, run->hdr.alignment, bitmap.nbits,
+ chunk_hdr->flags);
+ if (cstats == NULL) {
+ outv_err("out of memory, can't allocate statistics");
+ return;
+ }
+
+ outv_field(v, "Block size", "%s",
+ out_get_size_str(run->hdr.block_size,
+ pip->args.human));
+
+ uint32_t units = bitmap.nbits;
+ uint32_t free_space = 0;
+ uint32_t max_free_block = 0;
+ m.m_ops->calc_free(&m, &free_space, &max_free_block);
+ uint32_t used = units - free_space;
+
+ cstats->n_units += units;
+ cstats->n_used += used;
+
+ outv_field(v, "Bitmap", "%u / %u", used, units);
+
+ info_obj_run_bitmap(v && pip->args.obj.vbitmap, &bitmap);
+
+ m.m_ops->iterate_used(&m, info_obj_run_cb, pip);
+ }
+}
+
+/*
+ * info_obj_zone_chunks -- print chunk headers from specified zone
+ */
+static void
+info_obj_zone_chunks(struct pmem_info *pip, struct zone *zone, uint64_t z,
+ struct pmem_obj_zone_stats *stats)
+{
+ VEC_INIT(&stats->class_stats);
+
+ struct pmem_obj_class_stats default_class_stats = {0, 0,
+ CHUNKSIZE, 0, 0, 0};
+ VEC_PUSH_BACK(&stats->class_stats, default_class_stats);
+
+ uint64_t c = 0;
+ while (c < zone->header.size_idx) {
+ enum chunk_type type = zone->chunk_headers[c].type;
+ uint64_t size_idx = zone->chunk_headers[c].size_idx;
+ if (util_ranges_contain(&pip->args.obj.chunk_ranges, c)) {
+ if (pip->args.obj.chunk_types & (1ULL << type)) {
+ stats->n_chunks++;
+ stats->n_chunks_type[type]++;
+
+ stats->size_chunks += size_idx;
+ stats->size_chunks_type[type] += size_idx;
+
+ info_obj_chunk(pip, c, z,
+ &zone->chunk_headers[c],
+ &zone->chunks[c], stats);
+
+ }
+
+ if (size_idx > 1 && type != CHUNK_TYPE_RUN &&
+ pip->args.obj.chunk_types &
+ (1 << CHUNK_TYPE_FOOTER)) {
+ size_t f = c + size_idx - 1;
+ info_obj_chunk(pip, f, z,
+ &zone->chunk_headers[f],
+ &zone->chunks[f], stats);
+ }
+ }
+
+ c += size_idx;
+ }
+}
+
+/*
+ * info_obj_root_obj -- print root object
+ */
+static void
+info_obj_root_obj(struct pmem_info *pip)
+{
+ int v = pip->args.obj.vroot;
+
+ struct pmemobjpool *pop = pip->obj.pop;
+ if (!pop->root_offset) {
+ outv(v, "\nNo root object...\n");
+ return;
+ }
+
+ outv_title(v, "Root object");
+ outv_field(v, "Offset", "0x%016zx", pop->root_offset);
+ uint64_t root_size = pop->root_size;
+ outv_field(v, "Size", "%s",
+ out_get_size_str(root_size, pip->args.human));
+
+ struct memory_block m = memblock_from_offset(
+ pip->obj.heap, pop->root_offset);
+
+ /* do not print object id and offset for root object */
+ info_obj_object_hdr(pip, v, VERBOSE_SILENT, &m, 0);
+}
+
+/*
+ * info_obj_zones -- print zones and chunks
+ */
+static void
+info_obj_zones_chunks(struct pmem_info *pip)
+{
+ if (!outv_check(pip->args.obj.vheap) &&
+ !outv_check(pip->args.vstats) &&
+ !outv_check(pip->args.obj.vobjects))
+ return;
+
+ struct pmemobjpool *pop = pip->obj.pop;
+ struct heap_layout *layout = OFF_TO_PTR(pop, pop->heap_offset);
+ size_t maxzone = util_heap_max_zone(pop->heap_size);
+ pip->obj.stats.n_zones = maxzone;
+ pip->obj.stats.zone_stats = calloc(maxzone,
+ sizeof(struct pmem_obj_zone_stats));
+ if (!pip->obj.stats.zone_stats)
+ err(1, "Cannot allocate memory for zone stats");
+
+ for (size_t i = 0; i < maxzone; i++) {
+ struct zone *zone = ZID_TO_ZONE(layout, i);
+
+ if (util_ranges_contain(&pip->args.obj.zone_ranges, i)) {
+ int vvv = pip->args.obj.vheap &&
+ (pip->args.obj.vzonehdr ||
+ pip->args.obj.vchunkhdr);
+
+ outv_title(vvv, "Zone %zu", i);
+
+ if (zone->header.magic == ZONE_HEADER_MAGIC)
+ pip->obj.stats.n_zones_used++;
+
+ info_obj_zone_hdr(pip, pip->args.obj.vheap &&
+ pip->args.obj.vzonehdr,
+ &zone->header);
+
+ outv_indent(vvv, 1);
+ info_obj_zone_chunks(pip, zone, i,
+ &pip->obj.stats.zone_stats[i]);
+ outv_indent(vvv, -1);
+ }
+ }
+}
+
+/*
+ * info_obj_descriptor -- print pmemobj descriptor
+ */
+static void
+info_obj_descriptor(struct pmem_info *pip)
+{
+ int v = VERBOSE_DEFAULT;
+
+ if (!outv_check(v))
+ return;
+
+ outv(v, "\nPMEM OBJ Header:\n");
+ struct pmemobjpool *pop = pip->obj.pop;
+
+ uint8_t *hdrptr = (uint8_t *)pop + sizeof(pop->hdr);
+ size_t hdrsize = sizeof(*pop) - sizeof(pop->hdr);
+ size_t hdroff = sizeof(pop->hdr);
+ outv_hexdump(pip->args.vhdrdump, hdrptr, hdrsize, hdroff, 1);
+
+ /* check if layout is zeroed */
+ char *layout = util_check_memory((uint8_t *)pop->layout,
+ sizeof(pop->layout), 0) ?
+ pop->layout : "(null)";
+
+ /* address for checksum */
+ void *dscp = (void *)((uintptr_t)(pop) + sizeof(struct pool_hdr));
+
+ outv_field(v, "Layout", "%s", layout);
+ outv_field(v, "Lanes offset", "0x%lx", pop->lanes_offset);
+ outv_field(v, "Number of lanes", "%lu", pop->nlanes);
+ outv_field(v, "Heap offset", "0x%lx", pop->heap_offset);
+ outv_field(v, "Heap size", "%lu", pop->heap_size);
+ outv_field(v, "Checksum", "%s", out_get_checksum(dscp, OBJ_DSC_P_SIZE,
+ &pop->checksum, 0));
+ outv_field(v, "Root offset", "0x%lx", pop->root_offset);
+
+ /* run id with -v option */
+ outv_field(v + 1, "Run id", "%lu", pop->run_id);
+}
+
+/*
+ * info_obj_stats_objjects -- print objects' statistics
+ */
+static void
+info_obj_stats_objects(struct pmem_info *pip, int v,
+ struct pmem_obj_stats *stats)
+{
+ outv_field(v, "Number of objects", "%lu",
+ stats->n_total_objects);
+ outv_field(v, "Number of bytes", "%s", out_get_size_str(
+ stats->n_total_bytes, pip->args.human));
+
+ outv_title(v, "Objects by type");
+
+ outv_indent(v, 1);
+ struct pmem_obj_type_stats *type_stats;
+ PMDK_TAILQ_FOREACH(type_stats, &pip->obj.stats.type_stats, next) {
+ if (!type_stats->n_objects)
+ continue;
+
+ double n_objects_perc = 100.0 *
+ (double)type_stats->n_objects /
+ (double)stats->n_total_objects;
+ double n_bytes_perc = 100.0 *
+ (double)type_stats->n_bytes /
+ (double)stats->n_total_bytes;
+
+ outv_nl(v);
+ outv_field(v, "Type number", "%lu", type_stats->type_num);
+ outv_field(v, "Number of objects", "%lu [%s]",
+ type_stats->n_objects,
+ out_get_percentage(n_objects_perc));
+ outv_field(v, "Number of bytes", "%s [%s]",
+ out_get_size_str(
+ type_stats->n_bytes,
+ pip->args.human),
+ out_get_percentage(n_bytes_perc));
+ }
+ outv_indent(v, -1);
+}
+
+/*
+ * info_boj_stats_alloc_classes -- print allocation classes' statistics
+ */
+static void
+info_obj_stats_alloc_classes(struct pmem_info *pip, int v,
+ struct pmem_obj_zone_stats *stats)
+{
+ uint64_t total_bytes = 0;
+ uint64_t total_used = 0;
+
+ outv_indent(v, 1);
+
+ struct pmem_obj_class_stats *cstats;
+ VEC_FOREACH_BY_PTR(cstats, &stats->class_stats) {
+ if (cstats->n_units == 0)
+ continue;
+
+ double used_perc = 100.0 *
+ (double)cstats->n_used / (double)cstats->n_units;
+
+ outv_nl(v);
+ outv_field(v, "Unit size", "%s", out_get_size_str(
+ cstats->unit_size, pip->args.human));
+ outv_field(v, "Units", "%lu", cstats->n_units);
+ outv_field(v, "Used units", "%lu [%s]",
+ cstats->n_used,
+ out_get_percentage(used_perc));
+
+ uint64_t bytes = cstats->unit_size *
+ cstats->n_units;
+ uint64_t used = cstats->unit_size *
+ cstats->n_used;
+
+ total_bytes += bytes;
+ total_used += used;
+
+ double used_bytes_perc = 100.0 * (double)used / (double)bytes;
+
+ outv_field(v, "Bytes", "%s",
+ out_get_size_str(bytes, pip->args.human));
+ outv_field(v, "Used bytes", "%s [%s]",
+ out_get_size_str(used, pip->args.human),
+ out_get_percentage(used_bytes_perc));
+ }
+
+ outv_indent(v, -1);
+
+ double used_bytes_perc = total_bytes ? 100.0 *
+ (double)total_used / (double)total_bytes : 0.0;
+
+ outv_nl(v);
+ outv_field(v, "Total bytes", "%s",
+ out_get_size_str(total_bytes, pip->args.human));
+ outv_field(v, "Total used bytes", "%s [%s]",
+ out_get_size_str(total_used, pip->args.human),
+ out_get_percentage(used_bytes_perc));
+
+}
+
+/*
+ * info_obj_stats_chunks -- print chunks' statistics
+ */
+static void
+info_obj_stats_chunks(struct pmem_info *pip, int v,
+ struct pmem_obj_zone_stats *stats)
+{
+ outv_field(v, "Number of chunks", "%lu", stats->n_chunks);
+
+ outv_indent(v, 1);
+ for (unsigned type = 0; type < MAX_CHUNK_TYPE; type++) {
+ double type_perc = 100.0 *
+ (double)stats->n_chunks_type[type] /
+ (double)stats->n_chunks;
+ if (stats->n_chunks_type[type]) {
+ outv_field(v, out_get_chunk_type_str(type),
+ "%lu [%s]",
+ stats->n_chunks_type[type],
+ out_get_percentage(type_perc));
+ }
+ }
+ outv_indent(v, -1);
+
+ outv_nl(v);
+ outv_field(v, "Total chunks size", "%s", out_get_size_str(
+ stats->size_chunks, pip->args.human));
+
+ outv_indent(v, 1);
+ for (unsigned type = 0; type < MAX_CHUNK_TYPE; type++) {
+ double type_perc = 100.0 *
+ (double)stats->size_chunks_type[type] /
+ (double)stats->size_chunks;
+ if (stats->size_chunks_type[type]) {
+ outv_field(v, out_get_chunk_type_str(type),
+ "%lu [%s]",
+ stats->size_chunks_type[type],
+ out_get_percentage(type_perc));
+ }
+
+ }
+ outv_indent(v, -1);
+}
+
+/*
+ * info_obj_add_zone_stats -- add stats to total
+ */
+static void
+info_obj_add_zone_stats(struct pmem_obj_zone_stats *total,
+ struct pmem_obj_zone_stats *stats)
+{
+ total->n_chunks += stats->n_chunks;
+ total->size_chunks += stats->size_chunks;
+
+ for (int type = 0; type < MAX_CHUNK_TYPE; type++) {
+ total->n_chunks_type[type] +=
+ stats->n_chunks_type[type];
+ total->size_chunks_type[type] +=
+ stats->size_chunks_type[type];
+ }
+
+ struct pmem_obj_class_stats *cstats;
+ VEC_FOREACH_BY_PTR(cstats, &stats->class_stats) {
+ struct pmem_obj_class_stats *ctotal =
+ info_obj_class_stats_get_or_insert(total, cstats->unit_size,
+ cstats->alignment, cstats->nallocs, cstats->flags);
+ if (ctotal == NULL) {
+ outv_err("out of memory, can't allocate statistics");
+ return;
+ }
+ ctotal->n_units += cstats->n_units;
+ ctotal->n_used += cstats->n_used;
+ }
+}
+
+/*
+ * info_obj_stats_zones -- print zones' statistics
+ */
+static void
+info_obj_stats_zones(struct pmem_info *pip, int v, struct pmem_obj_stats *stats,
+ struct pmem_obj_zone_stats *total)
+{
+ double used_zones_perc = 100.0 * (double)stats->n_zones_used /
+ (double)stats->n_zones;
+
+ outv_field(v, "Number of zones", "%lu", stats->n_zones);
+ outv_field(v, "Number of used zones", "%lu [%s]", stats->n_zones_used,
+ out_get_percentage(used_zones_perc));
+
+ outv_indent(v, 1);
+ for (uint64_t i = 0; i < stats->n_zones_used; i++) {
+ outv_title(v, "Zone %" PRIu64, i);
+
+ struct pmem_obj_zone_stats *zstats = &stats->zone_stats[i];
+
+ info_obj_stats_chunks(pip, v, zstats);
+
+ outv_title(v, "Zone's allocation classes");
+ info_obj_stats_alloc_classes(pip, v, zstats);
+
+ info_obj_add_zone_stats(total, zstats);
+ }
+ outv_indent(v, -1);
+}
+
+/*
+ * info_obj_stats -- print statistics
+ */
+static void
+info_obj_stats(struct pmem_info *pip)
+{
+ int v = pip->args.vstats;
+
+ if (!outv_check(v))
+ return;
+
+ struct pmem_obj_stats *stats = &pip->obj.stats;
+ struct pmem_obj_zone_stats total;
+ memset(&total, 0, sizeof(total));
+
+ outv_title(v, "Statistics");
+
+ outv_title(v, "Objects");
+ info_obj_stats_objects(pip, v, stats);
+
+ outv_title(v, "Heap");
+ info_obj_stats_zones(pip, v, stats, &total);
+
+ if (stats->n_zones_used > 1) {
+ outv_title(v, "Total zone's statistics");
+ outv_title(v, "Chunks statistics");
+ info_obj_stats_chunks(pip, v, &total);
+
+ outv_title(v, "Allocation classes");
+ info_obj_stats_alloc_classes(pip, v, &total);
+ }
+ VEC_DELETE(&total.class_stats);
+}
+
+static struct pmem_info *Pip;
+#ifndef _WIN32
+static void
+info_obj_sa_sigaction(int signum, siginfo_t *info, void *context)
+{
+ uintptr_t offset = (uintptr_t)info->si_addr - (uintptr_t)Pip->obj.pop;
+ outv_err("Invalid offset 0x%lx\n", offset);
+ exit(EXIT_FAILURE);
+}
+
+static struct sigaction info_obj_sigaction = {
+ .sa_sigaction = info_obj_sa_sigaction,
+ .sa_flags = SA_SIGINFO
+};
+#else
+#define CALL_FIRST 1
+
+static LONG CALLBACK
+exception_handler(_In_ PEXCEPTION_POINTERS ExceptionInfo)
+{
+ PEXCEPTION_RECORD record = ExceptionInfo->ExceptionRecord;
+ if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ uintptr_t offset = (uintptr_t)record->ExceptionInformation[1] -
+ (uintptr_t)Pip->obj.pop;
+ outv_err("Invalid offset 0x%lx\n", offset);
+ exit(EXIT_FAILURE);
+}
+#endif
+
+/*
+ * info_obj -- print information about obj pool type
+ */
+int
+pmempool_info_obj(struct pmem_info *pip)
+{
+ pip->obj.pop = pool_set_file_map(pip->pfile, 0);
+ if (pip->obj.pop == NULL)
+ return -1;
+
+ pip->obj.size = pip->pfile->size;
+
+ struct palloc_heap *heap = calloc(1, sizeof(*heap));
+ if (heap == NULL)
+ err(1, "Cannot allocate memory for heap data");
+
+ heap->layout = OFF_TO_PTR(pip->obj.pop, pip->obj.pop->heap_offset);
+ heap->base = pip->obj.pop;
+ pip->obj.alloc_classes = alloc_class_collection_new();
+ pip->obj.heap = heap;
+
+ Pip = pip;
+#ifndef _WIN32
+ if (sigaction(SIGSEGV, &info_obj_sigaction, NULL)) {
+#else
+ if (AddVectoredExceptionHandler(CALL_FIRST, exception_handler) ==
+ NULL) {
+#endif
+ perror("sigaction");
+ return -1;
+ }
+
+ pip->obj.uuid_lo = pmemobj_get_uuid_lo(pip->obj.pop);
+
+ info_obj_descriptor(pip);
+ info_obj_lanes(pip);
+ info_obj_root_obj(pip);
+ info_obj_heap(pip);
+ info_obj_zones_chunks(pip);
+ info_obj_stats(pip);
+
+ free(heap);
+ alloc_class_collection_delete(pip->obj.alloc_classes);
+
+ return 0;
+}