diff --git a/src/PMDK.sln b/src/PMDK.sln
index b35594d61d2f88b1d83921563ff945b7ce70785a..18f14b72e928f72893bccf4d24669de70a04930b 100644
--- a/src/PMDK.sln
+++ b/src/PMDK.sln
@@ -538,6 +538,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obj_include", "test\obj_inc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpmempool_sync", "test\libpmempool_sync\libpmempool_sync.vcxproj", "{AE1C32FB-9B52-4760-ABFC-0D2FA2C7A6C8}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obj_defrag_advanced", "test\obj_defrag_advanced\obj_defrag_advanced.vcxproj", "{AE952763-5C84-43FC-B344-CACC950F056C}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "signal_handle", "test\signal_handle\signal_handle.vcxproj", "{AE9E908D-BAEC-491F-9914-436B3CE35E94}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obj_ctl_config", "test\obj_ctl_config\obj_ctl_config.vcxproj", "{AEAA72CD-E060-417C-9CA1-49B4738384E0}"
@@ -1416,6 +1418,10 @@ Global
{AE1C32FB-9B52-4760-ABFC-0D2FA2C7A6C8}.Debug|x64.Build.0 = Debug|x64
{AE1C32FB-9B52-4760-ABFC-0D2FA2C7A6C8}.Release|x64.ActiveCfg = Release|x64
{AE1C32FB-9B52-4760-ABFC-0D2FA2C7A6C8}.Release|x64.Build.0 = Release|x64
+ {AE952763-5C84-43FC-B344-CACC950F056C}.Debug|x64.ActiveCfg = Debug|x64
+ {AE952763-5C84-43FC-B344-CACC950F056C}.Debug|x64.Build.0 = Debug|x64
+ {AE952763-5C84-43FC-B344-CACC950F056C}.Release|x64.ActiveCfg = Release|x64
+ {AE952763-5C84-43FC-B344-CACC950F056C}.Release|x64.Build.0 = Release|x64
{AE9E908D-BAEC-491F-9914-436B3CE35E94}.Debug|x64.ActiveCfg = Debug|x64
{AE9E908D-BAEC-491F-9914-436B3CE35E94}.Debug|x64.Build.0 = Debug|x64
{AE9E908D-BAEC-491F-9914-436B3CE35E94}.Release|x64.ActiveCfg = Release|x64
@@ -1926,6 +1932,7 @@ Global
{A7CA7975-CEDB-48E6-9AEB-1209DCBD07F2} = {91C30620-70CA-46C7-AC71-71F3C602690E}
{AB15A115-E429-4123-BEBF-206FBA4CF615} = {63C9B3F8-437D-4AD9-B32D-D04AE38C35B6}
{AE1C32FB-9B52-4760-ABFC-0D2FA2C7A6C8} = {2F543422-4B8A-4898-BE6B-590F52B4E9D1}
+ {AE952763-5C84-43FC-B344-CACC950F056C} = {63C9B3F8-437D-4AD9-B32D-D04AE38C35B6}
{AE9E908D-BAEC-491F-9914-436B3CE35E94} = {B870D8A6-12CD-4DD0-B843-833695C2310A}
{AEAA72CD-E060-417C-9CA1-49B4738384E0} = {63C9B3F8-437D-4AD9-B32D-D04AE38C35B6}
{AF038868-2432-4159-A62F-941F11D12C5D} = {59AB6976-D16B-48D0-8D16-94360D3FE51D}
diff --git a/src/test/Makefile b/src/test/Makefile
index 7911541a7a015053ce368ae742eec8be367afa14..a4aaab44a29cd21b601312924e9c4f27bc432308 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -1,5 +1,5 @@
#
-# Copyright 2014-2019, Intel Corporation
+# Copyright 2014-2020, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -88,6 +88,7 @@ OBJ_TESTS = \
obj_ctl_stats\
obj_debug\
obj_defrag\
+ obj_defrag_advanced\
obj_direct\
obj_direct_volatile\
obj_extend\
diff --git a/src/test/obj_defrag_advanced/.gitignore b/src/test/obj_defrag_advanced/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..55413109ceadc4e20192f558c06c425488b8cf3b
--- /dev/null
+++ b/src/test/obj_defrag_advanced/.gitignore
@@ -0,0 +1 @@
+obj_defrag_advanced
diff --git a/src/test/obj_defrag_advanced/Makefile b/src/test/obj_defrag_advanced/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f2e8eda94bf761a84a119d58b963abcdcedeefdf
--- /dev/null
+++ b/src/test/obj_defrag_advanced/Makefile
@@ -0,0 +1,43 @@
+#
+# Copyright 2020, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# src/test/obj_defrag_advanced/Makefile -- build obj_defrag_advanced test
+#
+TARGET = obj_defrag_advanced
+OBJS = vgraph.o pgraph.o obj_defrag_advanced.o
+
+LIBPMEMOBJ=y
+
+include ../Makefile.inc
+
+CFLAGS += -DDEBUG
diff --git a/src/test/obj_defrag_advanced/TESTS.py b/src/test/obj_defrag_advanced/TESTS.py
new file mode 100755
index 0000000000000000000000000000000000000000..8846f119b71b22fb1a313470c27a8f802ad23464
--- /dev/null
+++ b/src/test/obj_defrag_advanced/TESTS.py
@@ -0,0 +1,136 @@
+#!../env.py
+#
+# Copyright 2020, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+import testframework as t
+
+
+class ObjDefragAdvanced(t.BaseTest):
+ test_type = t.Short
+
+ max_nodes = 50
+ max_edges = 10
+ graph_copies = 10
+ pool_size = 500 * t.MiB
+ max_rounds = 10
+ min_root_size = 0
+
+ def run(self, ctx):
+ path = ctx.create_holey_file(self.pool_size, 'testfile')
+ dump1 = 'dump1.log'
+ dump2 = 'dump2.log'
+
+ ctx.exec('obj_defrag_advanced',
+ 'op_pool_create', path,
+ 'op_graph_create', str(self.max_nodes), str(self.max_edges),
+ str(self.graph_copies), str(self.min_root_size),
+ 'op_graph_dump', dump1,
+ 'op_graph_defrag', str(self.max_rounds),
+ 'op_graph_dump', dump2,
+ 'op_pool_close',
+ 'op_dump_compare', dump1, dump2)
+
+
+class TEST0(ObjDefragAdvanced):
+ max_nodes = 5
+ max_edges = 5
+ graph_copies = 5
+
+
+class TEST1(ObjDefragAdvanced):
+ max_nodes = 2048
+ max_edges = 5
+ graph_copies = 5
+
+
+class TEST2(ObjDefragAdvanced):
+ test_type = t.Medium
+ fs = t.Pmem
+
+ max_nodes = 512
+ max_edges = 64
+ graph_copies = 5
+ min_root_size = 4096
+
+
+class ObjDefragAdvancedMt(ObjDefragAdvanced):
+ test_type = t.Medium
+ fs = t.Pmem
+
+ nthreads = 2
+ ncycles = 2
+
+ def run(self, ctx):
+ path = ctx.create_holey_file(self.pool_size, 'testfile')
+
+ ctx.exec('obj_defrag_advanced',
+ 'op_pool_create', path,
+ 'op_graph_create_n_defrag_mt', str(self.max_nodes), str(self.max_edges),
+ str(self.graph_copies), str(self.min_root_size), str(self.max_rounds),
+ str(self.nthreads), str(self.ncycles),
+ 'op_pool_close');
+
+
+class TEST3(ObjDefragAdvancedMt):
+ max_nodes = 256
+ max_edges = 64
+ graph_copies = 10
+ nthreads = 1
+ ncycles = 25
+
+
+class TEST4(ObjDefragAdvancedMt):
+ max_nodes = 128
+ max_edges = 32
+ graph_copies = 10
+ nthreads = 10
+ ncycles = 25
+
+
+class TEST5(ObjDefragAdvancedMt):
+ max_nodes = 256
+ max_edges = 32
+ graph_copies = 5
+ nthreads = 10
+ ncycles = 25
+
+
+# a testcase designed to verify the pool content in case of fail
+# class TESTX(ObjDefragAdvanced):
+# def run(self, ctx):
+# path = '/custom/pool/path'
+# dump_prefix = 'dump'
+#
+# ctx.exec('obj_defrag_advanced',
+# 'op_pool_open', path,
+# 'op_graph_dump_all', dump_prefix,
+# 'op_pool_close')
diff --git a/src/test/obj_defrag_advanced/obj_defrag_advanced.c b/src/test/obj_defrag_advanced/obj_defrag_advanced.c
new file mode 100644
index 0000000000000000000000000000000000000000..7dc42161998e87b58aec492712b982df18d930dc
--- /dev/null
+++ b/src/test/obj_defrag_advanced/obj_defrag_advanced.c
@@ -0,0 +1,595 @@
+/*
+ * Copyright 2020, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * obj_defrag_advanced.c -- test for libpmemobj defragmentation feature
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "rand.h"
+#include "vgraph.h"
+#include "pgraph.h"
+#include "os_thread.h"
+#include "unittest.h"
+
+struct create_params_t {
+ uint64_t seed;
+ rng_t rng;
+
+ struct vgraph_params vparams;
+ struct pgraph_params pparams;
+};
+
+/*
+ * graph_create -- create a graph
+ * - generate an intermediate volatile graph representation
+ * - use the volatile graph to allocate a persistent one
+ */
+static void
+graph_create(struct create_params_t *task, PMEMobjpool *pop, PMEMoid *oidp,
+ rng_t *rngp)
+{
+ struct vgraph_t *vgraph = vgraph_new(&task->vparams, rngp);
+ pgraph_new(pop, oidp, vgraph, &task->pparams, rngp);
+ vgraph_delete(vgraph);
+}
+
+/*
+ * graph_defrag -- defragment the pool
+ * - collect pointers to all PMEMoids
+ * - do a sanity checks
+ * - call pmemobj_defrag
+ * - return # of relocated objects
+ */
+static size_t
+graph_defrag(PMEMobjpool *pop, PMEMoid oid)
+{
+ struct pgraph_t *pgraph = (struct pgraph_t *)pmemobj_direct(oid);
+
+ /* count number of oids */
+ unsigned oidcnt = pgraph->nodes_num;
+ for (unsigned i = 0; i < pgraph->nodes_num; ++i) {
+ struct pnode_t *pnode = (struct pnode_t *)pmemobj_direct
+ (pgraph->nodes[i]);
+ oidcnt += pnode->edges_num;
+ }
+
+ /* create array of oid pointers */
+ PMEMoid **oidv = (PMEMoid **)MALLOC(sizeof(PMEMoid *) * oidcnt);
+ unsigned oidi = 0;
+ for (unsigned i = 0; i < pgraph->nodes_num; ++i) {
+ oidv[oidi++] = &pgraph->nodes[i];
+
+ struct pnode_t *pnode = (struct pnode_t *)pmemobj_direct
+ (pgraph->nodes[i]);
+ for (unsigned j = 0; j < pnode->edges_num; ++j) {
+ oidv[oidi++] = &pnode->edges[j];
+ }
+ }
+
+ UT_ASSERTeq(oidi, oidcnt);
+
+ /* check if all oids are valid */
+ for (unsigned i = 0; i < oidcnt; ++i) {
+ void *ptr = pmemobj_direct(*oidv[i]);
+ UT_ASSERTne(ptr, NULL);
+ }
+
+ /* check if all oids appear only once */
+ for (unsigned i = 0; i < oidcnt - 1; ++i) {
+ for (unsigned j = i + 1; j < oidcnt; ++j) {
+ UT_ASSERTne(oidv[i], oidv[j]);
+ }
+ }
+
+ struct pobj_defrag_result result;
+ int ret = pmemobj_defrag(pop, oidv, oidcnt, &result);
+ UT_ASSERTeq(ret, 0);
+ UT_ASSERTeq(result.total, pgraph->nodes_num);
+
+ FREE(oidv);
+
+ return result.relocated;
+}
+
+/*
+ * graph_defrag_ntimes -- defragment the graph N times
+ * - where N <= max_rounds
+ * - it stops defrag if # of relocated objects == 0
+ */
+static void
+graph_defrag_ntimes(PMEMobjpool *pop, PMEMoid oid, unsigned max_rounds)
+{
+ size_t relocated;
+ unsigned rounds = 0;
+ do {
+ relocated = graph_defrag(pop, oid);
+ ++rounds;
+ } while (relocated > 0 && rounds < max_rounds);
+
+ UT_OUT("# of defragmentation rounds: %u", rounds);
+}
+
+#define HAS_TO_EXIST (1)
+
+/*
+ * graph_dump -- dump a graph from the pool to a text file
+ */
+static void
+graph_dump(PMEMoid oid, const char *path, int has_exist)
+{
+ struct pgraph_t *pgraph = (struct pgraph_t *)pmemobj_direct(oid);
+ if (has_exist)
+ UT_ASSERTne(pgraph, NULL);
+
+ if (pgraph)
+ pgraph_print(pgraph, path);
+}
+
+#define FGETS_BUFF_LEN 1024
+
+/*
+ * dump_compare -- compare graph dumps
+ * Test fails if the contents of dumps do not match
+ */
+static void
+dump_compare(const char *path1, const char *path2)
+{
+ FILE *dump1 = FOPEN(path1, "r");
+ FILE *dump2 = FOPEN(path2, "r");
+
+ char buff1[FGETS_BUFF_LEN];
+ char buff2[FGETS_BUFF_LEN];
+ char *sret1, *sret2;
+
+ do {
+ sret1 = fgets(buff1, FGETS_BUFF_LEN, dump1);
+ sret2 = fgets(buff2, FGETS_BUFF_LEN, dump2);
+
+ /* both files have to end at the same time */
+ if (!sret1) {
+ UT_ASSERTeq(sret2, NULL);
+
+ FCLOSE(dump1);
+ FCLOSE(dump2);
+
+ return;
+ }
+
+ UT_ASSERTeq(sret1, buff1);
+ UT_ASSERTeq(sret2, buff2);
+
+ UT_ASSERTeq(strcmp(buff1, buff2), 0);
+ } while (1);
+}
+
+/*
+ * create_params_init -- initialize create params
+ */
+static void
+create_params_init(struct create_params_t *params)
+{
+ params->seed = 1;
+
+ /* good enough defaults - no magic here */
+ params->vparams.max_nodes = 50;
+ params->vparams.max_edges = 10;
+ params->vparams.range_nodes = 10;
+ params->vparams.range_edges = 10;
+ params->vparams.min_pattern_size = 8;
+ params->vparams.max_pattern_size = 1024;
+
+ params->pparams.graph_copies = 10;
+}
+
+/* global state */
+static struct global_t {
+ PMEMobjpool *pop;
+} global;
+
+/*
+ * PMEMobj root object structure
+ */
+struct root_t {
+ unsigned graphs_num;
+ PMEMoid graphs[];
+};
+
+/*
+ * root_size -- calculate a root object size
+ */
+static inline size_t
+root_size(unsigned graph_num, size_t min_root_size)
+{
+ size_t size = sizeof(struct root_t) + sizeof(PMEMoid) * graph_num;
+ return MAX(size, min_root_size);
+}
+
+#define QUERY_GRAPHS_NUM UINT_MAX
+
+static struct root_t *
+get_root(unsigned graphs_num, size_t min_root_size)
+{
+ PMEMoid roid;
+ struct root_t *root;
+
+ if (graphs_num == QUERY_GRAPHS_NUM) {
+ /* allocate a root object without graphs */
+ roid = pmemobj_root(global.pop, root_size(0, 0));
+ if (OID_IS_NULL(roid))
+ UT_FATAL("!pmemobj_root:");
+ root = (struct root_t *)pmemobj_direct(roid);
+ UT_ASSERTne(root, NULL);
+
+ graphs_num = root->graphs_num;
+ }
+
+ UT_ASSERT(graphs_num > 0);
+
+ /* reallocate a root object with all known graphs */
+ roid = pmemobj_root(global.pop, root_size(graphs_num, min_root_size));
+ if (OID_IS_NULL(roid))
+ UT_FATAL("!pmemobj_root:");
+ root = (struct root_t *)pmemobj_direct(roid);
+ UT_ASSERTne(root, NULL);
+
+ return root;
+}
+
+/*
+ * parse_nonzero -- parse non-zero unsigned
+ */
+static void
+parse_nonzero(unsigned *var, const char *arg)
+{
+ unsigned long v = STRTOUL(arg, NULL, 10);
+ UT_ASSERTne(v, 0);
+ UT_ASSERT(v < UINT_MAX);
+
+ *var = v;
+}
+
+#define GRAPH_LAYOUT POBJ_LAYOUT_NAME(graph)
+
+/*
+ * op_pool_create -- create a pool
+ */
+static int
+op_pool_create(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 1)
+ UT_FATAL("usage: %s <path>", tc->name);
+
+ /* parse arguments */
+ const char *path = argv[0];
+
+ /* open a pool */
+ global.pop = pmemobj_create(path, GRAPH_LAYOUT, 0, S_IWUSR | S_IRUSR);
+ if (global.pop == NULL) {
+ UT_FATAL("!pmemobj_create: %s", path);
+ }
+
+ return 1;
+}
+
+/*
+ * op_pool_close -- close the poll
+ */
+static int
+op_pool_close(const struct test_case *tc, int argc, char *argv[])
+{
+ pmemobj_close(global.pop);
+ global.pop = NULL;
+
+ return 0;
+}
+
+/*
+ * op_graph_create -- create a graph
+ */
+static int
+op_graph_create(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 4)
+ UT_FATAL("usage: %s <max-nodes> <max-edges> <graph-copies>"
+ " <min-root-size>", tc->name);
+
+ /* parse arguments */
+ struct create_params_t cparams;
+ create_params_init(&cparams);
+ parse_nonzero(&cparams.vparams.max_nodes, argv[0]);
+ parse_nonzero(&cparams.vparams.max_edges, argv[1]);
+ parse_nonzero(&cparams.pparams.graph_copies, argv[2]);
+ size_t min_root_size = STRTOULL(argv[3], NULL, 10);
+
+ struct root_t *root = get_root(1, min_root_size);
+
+ randomize(cparams.seed);
+
+ /* generate a single graph */
+ graph_create(&cparams, global.pop, &root->graphs[0], NULL);
+ root->graphs_num = 1;
+ pmemobj_persist(global.pop, root, root_size(1, min_root_size));
+
+ return 4;
+}
+
+/*
+ * op_graph_dump -- dump the graph
+ */
+static int
+op_graph_dump(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 1)
+ UT_FATAL("usage: %s <dump>", tc->name);
+
+ /* parse arguments */
+ const char *dump = argv[0];
+
+ struct root_t *root = get_root(QUERY_GRAPHS_NUM, 0);
+ UT_ASSERTeq(root->graphs_num, 1);
+
+ /* dump the graph before defrag */
+ graph_dump(root->graphs[0], dump, HAS_TO_EXIST);
+
+ return 1;
+}
+
+/*
+ * op_graph_defrag -- defrag the graph
+ */
+static int
+op_graph_defrag(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 1)
+ UT_FATAL("usage: %s <max-rounds>", tc->name);
+
+ /* parse arguments */
+ unsigned max_rounds;
+ parse_nonzero(&max_rounds, argv[0]);
+
+ struct root_t *root = get_root(QUERY_GRAPHS_NUM, 0);
+ UT_ASSERTeq(root->graphs_num, 1);
+
+ /* do the defrag */
+ graph_defrag_ntimes(global.pop, root->graphs[0], max_rounds);
+
+ return 1;
+}
+
+/*
+ * op_dump_compare -- compare dumps
+ */
+static int
+op_dump_compare(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 2)
+ UT_FATAL("usage: %s <dump1> <dump2>", tc->name);
+
+ /* parse arguments */
+ const char *dump1 = argv[0];
+ const char *dump2 = argv[1];
+
+ dump_compare(dump1, dump2);
+
+ return 2;
+}
+
+struct create_n_defrag_params_t {
+ unsigned thread_id;
+
+ struct create_params_t cparams;
+
+ PMEMobjpool *pop;
+ PMEMoid *oidp;
+
+ unsigned max_rounds;
+ unsigned ncycles;
+};
+
+/*
+ * create_n_defrag_thread -- create and defrag graphs mutiple times
+ */
+static void *
+create_n_defrag_thread(void *arg)
+{
+ struct create_n_defrag_params_t *params =
+ (struct create_n_defrag_params_t *)arg;
+
+ char dump1[PATH_MAX];
+ char dump2[PATH_MAX];
+
+ snprintf(dump1, PATH_MAX, "dump_t%u_1.log", params->thread_id);
+ snprintf(dump2, PATH_MAX, "dump_t%u_2.log", params->thread_id);
+
+ struct create_params_t *cparams = ¶ms->cparams;
+
+ for (unsigned i = 0; i < params->ncycles; ++i) {
+ graph_create(cparams, global.pop, params->oidp, &cparams->rng);
+ graph_dump(*params->oidp, dump1, HAS_TO_EXIST);
+
+ graph_defrag_ntimes(params->pop, *params->oidp,
+ params->max_rounds);
+ graph_dump(*params->oidp, dump2, HAS_TO_EXIST);
+
+ dump_compare(dump1, dump2);
+
+ pgraph_delete(params->oidp);
+ }
+
+ return NULL;
+}
+
+/*
+ * op_graph_create_n_defrag_mt -- multi-threaded graphs creation & defrag
+ */
+static int
+op_graph_create_n_defrag_mt(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 7)
+ UT_FATAL("usage: %s <max-nodes> <max-edges> <graph-copies>"
+ " <min-root-size> <max-defrag-rounds> <n-threads>"
+ "<n-create-defrag-cycles>", tc->name);
+
+ /* parse arguments */
+ struct create_params_t cparams;
+ create_params_init(&cparams);
+ parse_nonzero(&cparams.vparams.max_nodes, argv[0]);
+ parse_nonzero(&cparams.vparams.max_edges, argv[1]);
+ parse_nonzero(&cparams.pparams.graph_copies, argv[2]);
+ size_t min_root_size = STRTOULL(argv[3], NULL, 10);
+ unsigned max_rounds;
+ parse_nonzero(&max_rounds, argv[4]);
+ unsigned nthreads;
+ parse_nonzero(&nthreads, argv[5]);
+ unsigned ncycles;
+ parse_nonzero(&ncycles, argv[6]);
+
+ struct root_t *root = get_root(nthreads, min_root_size);
+ root->graphs_num = nthreads;
+ pmemobj_persist(global.pop, root, sizeof(*root));
+
+ /* prepare threads params */
+ struct create_n_defrag_params_t *paramss =
+ (struct create_n_defrag_params_t *)MALLOC(
+ sizeof(*paramss) * nthreads);
+
+ for (unsigned i = 0; i < nthreads; ++i) {
+ struct create_n_defrag_params_t *params = ¶mss[i];
+
+ params->thread_id = i;
+ memcpy(¶ms->cparams, &cparams, sizeof(cparams));
+ params->cparams.seed += i;
+ randomize_r(¶ms->cparams.rng, params->cparams.seed);
+ params->pop = global.pop;
+ params->oidp = &root->graphs[i];
+ params->max_rounds = max_rounds;
+ params->ncycles = ncycles;
+ }
+
+ /* spawn threads */
+ os_thread_t *threads = (os_thread_t *)MALLOC(
+ sizeof(*threads) * nthreads);
+ for (unsigned i = 0; i < nthreads; ++i)
+ os_thread_create(&threads[i], NULL, create_n_defrag_thread,
+ ¶mss[i]);
+
+ /* join all threads */
+ void *ret = NULL;
+ for (unsigned i = 0; i < nthreads; ++i) {
+ os_thread_join(&threads[i], &ret);
+ UT_ASSERTeq(ret, NULL);
+ }
+
+ FREE(threads);
+ FREE(paramss);
+
+ return 7;
+}
+
+/*
+ * op_pool_open -- open the pool
+ */
+static int
+op_pool_open(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 1)
+ UT_FATAL("usage: %s <path>", tc->name);
+
+ /* parse arguments */
+ const char *path = argv[0];
+
+ /* open a pool */
+ global.pop = pmemobj_open(path, GRAPH_LAYOUT);
+ if (global.pop == NULL)
+ UT_FATAL("!pmemobj_create: %s", path);
+
+ return 1;
+}
+
+/*
+ * op_graph_dump_all -- dump all graphs
+ */
+static int
+op_graph_dump_all(const struct test_case *tc, int argc, char *argv[])
+{
+ if (argc < 1)
+ UT_FATAL("usage: %s <dump-prefix>", tc->name);
+
+ /* parse arguments */
+ const char *dump_prefix = argv[0];
+
+ struct root_t *root = get_root(QUERY_GRAPHS_NUM, 0);
+
+ char dump[PATH_MAX];
+ for (unsigned i = 0; i < root->graphs_num; ++i) {
+ snprintf(dump, PATH_MAX, "%s_%u.log", dump_prefix, i);
+ graph_dump(root->graphs[i], dump, HAS_TO_EXIST);
+ }
+
+ return 1;
+}
+
+/*
+ * ops -- available ops
+ */
+static struct test_case ops[] = {
+ TEST_CASE(op_pool_create),
+ TEST_CASE(op_pool_close),
+ TEST_CASE(op_graph_create),
+ TEST_CASE(op_graph_dump),
+ TEST_CASE(op_graph_defrag),
+ TEST_CASE(op_dump_compare),
+ TEST_CASE(op_graph_create_n_defrag_mt),
+
+ /* for pool validation only */
+ TEST_CASE(op_pool_open),
+ TEST_CASE(op_graph_dump_all),
+};
+
+#define NOPS ARRAY_SIZE(ops)
+
+#define TEST_NAME "obj_defrag_advanced"
+
+int
+main(int argc, char *argv[])
+{
+ START(argc, argv, TEST_NAME);
+ TEST_CASE_PROCESS(argc, argv, ops, NOPS);
+ DONE(NULL);
+}
diff --git a/src/test/obj_defrag_advanced/obj_defrag_advanced.vcxproj b/src/test/obj_defrag_advanced/obj_defrag_advanced.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..48e76ed69c40701c6bd8da64366fdfc55222e9b0
--- /dev/null
+++ b/src/test/obj_defrag_advanced/obj_defrag_advanced.vcxproj
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{AE952763-5C84-43FC-B344-CACC950F056C}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>obj_defrag_advanced</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile />
+ <Link />
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile />
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\test_debug.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\test_release.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <IncludePath>$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <Optimization>Disabled</Optimization>
+ </ClCompile>
+ <Link />
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ </ClCompile>
+ <Link />
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\libpmemobj\libpmemobj.vcxproj">
+ <Project>{1baa1617-93ae-4196-8a1a-bd492fb18aef}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\libpmem\libpmem.vcxproj">
+ <Project>{9e9e3d25-2139-4a5d-9200-18148ddead45}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\windows\getopt\getopt.vcxproj">
+ <Project>{9186eac4-2f34-4f17-b940-6585d7869bcd}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\unittest\libut.vcxproj">
+ <Project>{ce3f2dfb-8470-4802-ad37-21caf6cb2681}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="TESTS.py" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="obj_defrag_advanced.c" />
+ <ClCompile Include="pgraph.c" />
+ <ClCompile Include="vgraph.c" />
+ <ClCompile Include="..\..\common\rand.c" />
+ <ClCompile Include="..\..\common\os_thread_windows.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="pgraph.h" />
+ <ClInclude Include="vgraph.h" />
+ <ClInclude Include="..\..\common\os_thread.h" />
+ <ClInclude Include="..\..\common\rand.h" />
+ <ClInclude Include="..\unittest\unittest.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/src/test/obj_defrag_advanced/obj_defrag_advanced.vcxproj.filters b/src/test/obj_defrag_advanced/obj_defrag_advanced.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..5a2693130b3d0447ebeaff2672fe5edc1537cad7
--- /dev/null
+++ b/src/test/obj_defrag_advanced/obj_defrag_advanced.vcxproj.filters
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <None Include="README" />
+ <None Include="TESTS.py">
+ <Filter>Test Scripts</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{06b70714-04ff-4d74-8f1d-eb33a3318a3c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{06b70714-04ff-4d74-8f1d-eb33a3318a3c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Test Scripts">
+ <UniqueIdentifier>{f6970093-c229-475a-a462-7718dc17b32e}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="obj_defrag_advanced.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="pgraph.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vgraph.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\common\rand.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\common\os_thread_windows.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="pgraph.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vgraph.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\common\os_thread.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\common\rand.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\unittest\unittest.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/test/obj_defrag_advanced/pgraph.c b/src/test/obj_defrag_advanced/pgraph.c
new file mode 100644
index 0000000000000000000000000000000000000000..e7a933ed030cf575c5cca9de123af19d0950a882
--- /dev/null
+++ b/src/test/obj_defrag_advanced/pgraph.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2020, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * pgraph.c -- persistent graph representation
+ */
+
+#include <inttypes.h>
+
+#include "unittest.h"
+
+#include "vgraph.h"
+#include "pgraph.h"
+
+#define PATTERN 'g'
+
+/*
+ * pnode_size -- return the entire of node size
+ */
+static size_t
+pnode_size(unsigned edges_num, size_t pattern_size)
+{
+ size_t node_size = sizeof(struct pnode_t);
+ node_size += sizeof(PMEMoid) * edges_num;
+ node_size += pattern_size;
+ return node_size;
+}
+
+/*
+ * pnode_init -- initialize the node
+ */
+static void
+pnode_init(PMEMobjpool *pop, PMEMoid pnode_oid, struct vnode_t *vnode,
+ PMEMoid pnodes[])
+{
+ struct pnode_t *pnode = (struct pnode_t *)pmemobj_direct(pnode_oid);
+ pnode->node_id = vnode->node_id;
+ pnode->size = vnode->psize;
+
+ /* set edges */
+ pnode->edges_num = vnode->edges_num;
+ for (unsigned i = 0; i < vnode->edges_num; ++i)
+ pnode->edges[i] = pnodes[vnode->edges[i]];
+
+ /* initialize pattern */
+ pnode->pattern_size = vnode->pattern_size;
+ void *pattern = (void *)&pnode->edges[pnode->edges_num];
+ pmemobj_memset(pop, pattern, PATTERN, pnode->pattern_size,
+ PMEMOBJ_F_MEM_NOFLUSH);
+
+ /* persist the whole node state */
+ pmemobj_persist(pop, (const void *)pnode, pnode->size);
+}
+
+/*
+ * order_shuffle -- shuffle the nodes in graph
+ */
+static void
+order_shuffle(unsigned *order, unsigned num, rng_t *rngp)
+{
+ for (unsigned i = 0; i < num; ++i) {
+ unsigned j = rand_range(0, num, rngp);
+ unsigned temp = order[j];
+ order[j] = order[i];
+ order[i] = temp;
+ }
+}
+
+/*
+ * order_new -- generate the sequence of the graph nodes allocation
+ */
+static unsigned *
+order_new(struct vgraph_t *vgraph, rng_t *rngp)
+{
+ unsigned *order = (unsigned *)MALLOC(sizeof(unsigned)
+ * vgraph->nodes_num);
+
+ /* initialize id list */
+ for (unsigned i = 0; i < vgraph->nodes_num; ++i)
+ order[i] = i;
+
+ order_shuffle(order, vgraph->nodes_num, rngp);
+
+ return order;
+}
+
+/*
+ * pgraph_copy_new -- allocate a persistent copy of the volatile graph
+ */
+static PMEMoid *
+pgraph_copy_new(PMEMobjpool *pop, struct vgraph_t *vgraph, rng_t *rngp)
+{
+ /* to be returned array of PMEMoids to raw nodes allocations */
+ PMEMoid *nodes = (PMEMoid *)MALLOC(sizeof(PMEMoid) * vgraph->nodes_num);
+
+ /* generates random order of nodes allocation */
+ unsigned *order = order_new(vgraph, rngp);
+
+ /* allocate the nodes in the random order */
+ int ret;
+ for (unsigned i = 0; i < vgraph->nodes_num; ++i) {
+ struct vnode_t vnode = vgraph->node[order[i]];
+ PMEMoid *node = &nodes[order[i]];
+ ret = pmemobj_alloc(pop, node, vnode.psize, 0, NULL, NULL);
+ UT_ASSERTeq(ret, 0);
+ }
+
+ FREE(order);
+
+ return nodes;
+}
+
+/*
+ * pgraph_copy_delete -- free copies of the graph
+ */
+static void
+pgraph_copy_delete(PMEMoid *nodes, unsigned num)
+{
+ for (unsigned i = 0; i < num; ++i) {
+ if (OID_IS_NULL(nodes[i]))
+ continue;
+
+ pmemobj_free(&nodes[i]);
+ }
+
+ FREE(nodes);
+}
+
+/*
+ * pgraph_size -- return the struct pgraph_t size
+ */
+static size_t
+pgraph_size(unsigned nodes_num)
+{
+ return sizeof(struct pgraph_t) + sizeof(PMEMoid) * nodes_num;
+}
+
+/*
+ * pgraph_new -- allocate a new persistent graph in such a way
+ * that the fragmentation is as large as possible
+ */
+void
+pgraph_new(PMEMobjpool *pop, PMEMoid *oidp, struct vgraph_t *vgraph,
+ struct pgraph_params *params, rng_t *rngp)
+{
+ int ret = pmemobj_alloc(pop, oidp, pgraph_size(vgraph->nodes_num),
+ 0, NULL, NULL);
+ UT_ASSERTeq(ret, 0);
+
+ struct pgraph_t *pgraph = (struct pgraph_t *)pmemobj_direct(*oidp);
+ pgraph->nodes_num = vgraph->nodes_num;
+ pmemobj_persist(pop, pgraph, sizeof(*pgraph));
+
+ /* calculate size of pnodes */
+ for (unsigned i = 0; i < vgraph->nodes_num; ++i) {
+ struct vnode_t *vnode = &vgraph->node[i];
+ vnode->psize = pnode_size(vnode->edges_num,
+ vnode->pattern_size);
+ }
+
+ /* prepare multiple copies of the nodes */
+ unsigned copies_num = rand_range(1, params->graph_copies, rngp);
+ PMEMoid **copies = (PMEMoid **)MALLOC(sizeof(PMEMoid *) * copies_num);
+ for (unsigned i = 0; i < copies_num; ++i)
+ copies[i] = pgraph_copy_new(pop, vgraph, rngp);
+
+ /* peek exactly the one copy of each node */
+ for (unsigned i = 0; i < pgraph->nodes_num; ++i) {
+ unsigned copy_id = rand_range(0, copies_num, rngp);
+ pgraph->nodes[i] = copies[copy_id][i];
+ copies[copy_id][i] = OID_NULL;
+ }
+ pmemobj_persist(pop, pgraph->nodes,
+ sizeof(PMEMoid) * pgraph->nodes_num);
+
+ /* free unused copies of the nodes */
+ for (unsigned i = 0; i < copies_num; ++i)
+ pgraph_copy_delete(copies[i], vgraph->nodes_num);
+
+ FREE(copies);
+
+ /* initialize pnodes */
+ for (unsigned i = 0; i < pgraph->nodes_num; ++i)
+ pnode_init(pop, pgraph->nodes[i], &vgraph->node[i],
+ pgraph->nodes);
+}
+
+/*
+ * pgraph_delete -- free the persistent graph
+ */
+void
+pgraph_delete(PMEMoid *oidp)
+{
+ struct pgraph_t *pgraph = (struct pgraph_t *)pmemobj_direct(*oidp);
+
+ /* free pnodes */
+ for (unsigned i = 0; i < pgraph->nodes_num; ++i)
+ pmemobj_free(&pgraph->nodes[i]);
+
+ pmemobj_free(oidp);
+}
+
+/*
+ * pgraph_print -- print graph in human readable format
+ */
+void
+pgraph_print(struct pgraph_t *pgraph, const char *dump)
+{
+ UT_ASSERTne(dump, NULL);
+
+ FILE *out = FOPEN(dump, "w");
+
+ /* print the graph statistics */
+ fprintf(out, "# of nodes: %u\n", pgraph->nodes_num);
+
+ uint64_t total_edges_num = 0;
+ for (unsigned i = 0; i < pgraph->nodes_num; ++i) {
+ PMEMoid node_oid = pgraph->nodes[i];
+ struct pnode_t *pnode =
+ (struct pnode_t *)pmemobj_direct(node_oid);
+ total_edges_num += pnode->edges_num;
+ }
+ fprintf(out, "Total # of edges: %" PRIu64 "\n\n", total_edges_num);
+
+ /* print the graph itself */
+ for (unsigned i = 0; i < pgraph->nodes_num; ++i) {
+ PMEMoid node_oid = pgraph->nodes[i];
+ struct pnode_t *pnode =
+ (struct pnode_t *)pmemobj_direct(node_oid);
+ fprintf(out, "%u:", pnode->node_id);
+ for (unsigned j = 0; j < pnode->edges_num; ++j) {
+ PMEMoid edge_oid = pnode->edges[j];
+ struct pnode_t *edge =
+ (struct pnode_t *)pmemobj_direct(edge_oid);
+ UT_ASSERT(edge->node_id < pgraph->nodes_num);
+ fprintf(out, "%u, ", edge->node_id);
+ }
+ fprintf(out, "\n");
+ }
+
+ FCLOSE(out);
+}
diff --git a/src/test/obj_defrag_advanced/pgraph.h b/src/test/obj_defrag_advanced/pgraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..ebe639ece1122b46d14e5527a619cd68f7186706
--- /dev/null
+++ b/src/test/obj_defrag_advanced/pgraph.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * pgraph.h -- persistent graph representation
+ */
+
+#ifndef OBJ_DEFRAG_ADV_PGRAPH
+#define OBJ_DEFRAG_ADV_PGRAPH
+
+#include <libpmemobj/base.h>
+
+struct pgraph_params
+{
+ unsigned graph_copies;
+};
+
+struct pnode_t
+{
+ unsigned node_id;
+ unsigned edges_num;
+ size_t pattern_size;
+ size_t size;
+ PMEMoid edges[];
+};
+
+struct pgraph_t
+{
+ unsigned nodes_num;
+ PMEMoid nodes[];
+};
+
+void pgraph_new(PMEMobjpool *pop, PMEMoid *oidp, struct vgraph_t *vgraph,
+ struct pgraph_params *params, rng_t *rngp);
+void pgraph_delete(PMEMoid *oidp);
+
+void pgraph_print(struct pgraph_t *graph, const char *dump);
+
+#endif /* pgraph.h */
diff --git a/src/test/obj_defrag_advanced/vgraph.c b/src/test/obj_defrag_advanced/vgraph.c
new file mode 100644
index 0000000000000000000000000000000000000000..c05b2f8377426f72b70cdad19c6a9ee93c6e52b2
--- /dev/null
+++ b/src/test/obj_defrag_advanced/vgraph.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2020, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * vgraph.c -- volatile graph representation
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "rand.h"
+#include "unittest.h"
+#include "vgraph.h"
+
+/*
+ * rand_range -- generate pseudo-random number from given interval [min, max]
+ */
+unsigned
+rand_range(unsigned min, unsigned max, rng_t *rngp)
+{
+ if (min == max)
+ return min;
+
+ if (min > max)
+ UT_FATAL("!rand_range");
+
+ unsigned ret;
+ if (rngp)
+ ret = (unsigned)rnd64_r(rngp);
+ else
+ ret = (unsigned)rnd64();
+
+ return ((unsigned)ret % (max - min)) + min;
+}
+
+/*
+ * vnode_new -- allocate a new volatile node
+ */
+static void
+vnode_new(struct vnode_t *node, unsigned v, struct vgraph_params *params,
+ rng_t *rngp)
+{
+ unsigned min_edges = 1;
+ if (params->max_edges > params->range_edges)
+ min_edges = params->max_edges - params->range_edges;
+ unsigned edges_num = rand_range(min_edges, params->max_edges, rngp);
+ node->node_id = v;
+ node->edges_num = edges_num;
+ node->edges = (unsigned *)MALLOC(sizeof(int) * edges_num);
+ node->pattern_size = rand_range(params->min_pattern_size,
+ params->max_pattern_size, rngp);
+}
+
+/*
+ * vnode_delete -- free a volatile node
+ */
+static void
+vnode_delete(struct vnode_t *node)
+{
+ FREE(node->edges);
+}
+
+/*
+ * vgraph_get_node -- return node in graph based on given id_node
+ */
+static struct vnode_t *
+vgraph_get_node(struct vgraph_t *graph, unsigned id_node)
+{
+ struct vnode_t *node;
+
+ node = &graph->node[id_node];
+ return node;
+}
+
+/*
+ * vgraph_add_edges -- randomly assign destination nodes to the edges
+ */
+static void
+vgraph_add_edges(struct vgraph_t *graph, rng_t *rngp)
+{
+ unsigned nodes_count = 0;
+ unsigned edges_count = 0;
+ struct vnode_t *node;
+ for (nodes_count = 0; nodes_count < graph->nodes_num; nodes_count++) {
+ node = vgraph_get_node(graph, nodes_count);
+ unsigned edges_num = node->edges_num;
+ for (edges_count = 0; edges_count < edges_num; edges_count++) {
+ unsigned node_link =
+ rand_range(0, graph->nodes_num, rngp);
+ node->edges[edges_count] = node_link;
+ }
+ }
+}
+
+/*
+ * vgraph_new -- allocate a new volatile graph
+ */
+struct vgraph_t *
+vgraph_new(struct vgraph_params *params, rng_t *rngp)
+{
+ unsigned min_nodes = 1;
+ if (params->max_nodes > params->range_nodes)
+ min_nodes = params->max_nodes - params->range_nodes;
+ unsigned nodes_num = rand_range(min_nodes, params->max_nodes, rngp);
+
+ struct vgraph_t *graph =
+ (struct vgraph_t *)MALLOC(sizeof(struct vgraph_t) +
+ sizeof(struct vnode_t) * nodes_num);
+ graph->nodes_num = nodes_num;
+
+ for (unsigned i = 0; i < nodes_num; i++) {
+ vnode_new(&graph->node[i], i, params, rngp);
+ }
+
+ vgraph_add_edges(graph, rngp);
+
+ return graph;
+}
+
+/*
+ * vgraph_delete -- free the volatile graph
+ */
+void
+vgraph_delete(struct vgraph_t *graph)
+{
+ for (unsigned i = 0; i < graph->nodes_num; i++)
+ vnode_delete(&graph->node[i]);
+
+ FREE(graph);
+}
diff --git a/src/test/obj_defrag_advanced/vgraph.h b/src/test/obj_defrag_advanced/vgraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..25648c270ecd6148fcaf98de1c0a8c1d8d4dedf2
--- /dev/null
+++ b/src/test/obj_defrag_advanced/vgraph.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2020, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * vgraph.h -- volatile graph representation
+ */
+
+#ifndef OBJ_DEFRAG_ADV_VGRAPH
+#define OBJ_DEFRAG_ADV_VGRAPH
+
+#include "rand.h"
+
+struct vgraph_params
+{
+ unsigned max_nodes; /* max # of nodes per graph */
+ unsigned max_edges; /* max # of edges per node */
+ /* # of nodes is between [max_nodes - range_nodes, max_nodes] */
+ unsigned range_nodes;
+ /* # of edges is between [max_edges - range_edges, max_edges] */
+ unsigned range_edges;
+ unsigned min_pattern_size;
+ unsigned max_pattern_size;
+};
+
+struct vnode_t
+{
+ unsigned node_id;
+ unsigned edges_num; /* # of edges starting from this node */
+ unsigned *edges; /* ids of nodes the edges are pointing to */
+
+ /* the persistent node attributes */
+ size_t pattern_size; /* size of the pattern allocated after the node */
+ size_t psize; /* the total size of the node */
+};
+
+struct vgraph_t
+{
+ unsigned nodes_num;
+ struct vnode_t node[];
+};
+
+unsigned rand_range(unsigned min, unsigned max, rng_t *rngp);
+
+struct vgraph_t *vgraph_new(struct vgraph_params *params, rng_t *rngp);
+void vgraph_delete(struct vgraph_t *graph);
+
+#endif