[Tarantool-patches] [PATCH luajit v4 1/4] test: separate memprof Lua API tests into subtests

Mikhail Shishatskiy m.shishatskiy at tarantool.org
Wed Sep 29 23:07:55 MSK 2021


As the number of memprof test cases is expected to grow,
memprof tests are separated into subtests to encapsulate
test cases and be able to skip some of the subtests.

Also, output generation and parsing separated into a
dedicated function `generate_parsed_output`, which allows
one to run memprof on different payloads, passing payload
funtion as an argument.

Part of tarantool/tarantool#5814
---

Issue: https://github.com/tarantool/tarantool/issues/5814
Branch: https://github.com/tarantool/luajit/tree/shishqa/gh-5814-group-allocations-on-trace-by-trace-number
CI: https://github.com/tarantool/tarantool/tree/shishqa/gh-5814-group-allocations-on-trace-by-trace-number

 .../misclib-memprof-lapi.test.lua             | 169 ++++++++++--------
 1 file changed, 94 insertions(+), 75 deletions(-)

diff --git a/test/tarantool-tests/misclib-memprof-lapi.test.lua b/test/tarantool-tests/misclib-memprof-lapi.test.lua
index 06d96b3b..9de4bd98 100644
--- a/test/tarantool-tests/misclib-memprof-lapi.test.lua
+++ b/test/tarantool-tests/misclib-memprof-lapi.test.lua
@@ -7,7 +7,7 @@ require("utils").skipcond(
 local tap = require("tap")
 
 local test = tap.test("misc-memprof-lapi")
-test:plan(13)
+test:plan(3)
 
 jit.off()
 jit.flush()
@@ -22,7 +22,7 @@ local symtab = require "utils.symtab"
 local TMP_BINFILE = arg[0]:gsub(".+/([^/]+)%.test%.lua$", "%.%1.memprofdata.tmp.bin")
 local BAD_PATH = arg[0]:gsub(".+/([^/]+)%.test%.lua$", "%1/memprofdata.tmp.bin")
 
-local function payload()
+local function default_payload()
   -- Preallocate table to avoid table array part reallocations.
   local _ = table_new(100, 0)
 
@@ -37,7 +37,7 @@ local function payload()
   collectgarbage()
 end
 
-local function generate_output(filename)
+local function generate_output(filename, payload)
   -- Clean up all garbage to avoid pollution of free.
   collectgarbage()
 
@@ -52,6 +52,25 @@ local function generate_output(filename)
   assert(res, err)
 end
 
+local function generate_parsed_output(payload)
+  local res, err = pcall(generate_output, TMP_BINFILE, payload)
+
+  -- Want to cleanup carefully if something went wrong.
+  if not res then
+    os.remove(TMP_BINFILE)
+    error(err)
+  end
+
+  local reader = bufread.new(TMP_BINFILE)
+  local symbols = symtab.parse(reader)
+  local events = memprof.parse(reader)
+
+  -- We don't need it any more.
+  os.remove(TMP_BINFILE)
+
+  return symbols, events
+end
+
 local function fill_ev_type(events, symbols, event_type)
   local ev_type = {}
   for _, event in pairs(events[event_type]) do
@@ -86,85 +105,85 @@ local function check_alloc_report(alloc, line, function_line, nevents)
   return true
 end
 
--- Not a directory.
-local res, err, errno = misc.memprof.start(BAD_PATH)
-test:ok(res == nil and err:match("No such file or directory"))
-test:ok(type(errno) == "number")
-
--- Profiler is running.
-res, err = misc.memprof.start(TMP_BINFILE)
-assert(res, err)
-res, err, errno = misc.memprof.start(TMP_BINFILE)
-test:ok(res == nil and err:match("profiler is running already"))
-test:ok(type(errno) == "number")
+-- Test profiler API.
+test:test("smoke", function(subtest)
+  subtest:plan(6)
 
-res, err = misc.memprof.stop()
-assert(res, err)
+  -- Not a directory.
+  local res, err, errno = misc.memprof.start(BAD_PATH)
+  subtest:ok(res == nil and err:match("No such file or directory"))
+  subtest:ok(type(errno) == "number")
 
--- Profiler is not running.
-res, err, errno = misc.memprof.stop()
-test:ok(res == nil and err:match("profiler is not running"))
-test:ok(type(errno) == "number")
+  -- Profiler is running.
+  res, err = misc.memprof.start(TMP_BINFILE)
+  assert(res, err)
+  res, err, errno = misc.memprof.start(TMP_BINFILE)
+  subtest:ok(res == nil and err:match("profiler is running already"))
+  subtest:ok(type(errno) == "number")
 
--- Test profiler output and parse.
-res, err = pcall(generate_output, TMP_BINFILE)
+  res, err = misc.memprof.stop()
+  assert(res, err)
 
--- Want to cleanup carefully if something went wrong.
-if not res then
-  os.remove(TMP_BINFILE)
-  error(err)
-end
+  -- Profiler is not running.
+  res, err, errno = misc.memprof.stop()
+  subtest:ok(res == nil and err:match("profiler is not running"))
+  subtest:ok(type(errno) == "number")
+end)
 
-local reader = bufread.new(TMP_BINFILE)
-local symbols = symtab.parse(reader)
-local events = memprof.parse(reader, symbols)
-
--- We don't need it any more.
-os.remove(TMP_BINFILE)
-
-local alloc = fill_ev_type(events, symbols, "alloc")
-local free = fill_ev_type(events, symbols, "free")
-
--- Check allocation reports. The second argument is a line number
--- of the allocation event itself. The third is a line number of
--- the corresponding function definition. The last one is
--- the number of allocations.
--- 1 event - alocation of table by itself + 1 allocation
--- of array part as far it is bigger than LJ_MAX_COLOSIZE (16).
-test:ok(check_alloc_report(alloc, 27, 25, 2))
--- 100 strings allocations.
-test:ok(check_alloc_report(alloc, 32, 25, 100))
-
--- Collect all previous allocated objects.
-test:ok(free.INTERNAL.num == 102)
-
--- Tests for leak-only option.
--- See also https://github.com/tarantool/tarantool/issues/5812.
-local heap_delta = process.form_heap_delta(events, symbols)
-local tab_alloc_stats = heap_delta[form_source_line(27)]
-local str_alloc_stats = heap_delta[form_source_line(32)]
-test:ok(tab_alloc_stats.nalloc == tab_alloc_stats.nfree)
-test:ok(tab_alloc_stats.dbytes == 0)
-test:ok(str_alloc_stats.nalloc == str_alloc_stats.nfree)
-test:ok(str_alloc_stats.dbytes == 0)
+-- Test profiler output and parse.
+test:test("output", function(subtest)
+  subtest:plan(7)
+
+  local symbols, events = generate_parsed_output(default_payload)
+
+  local alloc = fill_ev_type(events, symbols, "alloc")
+  local free = fill_ev_type(events, symbols, "free")
+
+  -- Check allocation reports. The second argument is a line
+  -- number of the allocation event itself. The third is a line
+  -- number of the corresponding function definition. The last
+  -- one is the number of allocations. 1 event - alocation of
+  -- table by itself + 1 allocation of array part as far it is
+  -- bigger than LJ_MAX_COLOSIZE (16).
+  subtest:ok(check_alloc_report(alloc, 27, 25, 2))
+  -- 100 strings allocations.
+  subtest:ok(check_alloc_report(alloc, 32, 25, 100))
+
+  -- Collect all previous allocated objects.
+  subtest:ok(free.INTERNAL.num == 102)
+
+  -- Tests for leak-only option.
+  -- See also https://github.com/tarantool/tarantool/issues/5812.
+  local heap_delta = process.form_heap_delta(events, symbols)
+  local tab_alloc_stats = heap_delta[form_source_line(27)]
+  local str_alloc_stats = heap_delta[form_source_line(32)]
+  subtest:ok(tab_alloc_stats.nalloc == tab_alloc_stats.nfree)
+  subtest:ok(tab_alloc_stats.dbytes == 0)
+  subtest:ok(str_alloc_stats.nalloc == str_alloc_stats.nfree)
+  subtest:ok(str_alloc_stats.dbytes == 0)
+end)
 
 -- Test for https://github.com/tarantool/tarantool/issues/5842.
--- We are not interested in this report.
-misc.memprof.start("/dev/null")
--- We need to cause stack resize for local variables at function
--- call. Let's create a new coroutine (all slots are free).
--- It has 1 slot for dummy frame + 39 free slots + 5 extra slots
--- (so-called red zone) + 2 * LJ_FR2 slots. So 50 local variables
--- is enough.
-local payload_str = ""
-for i = 1, 50 do
-  payload_str = payload_str..("local v%d = %d\n"):format(i, i)
-end
-local f, errmsg = loadstring(payload_str)
-assert(f, errmsg)
-local co = coroutine.create(f)
-coroutine.resume(co)
-misc.memprof.stop()
+test:test("stack-resize", function(subtest)
+  subtest:plan(0)
+
+  -- We are not interested in this report.
+  misc.memprof.start("/dev/null")
+  -- We need to cause stack resize for local variables at function
+  -- call. Let's create a new coroutine (all slots are free).
+  -- It has 1 slot for dummy frame + 39 free slots + 5 extra slots
+  -- (so-called red zone) + 2 * LJ_FR2 slots. So 50 local
+  -- variables is enough.
+  local payload_str = ""
+  for i = 1, 50 do
+    payload_str = payload_str..("local v%d = %d\n"):format(i, i)
+  end
+  local f, errmsg = loadstring(payload_str)
+  assert(f, errmsg)
+  local co = coroutine.create(f)
+  coroutine.resume(co)
+  misc.memprof.stop()
+end)
 
 jit.on()
 os.exit(test:check() and 0 or 1)
-- 
2.33.0



More information about the Tarantool-patches mailing list