commit - 3a3890ed9389c83a0fcf7e061b23ae947da9d4a2
commit + ab24dfb65b45ab372ccbf4831dde80d3887e686b
blob - d0669149f10e188ce871d8e6d439b7dfbe5fa713
blob + f211875151b1d763889f86cca28d7886666b881f
--- src/box/allocator.h
+++ src/box/allocator.h
{
return &small_alloc;
}
+ static inline void
+ get_alloc_info(void *ptr, size_t size, struct small_alloc_info *info)
+ {
+ small_alloc_info(&small_alloc, ptr, size, info);
+ }
private:
static struct small_alloc small_alloc;
};
blob - 1fed5050603e696114efa4e331832cf142644671
blob + 5eedde8721c779c92bc8745d4b896a661336b5fd
--- src/box/lua/tuple.c
+++ src/box/lua/tuple.c
assert(luaT_tuple_gc_ref != LUA_NOREF);
lua_rawgeti(L, LUA_REGISTRYINDEX, luaT_tuple_gc_ref);
luaL_setcdatagc(L, -2);
+}
+
+/**
+ * Push to Lua stack a table with the information about a tuple, located on top
+ * of the stack.
+ */
+static int
+lbox_tuple_info(lua_State *L)
+{
+ int argc = lua_gettop(L);
+ if (argc != 1)
+ luaL_error(L, "Usage: tuple:info()");
+
+ struct tuple *tuple = luaT_checktuple(L, 1);
+ struct tuple_info info;
+ tuple_info(tuple, &info);
+
+ lua_newtable(L);
+
+ lua_pushnumber(L, info.data_size);
+ lua_setfield(L, -2, "data_size");
+
+ lua_pushnumber(L, info.header_size);
+ lua_setfield(L, -2, "header_size");
+
+ lua_pushnumber(L, info.field_map_size);
+ lua_setfield(L, -2, "field_map_size");
+
+ lua_pushnumber(L, info.waste_size);
+ lua_setfield(L, -2, "waste_size");
+
+ lua_pushstring(L, tuple_arena_type_strs[info.arena_type]);
+ lua_setfield(L, -2, "arena");
+
+ return 1;
}
static const struct luaL_Reg lbox_tuple_meta[] = {
{"tuple_to_map", lbox_tuple_to_map},
{"tuple_field_by_path", lbox_tuple_field_by_path},
{"new", lbox_tuple_new},
+ {"info", lbox_tuple_info},
{NULL, NULL}
};
blob - 584fe32ab1a69a8b177e49bcd12cdc583f5b4066
blob + 5f63acbd650cabfb351b87ac95e14445a8c684d5
--- src/box/lua/tuple.lua
+++ src/box/lua/tuple.lua
["upsert"] = tuple_upsert;
["bsize"] = tuple_bsize;
["tomap"] = internal.tuple.tuple_to_map;
+ ["info"] = internal.tuple.info;
}
-- Aliases for tuple:methods().
blob - 7a1359778c41b7e054ca6f03fc10172f78d2472d
blob + 934da1fc806851ec1ab17eb4561d6bad230229ad
--- src/box/memtx_engine.cc
+++ src/box/memtx_engine.cc
assert(tuple_is_unreferenced(tuple));
MemtxAllocator<ALLOC>::free_tuple(tuple);
tuple_format_unref(format);
+}
+
+/** Fill `info' with the information about the `tuple'. */
+template<class ALLOC>
+static inline void
+memtx_tuple_info(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *info);
+
+template<>
+inline void
+memtx_tuple_info<SmallAlloc>(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *info)
+{
+ (void)format;
+ size_t size = tuple_size(tuple);
+ struct small_alloc_info alloc_info;
+ SmallAlloc::get_alloc_info(tuple, size, &alloc_info);
+
+ info->data_size = tuple_bsize(tuple);
+ info->header_size = sizeof(struct tuple);
+ if (tuple_is_compact(tuple))
+ info->header_size -= TUPLE_COMPACT_SAVINGS;
+ info->field_map_size = tuple_data_offset(tuple) - info->header_size;
+ if (alloc_info.is_large) {
+ info->waste_size = 0;
+ info->arena_type = TUPLE_ARENA_MALLOC;
+ } else {
+ info->waste_size = alloc_info.real_size - size;
+ info->arena_type = TUPLE_ARENA_MEMTX;
+ }
}
+template<>
+inline void
+memtx_tuple_info<SysAlloc>(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *info)
+{
+ (void)format;
+ info->data_size = tuple_bsize(tuple);
+ info->header_size = sizeof(struct tuple);
+ if (tuple_is_compact(tuple))
+ info->header_size -= TUPLE_COMPACT_SAVINGS;
+ info->field_map_size = tuple_data_offset(tuple) - info->header_size;
+ info->waste_size = 0;
+ info->arena_type = TUPLE_ARENA_MALLOC;
+}
+
struct tuple_format_vtab memtx_tuple_format_vtab;
-template <class ALLOC>
+template<class ALLOC>
static inline void
create_memtx_tuple_format_vtab(struct tuple_format_vtab *vtab)
{
vtab->tuple_delete = memtx_tuple_delete<ALLOC>;
vtab->tuple_new = memtx_tuple_new<ALLOC>;
+ vtab->tuple_info = memtx_tuple_info<ALLOC>;
}
/**
blob - 30277a186c661f80df06ccb085d22e1d1f8f3c2c
blob + 70041187336339c66167fca710bed42b892416d2
--- src/box/tuple.c
+++ src/box/tuple.c
OBJSIZE_MIN = 16,
};
+const char *tuple_arena_type_strs[tuple_arena_type_MAX] = {
+ [TUPLE_ARENA_MEMTX] = "memtx",
+ [TUPLE_ARENA_MALLOC] = "malloc",
+ [TUPLE_ARENA_RUNTIME] = "runtime",
+};
+
/**
* Storage for additional reference counter of a tuple.
*/
static struct tuple *
runtime_tuple_new(struct tuple_format *format, const char *data, const char *end);
+/** Fill `tuple_info'. */
+static void
+runtime_tuple_info(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *tuple_info);
+
/** A virtual method table for tuple_format_runtime */
static struct tuple_format_vtab tuple_format_runtime_vtab = {
runtime_tuple_delete,
runtime_tuple_new,
+ runtime_tuple_info,
};
static struct tuple *
size_t total = tuple_size(tuple);
tuple_format_unref(format);
smfree(&runtime_alloc, tuple, total);
+}
+
+static void
+runtime_tuple_info(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *tuple_info)
+{
+ assert(format->vtab.tuple_delete ==
+ tuple_format_runtime_vtab.tuple_delete);
+ (void)format;
+
+ uint16_t data_offset = tuple_data_offset(tuple);
+ tuple_info->data_size = tuple_bsize(tuple);
+ tuple_info->header_size = sizeof(struct tuple);
+ if (tuple_is_compact(tuple))
+ tuple_info->header_size -= TUPLE_COMPACT_SAVINGS;
+ tuple_info->field_map_size = data_offset - tuple_info->header_size;
+ tuple_info->waste_size = 0;
+ tuple_info->arena_type = TUPLE_ARENA_RUNTIME;
}
int
blob - be32936723e40b2a244ea7588672603b9228514d
blob + f151e2b75e0f0f0d016efaedcc24d5010106d825
--- src/box/tuple.h
+++ src/box/tuple.h
};
static_assert(sizeof(struct tuple) == 10, "Just to be sure");
+
+/** Type of the arena where the tuple is allocated. */
+enum tuple_arena_type {
+ TUPLE_ARENA_MEMTX = 0,
+ TUPLE_ARENA_MALLOC = 1,
+ TUPLE_ARENA_RUNTIME = 2,
+ tuple_arena_type_MAX
+};
+
+/** Arena type names. */
+extern const char *tuple_arena_type_strs[tuple_arena_type_MAX];
+
+/** Information about the tuple. */
+struct tuple_info {
+ /** Size of the MsgPack data. See also tuple_bsize(). */
+ size_t data_size;
+ /** Header size depends on the engine and on the compact/bulky mode. */
+ size_t header_size;
+ /** Size of the field_map. See also field_map_build_size(). */
+ size_t field_map_size;
+ /**
+ * The amount of excess memory used to store the tuple in mempool.
+ * Note that this value is calculated not during the actual allocation,
+ * but afterwards. This means that it can be incorrect if the state of
+ * the allocator changed. See also small_alloc_info().
+ */
+ size_t waste_size;
+ /** Type of the arena where the tuple is allocated. */
+ enum tuple_arena_type arena_type;
+};
+
+/** Fill `info' with the information about the `tuple'. */
+static inline void
+tuple_info(struct tuple *tuple, struct tuple_info *info)
+{
+ struct tuple_format *format = tuple_format_by_id(tuple->format_id);
+ format->vtab.tuple_info(format, tuple, info);
+}
static_assert(DIV_ROUND_UP(tuple_flag_MAX, 8) <=
sizeof(((struct tuple *)0)->flags),
blob - 8c37f36a6f3f267535f40d94a48fa4efe8c42ab6
blob + d261e06e1b39ca4a54947ede9c51f9316df11c46
--- src/box/tuple_format.h
+++ src/box/tuple_format.h
enum { TUPLE_OFFSET_SLOT_NIL = INT32_MAX };
struct tuple;
+struct tuple_info;
struct tuple_format;
struct coll;
struct Expr;
struct tuple*
(*tuple_new)(struct tuple_format *format, const char *data,
const char *end);
+ /**
+ * Fill `tuple_info' with the engine-specific and allocator-specific
+ * information about the `tuple'.
+ */
+ void
+ (*tuple_info)(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *tuple_info);
};
struct tuple_constraint;
blob - b9eabbbdc1a6af1b25183ed000a85d8160d1de33
blob + 56be337bfc25016d8278b1b50313118f8703b0d3
--- src/box/vy_stmt.c
+++ src/box/vy_stmt.c
free(tuple);
}
+/** Fill `tuple_info'. */
+static void
+vy_tuple_info(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *tuple_info)
+{
+ (void)format;
+ uint16_t data_offset = tuple_data_offset(tuple);
+ tuple_info->data_size = tuple_bsize(tuple);
+ tuple_info->header_size = sizeof(struct vy_stmt);
+ tuple_info->field_map_size = data_offset - tuple_info->header_size;
+ tuple_info->waste_size = 0;
+ tuple_info->arena_type = TUPLE_ARENA_MALLOC;
+}
+
void
vy_stmt_env_create(struct vy_stmt_env *env)
{
env->tuple_format_vtab.tuple_new = vy_tuple_new;
env->tuple_format_vtab.tuple_delete = vy_tuple_delete;
+ env->tuple_format_vtab.tuple_info = vy_tuple_info;
env->max_tuple_size = 1024 * 1024;
env->sum_tuple_size = 0;
env->key_format = vy_simple_stmt_format_new(env, NULL, 0);
blob - /dev/null
blob + 3318921b4a0dd940446e4b2726c7227f69859567 (mode 644)
--- /dev/null
+++ test/box-luatest/gh_6762_tuple_and_space_size_test.lua
+local t = require('luatest')
+local tarantool = require('tarantool')
+local server = require('luatest.server')
+
+-- If ASAN is enabled, the information about memory allocation is different.
+-- There is no sense in testing it.
+local function skip_if_asan_is_enabled()
+ t.skip_if(tarantool.build.asan)
+end
+
+local function after_all(cg)
+ cg.server:drop()
+end
+
+local function after_each(cg)
+ cg.server:exec(function()
+ if box.space.memtx then
+ box.space.memtx:drop()
+ end
+ if box.space.vinyl then
+ box.space.vinyl:drop()
+ end
+ end)
+end
+
+local g1 = t.group('gh-6762-tuple-info')
+
+g1.before_all(function(cg)
+ local box_cfg = {
+ slab_alloc_factor = 1.3,
+ slab_alloc_granularity = 32,
+ memtx_max_tuple_size = 1200*1000
+ }
+ cg.server = server:new{box_cfg = box_cfg}
+ cg.server:start()
+end)
+g1.after_all(after_all)
+g1.after_each(after_each)
+
+-- Test info() method of a runtime tuple.
+g1.test_tuple_info_runtime = function(cg)
+ cg.server:exec(function()
+ -- Compact form of a tuple.
+ t.assert_equals(box.tuple.new{string.rep('c', 252)}:info(),
+ { data_size = 255,
+ header_size = 6,
+ field_map_size = 0,
+ waste_size = 0,
+ arena = "runtime" }
+ )
+ -- Bulky form of a tuple.
+ t.assert_equals(box.tuple.new{string.rep('b', 253)}:info(),
+ { data_size = 256,
+ header_size = 10,
+ field_map_size = 0,
+ waste_size = 0,
+ arena = "runtime" }
+ )
+ end)
+end
+
+-- Test info() method of a memtx tuple.
+g1.test_tuple_info_memtx = function(cg)
+ skip_if_asan_is_enabled()
+ cg.server:exec(function()
+ local s = box.schema.space.create('memtx')
+ s:create_index('pk', {parts = {2}})
+
+ -- Compact form of a tuple.
+ t.assert_equals(s:insert{string.rep('c', 251), 0}:info(),
+ { data_size = 255,
+ header_size = 6,
+ field_map_size = 4,
+ waste_size = 247,
+ arena = "memtx" }
+ )
+ -- Bulky form of a tuple.
+ t.assert_equals(s:insert{string.rep('b', 252), 1}:info(),
+ { data_size = 256,
+ header_size = 10,
+ field_map_size = 4,
+ waste_size = 242,
+ arena = "memtx" }
+ )
+ -- malloc'ed tuple.
+ t.assert_equals(s:insert{string.rep('m', 1100*1000), 2}:info(),
+ { data_size = 1100007,
+ header_size = 10,
+ field_map_size = 4,
+ waste_size = 0,
+ arena = "malloc" }
+ )
+ -- Check that info().data_size equals bsize()
+ t.assert_equals(s:get(0):info().data_size, s:get(0):bsize())
+ t.assert_equals(s:get(1):info().data_size, s:get(1):bsize())
+ t.assert_equals(s:get(2):info().data_size, s:get(2):bsize())
+ end)
+end
+
+-- Test info() method of a vinyl tuple.
+g1.test_tuple_info_vinyl = function(cg)
+ cg.server:exec(function()
+ local s = box.schema.space.create('vinyl', {engine = 'vinyl'})
+ s:create_index('pk', {parts = {2}})
+ t.assert_equals(s:insert{'v', 0}:info(),
+ { data_size = 4,
+ header_size = 24,
+ field_map_size = 4,
+ waste_size = 0,
+ arena = "malloc" }
+ )
+ -- Check that info().data_size equals bsize()
+ t.assert_equals(s:get(0):info().data_size, s:get(0):bsize())
+ end)
+end
+
+-- Test error messages.
+g1.test_errors = function()
+ t.assert_error_msg_content_equals(
+ 'Usage: tuple:info()',
+ function() box.tuple.new{0}.info() end
+ )
+ t.assert_error_msg_content_equals(
+ 'Usage: tuple:info()',
+ function() box.tuple.new{0}:info('xxx') end
+ )
+ t.assert_error_msg_equals(
+ 'Invalid argument #1 (box.tuple expected, got string)',
+ box.tuple.new{0}.info, 'xxx'
+ )
+end
blob - c5e19430353c7e8aa87217f3e9108a0f45247286
blob + eecd3371d6fb34d24ee509fab3e65c7cab571950
--- test/unit/memtx_allocator.cc
+++ test/unit/memtx_allocator.cc
MemtxAllocator<SmallAlloc>::free_tuple(tuple);
}
+static void
+test_tuple_info(struct tuple_format *format, struct tuple *tuple,
+ struct tuple_info *tuple_info)
+{
+ assert(format == test_tuple_format);
+ (void)format;
+ (void)tuple;
+ (void)tuple_info;
+}
+
static struct tuple_format_vtab test_tuple_format_vtab = {
.tuple_delete = test_tuple_delete,
.tuple_new = test_tuple_new,
+ .tuple_info = test_tuple_info,
};
static struct tuple *