<HTML><BODY><div class="cl-x8kgn9irx3"><div>Hi, Sergey!</div><div> </div><div>Thanks for the patch. Please, consider my suggestion: the debugger-aware «constants» are defined in one block per debugger.</div><div> </div><div><div><div>diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py b/test/tarantool-debugger-tests/debug-extension-tests.py</div><div>index 30a2c478..f4414f4d 100644</div><div>--- a/test/tarantool-debugger-tests/debug-extension-tests.py</div><div>+++ b/test/tarantool-debugger-tests/debug-extension-tests.py</div><div>@@ -19,9 +19,6 @@ LLDB = 'lldb' in DEBUGGER</div><div> EXTENSION = EXTENSION_PATH + '/luajit_dbg.py'</div><div> TIMEOUT = 10</div><div> </div><div>-# Don't run any initialization scripts.</div><div>-RUN_CMD_FILE = []</div><div>-</div><div> if LLDB:</div><div> RUN_CMD_FILE = [</div><div> '--batch',</div><div>@@ -30,15 +27,15 @@ if LLDB:</div><div> '--source-quietly',</div><div> '--source'</div><div> ]</div><div>+ INFERIOR_ARGS = '--'</div><div>+ PROCESS_RUN = 'process launch'</div><div>+ LOAD_EXTENSION = 'command script import {ext}'.format(ext=EXTENSION)</div><div> else:</div><div> # GDB.</div><div> RUN_CMD_FILE = ['--batch', '--nx', '--quiet', '--command']</div><div>-</div><div>-INFERIOR_ARGS = '--' if LLDB else '--args'</div><div>-PROCESS_RUN = 'process launch' if LLDB else 'r'</div><div>-LOAD_EXTENSION = (</div><div>- 'command script import {ext}' if LLDB else 'source {ext}'</div><div>-).format(ext=EXTENSION)</div><div>+ INFERIOR_ARGS = '--args'</div><div>+ PROCESS_RUN = 'r'</div><div>+ LOAD_EXTENSION = 'source {ext}'.format(ext=EXTENSION)</div></div><br> </div><div data-signature-widget="container"><div data-signature-widget="content"><div>--<br>Best regards,</div><div>Evgeniy Temirgaleev</div></div></div><br><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><span>From: Sergey Kaplun <<a href="mailto:skaplun@tarantool.org">skaplun@tarantool.org</a>><br>To: Mikhail Elhimov <<a href="mailto:m.elhimov@vk.team">m.elhimov@vk.team</a>>, Sergey Bronnikov <<a href="mailto:sergeyb@tarantool.org">sergeyb@tarantool.org</a>>, Evgeniy Temirgaleev <<a href="mailto:e.temirgaleev@tarantool.org">e.temirgaleev@tarantool.org</a>><br>Cc: tarantool-patches@dev.tarantool.org, Sergey Kaplun <<a href="mailto:skaplun@tarantool.org">skaplun@tarantool.org</a>><br>Date: Tuesday, May 19, 2026 3:40 PM +03:00</span><br> <div><div id=""><div class="cl-gumvhxc7ew"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div id="style_17791944001467731085_mr_css_attr"><div id="style_17791944001467731085_BODY_mr_css_attr">From: Maxim Kokryashkin <<a href="mailto:m.kokryashkin@tarantool.org">m.kokryashkin@tarantool.org</a>><br><br>This patch adds tests for LuaJIT debugging extensions for lldb and gdb.<br>The tests are written in Python's unittest framework [1].<br><br>Most of the tests are failed for the lldb due to outdated extension<br>sources and overcomplicated hard-coded C structures fields<br>introspection. Hence, tests for LLDB are disabled since they are failing<br>anyway.<br><br>The tarantool-debugger-tests target is introduced. This target is<br>included in the LuaJIT-check-all target but not in the LuaJIT-test<br>target to avoid it running for all LuaJIT builds by default in CI.<br><br>[1]: <a href="https://docs.python.org/3/library/unittest.html">https://docs.python.org/3/library/unittest.html</a><br>---<br>test/CMakeLists.txt | 7 +<br>test/tarantool-debugger-tests/CMakeLists.txt | 93 ++++++<br>.../debug-extension-tests.py | 286 ++++++++++++++++++<br>3 files changed, 386 insertions(+)<br>create mode 100644 test/tarantool-debugger-tests/CMakeLists.txt<br>create mode 100644 test/tarantool-debugger-tests/debug-extension-tests.py<br><br>diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt<br>index f48afa25..26b15892 100644<br>--- a/test/CMakeLists.txt<br>+++ b/test/CMakeLists.txt<br>@@ -175,6 +175,7 @@ add_subdirectory(LuaJIT-tests)<br>add_subdirectory(PUC-Rio-Lua-5.1-tests)<br>add_subdirectory(lua-Harness-tests)<br>add_subdirectory(tarantool-c-tests)<br>+add_subdirectory(tarantool-debugger-tests)<br>add_subdirectory(tarantool-tests)<br><br># Each testsuite has its own CMake target, but combining these<br>@@ -186,6 +187,9 @@ add_subdirectory(tarantool-tests)<br># command that runs all generated CMake tests.<br>add_custom_target(${PROJECT_NAME}-test<br>COMMAND ${CMAKE_CTEST_COMMAND} ${CTEST_FLAGS}<br>+ # Omit this target in LuaJIT-test since we don't want to set<br>+ # up and run debuggers for every build.<br>+ --label-exclude tarantool-debugger-tests<br>DEPENDS tarantool-c-tests-deps<br>tarantool-tests-deps<br>lua-Harness-tests-deps<br>@@ -195,5 +199,8 @@ add_custom_target(${PROJECT_NAME}-test<br><br>add_custom_target(${PROJECT_NAME}-check-all<br>DEPENDS ${PROJECT_NAME}-test<br>+ # Omit this target in LuaJIT-test since we don't want to<br>+ # set up and run debuggers for every build.<br>+ tarantool-debugger-tests<br>${PROJECT_NAME}-lint<br>)<br>diff --git a/test/tarantool-debugger-tests/CMakeLists.txt b/test/tarantool-debugger-tests/CMakeLists.txt<br>new file mode 100644<br>index 00000000..7fd0debc<br>--- /dev/null<br>+++ b/test/tarantool-debugger-tests/CMakeLists.txt<br>@@ -0,0 +1,93 @@<br>+set(TEST_SUITE_NAME "tarantool-debugger-tests")<br>+<br>+# XXX: The call produces both test and target<br>+# <tarantool-debugger-tests-deps> as a side effect.<br>+add_test_suite_target(tarantool-debugger-tests<br>+ LABELS ${TEST_SUITE_NAME}<br>+ DEPENDS ${LUAJIT_TEST_BINARY}<br>+)<br>+<br>+# Debug info is required for testing of extensions.<br>+if(NOT (CMAKE_BUILD_TYPE MATCHES Debug))<br>+ message(WARNING<br>+ "Not a DEBUG build, tarantool-debugger-tests is dummy"<br>+ )<br>+ return()<br>+endif()<br>+<br>+# MacOS asks for permission to debug a process even when the<br>+# machine is set into development mode. To solve the issue,<br>+# it is required to add relevant users to the `_developer` user<br>+# group in macOS. Disabled for now.<br>+if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND DEFINED ENV{CI})<br>+ message(WARNING<br>+ "Interactive debugging is unavailable for macOS CI builds,"<br>+ " tarantool-debugger-tests is dummy"<br>+ )<br>+ return()<br>+endif()<br>+<br>+if(CMAKE_VERSION VERSION_LESS "3.12")<br>+ # TODO:Can remove this after upgrading to CMake >= 3.12.<br>+ find_package(PythonInterp)<br>+ if(NOT PYTHONINTERP_FOUND)<br>+ message(WARNING "`python` is not found, tarantool-debugger-tests is dummy")<br>+ return()<br>+ endif()<br>+else()<br>+ find_package(Python COMPONENTS Interpreter)<br>+ if(NOT PYTHON_FOUND)<br>+ message(WARNING "`python` is not found, tarantool-debugger-tests is dummy")<br>+ return()<br>+ endif()<br>+ set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}")<br>+endif()<br>+<br>+set(DEBUGGER_TEST_ENV<br>+ "LUAJIT_TEST_BINARY=${LUAJIT_TEST_BINARY}"<br>+ # Suppresses __pycache__ generation.<br>+ "PYTHONDONTWRITEBYTECODE=1"<br>+ "DEBUGGER_EXTENSION_PATH=${PROJECT_SOURCE_DIR}/src"<br>+)<br>+<br>+set(TEST_SCRIPT_PATH<br>+ ${CMAKE_CURRENT_SOURCE_DIR}/debug-extension-tests.py<br>+)<br>+<br>+find_program(GDB gdb)<br>+if(GDB)<br>+ set(test_title "test/${TEST_SUITE_NAME}/gdb")<br>+ set(GDB_TEST_ENV ${DEBUGGER_TEST_ENV} "DEBUGGER_COMMAND=${GDB}")<br>+ add_test(NAME "${test_title}"<br>+ COMMAND ${PYTHON_EXECUTABLE} ${TEST_SCRIPT_PATH}<br>+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}<br>+ )<br>+ set_tests_properties("${test_title}" PROPERTIES<br>+ ENVIRONMENT "${GDB_TEST_ENV}"<br>+ LABELS ${TEST_SUITE_NAME}<br>+ DEPENDS tarantool-debugger-tests-deps<br>+ )<br>+else()<br>+ message(WARNING<br>+ "`gdb' is not found, so tarantool-debugger-tests/gdb is omitted"<br>+ )<br>+endif()<br>+<br>+find_program(LLDB lldb)<br>+if(LLDB)<br>+ set(test_title "test/${TEST_SUITE_NAME}/lldb")<br>+ set(LLDB_TEST_ENV ${DEBUGGER_TEST_ENV} "DEBUGGER_COMMAND=${LLDB}")<br>+ add_test(NAME "${test_title}"<br>+ COMMAND ${PYTHON_EXECUTABLE} ${TEST_SCRIPT_PATH}<br>+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}<br>+ )<br>+ set_tests_properties("${test_title}" PROPERTIES<br>+ ENVIRONMENT "${LLDB_TEST_ENV}"<br>+ LABELS ${TEST_SUITE_NAME}<br>+ DEPENDS tarantool-debugger-tests-deps<br>+ )<br>+else()<br>+ message(WARNING<br>+ "`lldb' is not found, so tarantool-debugger-tests/lldb is omitted"<br>+ )<br>+endif()<br>diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py b/test/tarantool-debugger-tests/debug-extension-tests.py<br>new file mode 100644<br>index 00000000..6094c535<br>--- /dev/null<br>+++ b/test/tarantool-debugger-tests/debug-extension-tests.py<br>@@ -0,0 +1,286 @@<br>+# This file provides tests for LuaJIT debug extensions for lldb<br>+# and gdb.<br>+<br>+import os<br>+import re<br>+import subprocess<br>+import sys<br>+import tempfile<br>+import unittest<br>+<br>+from threading import Timer<br>+<br>+LEGACY = re.match(r'^2\.', sys.version)<br>+<br>+LUAJIT_BINARY = os.environ['LUAJIT_TEST_BINARY']<br>+EXTENSION_PATH = os.environ['DEBUGGER_EXTENSION_PATH']<br>+DEBUGGER = os.environ['DEBUGGER_COMMAND']<br>+LLDB = 'lldb' in DEBUGGER<br>+EXTENSION = EXTENSION_PATH + ('/luajit_lldb.py' if LLDB else '/luajit-gdb.py')<br>+TIMEOUT = 10<br>+<br>+# Don't run any initialization scripts.<br>+RUN_CMD_FILE = []<br>+<br>+if LLDB:<br>+ RUN_CMD_FILE = [<br>+ '--batch',<br>+ '--no-lldbinit',<br>+ '--no-use-colors',<br>+ '--source-quietly',<br>+ '--source'<br>+ ]<br>+else:<br>+ # GDB.<br>+ RUN_CMD_FILE = ['--batch', '--nx', '--quiet', '--command']<br>+<br>+INFERIOR_ARGS = '--' if LLDB else '--args'<br>+PROCESS_RUN = 'process launch' if LLDB else 'r'<br>+LOAD_EXTENSION = (<br>+ 'command script import {ext}' if LLDB else 'source {ext}'<br>+).format(ext=EXTENSION)<br>+<br>+<br>+RX_ADDR = r'0x[a-f0-9]+'<br>+RX_HASH = RX_ADDR # The same pattern for hexademic values.<br>+RX_FRAME = r'\[(S|\s)(B|\s)(T|\s)(M|\s)\]'<br>+<br>+<br>+def persist(data):<br>+ tmp = tempfile.NamedTemporaryFile(mode='w')<br>+ tmp.write(data)<br>+ tmp.flush()<br>+ return tmp<br>+<br>+<br>+def execute_process(cmd, timeout=TIMEOUT):<br>+ if LEGACY:<br>+ # XXX: The Python 2.7 version of `subprocess.Popen`<br>+ # doesn't have a timeout option, so the required<br>+ # functionality was implemented via `threading.Timer`.<br>+ process = subprocess.Popen(<br>+ cmd,<br>+ stdout=subprocess.PIPE,<br>+ stderr=subprocess.PIPE,<br>+ # This prevents sending of SIGSTTOU to the test when<br>+ # running by `make'. Stdin is unused anyway.<br>+ stdin=subprocess.DEVNULL<br>+ )<br>+ timer = Timer(TIMEOUT, process.kill)<br>+ timer.start()<br>+ stdout, _ = process.communicate()<br>+ timer.cancel()<br>+<br>+ # XXX: If the timeout is exceeded and the process is<br>+ # killed by the timer, then the return code is non-zero,<br>+ # and we are going to blow up.<br>+ assert process.returncode == 0<br>+ return stdout.decode('ascii')<br>+ else:<br>+ process = subprocess.run(<br>+ cmd,<br>+ stdout=subprocess.PIPE,<br>+ stderr=subprocess.PIPE,<br>+ # This prevents sending of SIGSTTOU to the test when<br>+ # running by `make'. Stdin is unused anyway.<br>+ stdin=subprocess.DEVNULL,<br>+ universal_newlines=True,<br>+ timeout=TIMEOUT<br>+ )<br>+ return process.stdout<br>+<br>+<br>+class TestCaseBase(unittest.TestCase):<br>+ @classmethod<br>+ def construct_cmds(cls):<br>+ return '\n'.join([<br>+ 'b {loc}'.format(loc=cls.location),<br>+ PROCESS_RUN,<br>+ 'n',<br>+ LOAD_EXTENSION,<br>+ cls.extension_cmds.strip(),<br>+ 'q',<br>+ ])<br>+<br>+ @classmethod<br>+ def setUpClass(cls):<br>+ cmd_file = persist(cls.construct_cmds())<br>+ script_file = persist(cls.lua_script)<br>+ process_cmd = [<br>+ DEBUGGER,<br>+ *RUN_CMD_FILE,<br>+ cmd_file.name,<br>+ INFERIOR_ARGS,<br>+ LUAJIT_BINARY,<br>+ script_file.name,<br>+ ]<br>+ cls.output = execute_process(process_cmd)<br>+ cmd_file.close()<br>+ script_file.close()<br>+<br>+ def check(self):<br>+ if LEGACY:<br>+ self.assertRegexpMatches(self.output, self.pattern.strip())<br>+ else:<br>+ self.assertRegex(self.output, self.pattern.strip())<br>+<br>+<br>+class TestLoad(TestCaseBase):<br>+ extension_cmds = ''<br>+ location = 'lj_cf_print'<br>+ lua_script = 'print(1)'<br>+ pattern = (<br>+ r'lj-arch command initialized\n'<br>+ r'lj-tv command initialized\n'<br>+ r'lj-str command initialized\n'<br>+ r'lj-tab command initialized\n'<br>+ r'lj-stack command initialized\n'<br>+ r'lj-state command initialized\n'<br>+ r'lj-gc command initialized\n'<br>+ r'.*is successfully loaded'<br>+ )<br>+<br>+<br>+class TestLJArch(TestCaseBase):<br>+ extension_cmds = 'lj-arch'<br>+ location = 'lj_cf_print'<br>+ lua_script = 'print(1)'<br>+ pattern = (<br>+ r'LJ_64: (True|False), '<br>+ r'LJ_GC64: (True|False), '<br>+ r'LJ_DUALNUM: (True|False)'<br>+ )<br>+<br>+<br>+class TestLJState(TestCaseBase):<br>+ extension_cmds = 'lj-state'<br>+ location = 'lj_cf_print'<br>+ lua_script = 'print(1)'<br>+ pattern = (<br>+ r'VM state: [A-Z]+\n'<br>+ r'GC state: [A-Z]+\n'<br>+ r'JIT state: [A-Z]+\n'<br>+ )<br>+<br>+<br>+class TestLJGC(TestCaseBase):<br>+ extension_cmds = 'lj-gc'<br>+ location = 'lj_cf_print'<br>+ lua_script = 'print(1)'<br>+ pattern = (<br>+ r'GC stats: [A-Z]+\n'<br>+ r'\ttotal: \d+\n'<br>+ r'\tthreshold: \d+\n'<br>+ r'\tdebt: \d+\n'<br>+ r'\testimate: \d+\n'<br>+ r'\tstepmul: \d+\n'<br>+ r'\tpause: \d+\n'<br>+ r'\tsweepstr: \d+/\d+\n'<br>+ r'\troot: \d+ objects\n'<br>+ r'\tgray: \d+ objects\n'<br>+ r'\tgrayagain: \d+ objects\n'<br>+ r'\tweak: \d+ objects\n'<br>+ r'\tmmudata: \d+ objects\n'<br>+ )<br>+<br>+<br>+class TestLJStack(TestCaseBase):<br>+ extension_cmds = 'lj-stack'<br>+ location = 'lj_cf_print'<br>+ lua_script = 'print(1)'<br>+ pattern = (<br>+ r'-+ Red zone:\s+\d+ slots -+\n'<br>+ r'(' + RX_ADDR + r'\s+' + RX_FRAME + r' VALUE: nil\n?)*\n'<br>+ r'-+ Stack:\s+\d+ slots -+\n'<br>+ r'(' + RX_ADDR + r'(:' + RX_ADDR + r')?\s+' + RX_FRAME + r'.*\n?)+\n'<br>+ )<br>+<br>+<br>+class TestLJTV(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'lj-tv L->base\n'<br>+ 'lj-tv L->base + 1\n'<br>+ 'lj-tv L->base + 2\n'<br>+ 'lj-tv L->base + 3\n'<br>+ 'lj-tv L->base + 4\n'<br>+ 'lj-tv L->base + 5\n'<br>+ 'lj-tv L->base + 6\n'<br>+ 'lj-tv L->base + 7\n'<br>+ 'lj-tv L->base + 8\n'<br>+ 'lj-tv L->base + 9\n'<br>+ 'lj-tv L->base + 10\n'<br>+ 'lj-tv L->base + 11\n'<br>+ )<br>+<br>+ lua_script = (<br>+ 'local ffi = require("ffi")\n'<br>+ 'print(\n'<br>+ ' nil,\n'<br>+ ' false,\n'<br>+ ' true,\n'<br>+ ' "hello",\n'<br>+ ' {1},\n'<br>+ ' 1,\n'<br>+ ' 1.1,\n'<br>+ ' coroutine.create(function() end),\n'<br>+ ' ffi.new("int*"),\n'<br>+ ' function() end,\n'<br>+ ' print,\n'<br>+ ' require\n'<br>+ ')\n'<br>+ )<br>+<br>+ pattern = (<br>+ r'nil\n'<br>+ r'false\n'<br>+ r'true\n'<br>+ r'string \"hello\" @ ' + RX_ADDR + r'\n'<br>+ r'table @ ' + RX_ADDR + r' \(asize: \d+, hmask: ' + RX_HASH + r'\)\n'<br>+ r'(number|integer) .*1.*\n'<br>+ r'number 1.1\d+\n'<br>+ r'thread @ ' + RX_ADDR + r'\n'<br>+ r'cdata @ ' + RX_ADDR + r'\n'<br>+ r'Lua function @ ' + RX_ADDR + r', [0-9]+ upvalues, .+:[0-9]+\n'<br>+ r'fast function #[0-9]+\n'<br>+ r'C function @ ' + RX_ADDR + r'\n'<br>+ )<br>+<br>+<br>+class TestLJStr(TestCaseBase):<br>+ extension_cmds = (<br>+ # XXX: Get the value to the stack slot for the variable.<br>+ 'n\n'<br>+ 'lj-str fname\n'<br>+ )<br>+ location = 'lj_cf_dofile'<br>+ lua_script = 'pcall(dofile("name"))'<br>+ pattern = r'String: .* \[\d+ bytes\] with hash ' + RX_HASH<br>+<br>+<br>+class TestLJTab(TestCaseBase):<br>+ extension_cmds = (<br>+ # XXX: Get the value to the stack slot for the variable.<br>+ 'n\n'<br>+ 'lj-tab t\n'<br>+ )<br>+ location = 'lj_cf_unpack'<br>+ lua_script = 'unpack({1; a = 1})'<br>+ pattern = (<br>+ r'Array part: 3 slots\n' +<br>+ RX_ADDR + r': \[0\]: nil\n' +<br>+ RX_ADDR + r': \[1\]: .+ 1\n' +<br>+ RX_ADDR + r': \[2\]: nil\n' +<br>+ r'Hash part: 2 nodes\n' +<br>+ RX_ADDR + r': { string "a" @ ' + RX_ADDR + r' } => ' +<br>+ r'{ .+ 1 }; next = 0x0\n' +<br>+ RX_ADDR + r': { nil } => { nil }; next = 0x0\n'<br>+ )<br>+<br>+<br>+for test_cls in TestCaseBase.__subclasses__():<br>+ test_cls.test = lambda self: self.check()<br>+<br>+# FIXME: skip for LLDB since most commands are not working anyway.<br>+if __name__ == '__main__' and not LLDB:<br>+ unittest.main(verbosity=2)<br>--<br>2.53.0</div></div></div></div></div></div></blockquote></div></div></BODY></HTML>