Tarantool development patches archive
 help / color / mirror / Atom feed
From: Sergey Bronnikov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Sergey Kaplun <skaplun@tarantool.org>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH v2 luajit 1/6] test: introduce tests for debugging extensions
Date: Wed, 20 May 2026 16:38:54 +0300	[thread overview]
Message-ID: <3703735e-192b-4e3f-8842-2cff7a61e725@tarantool.org> (raw)
In-Reply-To: <20260519123913.178775-2-skaplun@tarantool.org>

[-- Attachment #1: Type: text/plain, Size: 26903 bytes --]

Hi, Sergey,

thanks for the patch! Please see my comments.

Sergey

On 5/19/26 15:39, Sergey Kaplun wrote:
> From: Maxim Kokryashkin<m.kokryashkin@tarantool.org>

It looks like the patch was made by Maxim K. and you took it as is, but 
it is not so.

The latest version of this patch is here [1] and there several changes 
introduced by you.

I suppose these changes should be described and you should to somehow

say that you changed the patch a little (for example using Git trailer 
"Co-authored-by:").

Otherwise, we are following bad practices, let's not be like those 
people we usually joke about.

The full diff is below:

--- dbg/debug-extension-tests.py 2026-05-20 16:02:11.854999756 +0300
+++ test/tarantool-debugger-tests/debug-extension-tests.py   2026-05-20 
16:02:43.124696812 +0300
@@ -1,4 +1,6 @@
-# This file provides tests for LuaJIT debug extensions for lldb and gdb.
+# This file provides tests for LuaJIT debug extensions for lldb
+# and gdb.
+
  import os
  import re
  import subprocess
@@ -11,12 +13,27 @@
  LEGACY = re.match(r'^2\.', sys.version)

  LUAJIT_BINARY = os.environ['LUAJIT_TEST_BINARY']
-EXTENSION = os.environ['DEBUGGER_EXTENSION_PATH']
+EXTENSION_PATH = os.environ['DEBUGGER_EXTENSION_PATH']
  DEBUGGER = os.environ['DEBUGGER_COMMAND']
  LLDB = 'lldb' in DEBUGGER
+EXTENSION = EXTENSION_PATH + ('/luajit_lldb.py' if LLDB else 
'/luajit-gdb.py')
  TIMEOUT = 10

-RUN_CMD_FILE = '-s' if LLDB else '-x'
+# Don't run any initialization scripts.
+RUN_CMD_FILE = []
+
+if LLDB:
+    RUN_CMD_FILE = [
+        '--batch',
+        '--no-lldbinit',
+        '--no-use-colors',
+        '--source-quietly',
+        '--source'
+    ]
+else:
+    # GDB.
+    RUN_CMD_FILE = ['--batch', '--nx', '--quiet', '--command']
+
  INFERIOR_ARGS = '--' if LLDB else '--args'
  PROCESS_RUN = 'process launch' if LLDB else 'r'
  LOAD_EXTENSION = (
@@ -24,6 +41,11 @@
  ).format(ext=EXTENSION)


+RX_ADDR = r'0x[a-f0-9]+'
+RX_HASH = RX_ADDR  # The same pattern for hexademic values.
+RX_FRAME = r'\[(S|\s)(B|\s)(T|\s)(M|\s)\]'
+
+
  def persist(data):
      tmp = tempfile.NamedTemporaryFile(mode='w')
      tmp.write(data)
@@ -33,32 +55,39 @@

  def execute_process(cmd, timeout=TIMEOUT):
      if LEGACY:
-        # XXX: The Python 2.7 version of `subprocess.Popen` doesn't have a
-        # timeout option, so the required functionality was implemented via
-        # `threading.Timer`.
-        process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        # XXX: The Python 2.7 version of `subprocess.Popen`
+        # doesn't have a timeout option, so the required
+        # functionality was implemented via `threading.Timer`.
+        process = subprocess.Popen(
+            cmd,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            # This prevents sending of SIGSTTOU to the test when
+            # running by `make'. Stdin is unused anyway.
+            stdin=subprocess.DEVNULL
+        )
          timer = Timer(TIMEOUT, process.kill)
          timer.start()
          stdout, _ = process.communicate()
          timer.cancel()

-        # XXX: If the timeout is exceeded and the process is killed by the
-        # timer, then the return code is non-zero, and we are going to 
blow up.
+        # XXX: If the timeout is exceeded and the process is
+        # killed by the timer, then the return code is non-zero,
+        # and we are going to blow up.
          assert process.returncode == 0
          return stdout.decode('ascii')
      else:
-        process = subprocess.run(cmd, capture_output=True, timeout=TIMEOUT)
-        return process.stdout.decode('ascii')
-
-
-def filter_debugger_output(output):
-    descriptor = '(lldb)' if LLDB else '(gdb)'
-    return ''.join(
-        filter(
-            lambda line: not line.startswith(descriptor),
-            output.splitlines(True),
-        ),
-    )
+        process = subprocess.run(
+            cmd,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            # This prevents sending of SIGSTTOU to the test when
+            # running by `make'. Stdin is unused anyway.
+            stdin=subprocess.DEVNULL,
+            universal_newlines=True,
+            timeout=TIMEOUT
+        )
+        return process.stdout


  class TestCaseBase(unittest.TestCase):
@@ -79,13 +108,13 @@
          script_file = persist(cls.lua_script)
          process_cmd = [
              DEBUGGER,
-            RUN_CMD_FILE,
+            *RUN_CMD_FILE,
              cmd_file.name,
              INFERIOR_ARGS,
              LUAJIT_BINARY,
              script_file.name,
          ]
-        cls.output = filter_debugger_output(execute_process(process_cmd))
+        cls.output = execute_process(process_cmd)
          cmd_file.close()
          script_file.close()

@@ -101,14 +130,14 @@
      location = 'lj_cf_print'
      lua_script = 'print(1)'
      pattern = (
-        'lj-tv command intialized\n'
-        'lj-state command intialized\n'
-        'lj-arch command intialized\n'
-        'lj-gc command intialized\n'
-        'lj-str command intialized\n'
-        'lj-tab command intialized\n'
-        'lj-stack command intialized\n'
-        'LuaJIT debug extension is successfully loaded\n'
+        r'lj-arch command initialized\n'
+        r'lj-tv command initialized\n'
+        r'lj-str command initialized\n'
+        r'lj-tab command initialized\n'
+        r'lj-stack command initialized\n'
+        r'lj-state command initialized\n'
+        r'lj-gc command initialized\n'
+        r'.*is successfully loaded'
      )


@@ -117,9 +146,9 @@
      location = 'lj_cf_print'
      lua_script = 'print(1)'
      pattern = (
-        'LJ_64: (True|False), '
-        'LJ_GC64: (True|False), '
-        'LJ_DUALNUM: (True|False)'
+        r'LJ_64: (True|False), '
+        r'LJ_GC64: (True|False), '
+        r'LJ_DUALNUM: (True|False)'
      )


@@ -128,9 +157,9 @@
      location = 'lj_cf_print'
      lua_script = 'print(1)'
      pattern = (
-        'VM state: [A-Z]+\n'
-        'GC state: [A-Z]+\n'
-        'JIT state: [A-Z]+\n'
+        r'VM state: [A-Z]+\n'
+        r'GC state: [A-Z]+\n'
+        r'JIT state: [A-Z]+\n'
      )


@@ -139,19 +168,19 @@
      location = 'lj_cf_print'
      lua_script = 'print(1)'
      pattern = (
-        'GC stats: [A-Z]+\n'
-        '\ttotal: \d+\n'
-        '\tthreshold: \d+\n'
-        '\tdebt: \d+\n'
-        '\testimate: \d+\n'
-        '\tstepmul: \d+\n'
-        '\tpause: \d+\n'
-        '\tsweepstr: \d+/\d+\n'
-        '\troot: \d+ objects\n'
-        '\tgray: \d+ objects\n'
-        '\tgrayagain: \d+ objects\n'
-        '\tweak: \d+ objects\n'
-        '\tmmudata: \d+ objects\n'
+        r'GC stats: [A-Z]+\n'
+        r'\ttotal: \d+\n'
+        r'\tthreshold: \d+\n'
+        r'\tdebt: \d+\n'
+        r'\testimate: \d+\n'
+        r'\tstepmul: \d+\n'
+        r'\tpause: \d+\n'
+        r'\tsweepstr: \d+/\d+\n'
+        r'\troot: \d+ objects\n'
+        r'\tgray: \d+ objects\n'
+        r'\tgrayagain: \d+ objects\n'
+        r'\tweak: \d+ objects\n'
+        r'\tmmudata: \d+ objects\n'
      )


@@ -160,17 +189,15 @@
      location = 'lj_cf_print'
      lua_script = 'print(1)'
      pattern = (
-        '-+ Red zone:\s+\d+ slots -+\n'
-        '(0x[a-zA-Z0-9]+\s+\[(S|\s)(B|\s)(T|\s)(M|\s)\] VALUE: nil\n?)*\n'
-        '-+ Stack:\s+\d+ slots -+\n'
-        '(0x[A-Za-z0-9]+(:0x[A-Za-z0-9]+)?\s+'
-        '\[(S|\s)(B|\s)(T|\s)(M|\s)\].*\n?)+\n'
+        r'-+ Red zone:\s+\d+ slots -+\n'
+        r'(' + RX_ADDR + r'\s+' + RX_FRAME + r' VALUE: nil\n?)*\n'
+        r'-+ Stack:\s+\d+ slots -+\n'
+        r'(' + RX_ADDR + r'(:' + RX_ADDR + r')?\s+' + RX_FRAME + 
r'.*\n?)+\n'
      )


  class TestLJTV(TestCaseBase):
      location = 'lj_cf_print'
-    lua_script = 'print(1)'
      extension_cmds = (
          'lj-tv L->base\n'
          'lj-tv L->base + 1\n'
@@ -205,46 +232,55 @@
      )

      pattern = (
-        'nil\n'
-        'false\n'
-        'true\n'
-        'string \"hello\" @ 0x[a-zA-Z0-9]+\n'
-        'table @ 0x[a-zA-Z0-9]+ \(asize: \d+, hmask: 0x[a-zA-Z0-9]+\)\n'
-        '(number|integer) .*1.*\n'
-        'number 1.1\d+\n'
-        'thread @ 0x[a-zA-Z0-9]+\n'
-        'cdata @ 0x[a-zA-Z0-9]+\n'
-        'Lua function @ 0x[a-zA-Z0-9]+, [0-9]+ upvalues, .+:[0-9]+\n'
-        'fast function #[0-9]+\n'
-        'C function @ 0x[a-zA-Z0-9]+\n'
+        r'nil\n'
+        r'false\n'
+        r'true\n'
+        r'string \"hello\" @ ' + RX_ADDR + r'\n'
+        r'table @ ' + RX_ADDR + r' \(asize: \d+, hmask: ' + RX_HASH + 
r'\)\n'
+        r'(number|integer) .*1.*\n'
+        r'number 1.1\d+\n'
+        r'thread @ ' + RX_ADDR + r'\n'
+        r'cdata @ ' + RX_ADDR + r'\n'
+        r'Lua function @ ' + RX_ADDR + r', [0-9]+ upvalues, .+:[0-9]+\n'
+        r'fast function #[0-9]+\n'
+        r'C function @ ' + RX_ADDR + r'\n'
      )


  class TestLJStr(TestCaseBase):
-    extension_cmds = 'lj-str fname'
+    extension_cmds = (
+        # XXX: Get the value to the stack slot for the variable.
+        'n\n'
+        'lj-str fname\n'
+    )
      location = 'lj_cf_dofile'
      lua_script = 'pcall(dofile("name"))'
-    pattern = 'String: .* \[\d+ bytes\] with hash 0x[a-zA-Z0-9]+'
+    pattern = r'String: .* \[\d+ bytes\] with hash ' + RX_HASH


  class TestLJTab(TestCaseBase):
-    extension_cmds = 'lj-tab t'
+    extension_cmds = (
+        # XXX: Get the value to the stack slot for the variable.
+        'n\n'
+        'lj-tab t\n'
+    )
      location = 'lj_cf_unpack'
      lua_script = 'unpack({1; a = 1})'
      pattern = (
-        'Array part: 3 slots\n'
-        '0x[a-zA-Z0-9]+: \[0\]: nil\n'
-        '0x[a-zA-Z0-9]+: \[1\]: .+ 1\n'
-        '0x[a-zA-Z0-9]+: \[2\]: nil\n'
-        'Hash part: 2 nodes\n'
-        '0x[a-zA-Z0-9]+: { string "a" @ 0x[a-zA-Z0-9]+ } => '
-        '{ .+ 1 }; next = 0x0\n'
-        '0x[a-zA-Z0-9]+: { nil } => { nil }; next = 0x0\n'
+        r'Array part: 3 slots\n' +
+        RX_ADDR + r': \[0\]: nil\n' +
+        RX_ADDR + r': \[1\]: .+ 1\n' +
+        RX_ADDR + r': \[2\]: nil\n' +
+        r'Hash part: 2 nodes\n' +
+        RX_ADDR + r': { string "a" @ ' + RX_ADDR + r' } => ' +
+        r'{ .+ 1 }; next = 0x0\n' +
+        RX_ADDR + r': { nil } => { nil }; next = 0x0\n'
      )


  for test_cls in TestCaseBase.__subclasses__():
      test_cls.test = lambda self: self.check()

-if __name__ == '__main__':
+# FIXME: skip for LLDB since most commands are not working anyway.
+if __name__ == '__main__' and not LLDB:
      unittest.main(verbosity=2)

[1]: 
https://lists.tarantool.org/tarantool-patches/cover.1712182830.git.m.kokryashkin@tarantool.org/

> This patch adds tests for LuaJIT debugging extensions for lldb and gdb.
> The tests are written in Python's unittest framework [1].
>
> Most of the tests are failed for the lldb due to outdated extension
> sources and overcomplicated hard-coded C structures fields
> introspection. Hence, tests for LLDB are disabled since they are failing
> anyway.
>
> The tarantool-debugger-tests target is introduced. This target is
> included in the LuaJIT-check-all target but not in the LuaJIT-test
> target to avoid it running for all LuaJIT builds by default in CI.

I don't get why we cannot run debugger tests together with other tests.

dbg extension must work with all LuaJIT configurations that we test in CI,

so I suppose we should run these (dbg) tests with all other regression 
tests.

For example, the proposed GHA workflows doesn't cover DUALNUM build,

сan I be sure that the extension will work with DUALNUM build? Seems no.

>
> [1]:https://docs.python.org/3/library/unittest.html
> ---
>   test/CMakeLists.txt                           |   7 +
>   test/tarantool-debugger-tests/CMakeLists.txt  |  93 ++++++
>   .../debug-extension-tests.py                  | 286 ++++++++++++++++++
>   3 files changed, 386 insertions(+)
>   create mode 100644 test/tarantool-debugger-tests/CMakeLists.txt
>   create mode 100644 test/tarantool-debugger-tests/debug-extension-tests.py
>
> diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
> index f48afa25..26b15892 100644
> --- a/test/CMakeLists.txt
> +++ b/test/CMakeLists.txt
> @@ -175,6 +175,7 @@ add_subdirectory(LuaJIT-tests)
>   add_subdirectory(PUC-Rio-Lua-5.1-tests)
>   add_subdirectory(lua-Harness-tests)
>   add_subdirectory(tarantool-c-tests)
> +add_subdirectory(tarantool-debugger-tests)
>   add_subdirectory(tarantool-tests)
>   
>   # Each testsuite has its own CMake target, but combining these
> @@ -186,6 +187,9 @@ add_subdirectory(tarantool-tests)
>   # command that runs all generated CMake tests.
>   add_custom_target(${PROJECT_NAME}-test
>     COMMAND ${CMAKE_CTEST_COMMAND} ${CTEST_FLAGS}
> +    # Omit this target in LuaJIT-test since we don't want to set
> +    # up and run debuggers for every build.
> +    --label-exclude tarantool-debugger-tests
see the comment above
>     DEPENDS tarantool-c-tests-deps
>             tarantool-tests-deps
>             lua-Harness-tests-deps
> @@ -195,5 +199,8 @@ add_custom_target(${PROJECT_NAME}-test
>   
>   add_custom_target(${PROJECT_NAME}-check-all
>     DEPENDS ${PROJECT_NAME}-test
> +          # Omit this target in LuaJIT-test since we don't want to
> +          # set up and run debuggers for every build.
> +          tarantool-debugger-tests
>             ${PROJECT_NAME}-lint
>   )
> diff --git a/test/tarantool-debugger-tests/CMakeLists.txt b/test/tarantool-debugger-tests/CMakeLists.txt
> new file mode 100644
> index 00000000..7fd0debc
> --- /dev/null
> +++ b/test/tarantool-debugger-tests/CMakeLists.txt
> @@ -0,0 +1,93 @@
> +set(TEST_SUITE_NAME "tarantool-debugger-tests")
> +
> +# XXX: The call produces both test and target
> +# <tarantool-debugger-tests-deps> as a side effect.
> +add_test_suite_target(tarantool-debugger-tests
> +  LABELS ${TEST_SUITE_NAME}
> +  DEPENDS ${LUAJIT_TEST_BINARY}
> +)
> +
> +# Debug info is required for testing of extensions.
> +if(NOT (CMAKE_BUILD_TYPE MATCHES Debug))
> +  message(WARNING
> +    "Not a DEBUG build, tarantool-debugger-tests is dummy"
> +  )

it is not dummy, it doesn't exist at all:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

cd build

make tarantool-debugger-tests
make[3]: *** No rule to make target 'src/luajit', needed by 
'test/tarantool-debugger-tests/CMakeFiles/tarantool-debugger-tests-deps'. 
Stop.

> +  return()
> +endif()
> +
> +# MacOS asks for permission to debug a process even when the
> +# machine is set into development mode. To solve the issue,
> +# it is required to add relevant users to the `_developer` user
> +# group in macOS. Disabled for now.
> +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND DEFINED ENV{CI})
> +  message(WARNING
> +    "Interactive debugging is unavailable for macOS CI builds,"
> +    " tarantool-debugger-tests is dummy"
> +  )
the same as above, it is not dummy
> +  return()
> +endif()
> +
> +if(CMAKE_VERSION VERSION_LESS "3.12")
> +  #TODO:Can remove this after upgrading to CMake >= 3.12.
s/TODO:/TODO/
> +  find_package(PythonInterp)
> +  if(NOT PYTHONINTERP_FOUND)
> +    message(WARNING "`python` is not found, tarantool-debugger-tests is dummy")
it is not dummy
> +    return()
> +  endif()
> +else()
> +  find_package(Python COMPONENTS Interpreter)
> +  if(NOT PYTHON_FOUND)
> +    message(WARNING "`python` is not found, tarantool-debugger-tests is dummy")
it is not dummy
> +    return()
> +  endif()
> +  set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}")
> +endif()
> +
> +set(DEBUGGER_TEST_ENV
> +  "LUAJIT_TEST_BINARY=${LUAJIT_TEST_BINARY}"
> +  # Suppresses __pycache__ generation.
> +  "PYTHONDONTWRITEBYTECODE=1"
> +  "DEBUGGER_EXTENSION_PATH=${PROJECT_SOURCE_DIR}/src"
> +)
> +
> +set(TEST_SCRIPT_PATH
> +  ${CMAKE_CURRENT_SOURCE_DIR}/debug-extension-tests.py
> +)
> +
> +find_program(GDB gdb)
> +if(GDB)
> +  set(test_title "test/${TEST_SUITE_NAME}/gdb")
> +  set(GDB_TEST_ENV ${DEBUGGER_TEST_ENV} "DEBUGGER_COMMAND=${GDB}")
> +  add_test(NAME "${test_title}"
> +    COMMAND ${PYTHON_EXECUTABLE} ${TEST_SCRIPT_PATH}
> +    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
> +  )
> +  set_tests_properties("${test_title}" PROPERTIES
> +    ENVIRONMENT "${GDB_TEST_ENV}"
> +    LABELS ${TEST_SUITE_NAME}
> +    DEPENDS tarantool-debugger-tests-deps
> +  )
> +else()
> +  message(WARNING
> +    "`gdb' is not found, so tarantool-debugger-tests/gdb is omitted"
> +  )
> +endif()
> +
> +find_program(LLDB lldb)
> +if(LLDB)
> +  set(test_title "test/${TEST_SUITE_NAME}/lldb")
> +  set(LLDB_TEST_ENV ${DEBUGGER_TEST_ENV} "DEBUGGER_COMMAND=${LLDB}")
> +  add_test(NAME "${test_title}"
> +    COMMAND ${PYTHON_EXECUTABLE} ${TEST_SCRIPT_PATH}
> +    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
> +  )
> +  set_tests_properties("${test_title}" PROPERTIES
> +    ENVIRONMENT "${LLDB_TEST_ENV}"
> +    LABELS ${TEST_SUITE_NAME}
> +    DEPENDS tarantool-debugger-tests-deps
> +  )
> +else()
> +  message(WARNING
> +    "`lldb' is not found, so tarantool-debugger-tests/lldb is omitted"
> +  )
> +endif()
Duplicate code for GDB and LLDB. May be use a macro for this?
> diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py b/test/tarantool-debugger-tests/debug-extension-tests.py
> new file mode 100644
> index 00000000..6094c535
> --- /dev/null
> +++ b/test/tarantool-debugger-tests/debug-extension-tests.py
> @@ -0,0 +1,286 @@
> +# This file provides tests for LuaJIT debug extensions for lldb
> +# and gdb.
> +
> +import os
> +import re
> +import subprocess
> +import sys
> +import tempfile
> +import unittest
> +
> +from threading import Timer
> +
> +LEGACY = re.match(r'^2\.', sys.version)
> +
> +LUAJIT_BINARY = os.environ['LUAJIT_TEST_BINARY']
> +EXTENSION_PATH = os.environ['DEBUGGER_EXTENSION_PATH']
> +DEBUGGER = os.environ['DEBUGGER_COMMAND']
> +LLDB = 'lldb' in DEBUGGER
> +EXTENSION = EXTENSION_PATH + ('/luajit_lldb.py' if LLDB else '/luajit-gdb.py')
> +TIMEOUT = 10
> +
> +# Don't run any initialization scripts.
> +RUN_CMD_FILE = []
> +
> +if LLDB:
> +    RUN_CMD_FILE = [
> +        '--batch',
> +        '--no-lldbinit',
> +        '--no-use-colors',
> +        '--source-quietly',
> +        '--source'
> +    ]
> +else:
> +    # GDB.
> +    RUN_CMD_FILE = ['--batch', '--nx', '--quiet', '--command']
> +
> +INFERIOR_ARGS = '--' if LLDB else '--args'
> +PROCESS_RUN = 'process launch' if LLDB else 'r'
> +LOAD_EXTENSION = (
> +    'command script import {ext}' if LLDB else 'source {ext}'
> +).format(ext=EXTENSION)
> +
> +
> +RX_ADDR = r'0x[a-f0-9]+'
> +RX_HASH = RX_ADDR  # The same pattern for hexademic values.
> +RX_FRAME = r'\[(S|\s)(B|\s)(T|\s)(M|\s)\]'
> +
> +
> +def persist(data):
> +    tmp = tempfile.NamedTemporaryFile(mode='w')
> +    tmp.write(data)
> +    tmp.flush()
> +    return tmp
> +
> +
> +def execute_process(cmd, timeout=TIMEOUT):
> +    if LEGACY:
> +        # XXX: The Python 2.7 version of `subprocess.Popen`
> +        # doesn't have a timeout option, so the required
> +        # functionality was implemented via `threading.Timer`.
> +        process = subprocess.Popen(
> +            cmd,
> +            stdout=subprocess.PIPE,
> +            stderr=subprocess.PIPE,
> +            # This prevents sending of SIGSTTOU to the test when
> +            # running by `make'. Stdin is unused anyway.
> +            stdin=subprocess.DEVNULL
> +        )
> +        timer = Timer(TIMEOUT, process.kill)
> +        timer.start()
> +        stdout, _ = process.communicate()
> +        timer.cancel()
> +
> +        # XXX: If the timeout is exceeded and the process is
> +        # killed by the timer, then the return code is non-zero,
> +        # and we are going to blow up.
> +        assert process.returncode == 0
> +        return stdout.decode('ascii')
> +    else:
> +        process = subprocess.run(
> +            cmd,
> +            stdout=subprocess.PIPE,
> +            stderr=subprocess.PIPE,
> +            # This prevents sending of SIGSTTOU to the test when
> +            # running by `make'. Stdin is unused anyway.
> +            stdin=subprocess.DEVNULL,
> +            universal_newlines=True,
> +            timeout=TIMEOUT
> +        )
> +        return process.stdout
> +
> +
> +class TestCaseBase(unittest.TestCase):
> +    @classmethod
> +    def construct_cmds(cls):
> +        return '\n'.join([
> +            'b {loc}'.format(loc=cls.location),
> +            PROCESS_RUN,
> +            'n',
> +            LOAD_EXTENSION,
> +            cls.extension_cmds.strip(),
> +            'q',
> +        ])
> +
> +    @classmethod
> +    def setUpClass(cls):
> +        cmd_file = persist(cls.construct_cmds())
> +        script_file = persist(cls.lua_script)
> +        process_cmd = [
> +            DEBUGGER,
> +            *RUN_CMD_FILE,
> +            cmd_file.name,
> +            INFERIOR_ARGS,
> +            LUAJIT_BINARY,
> +            script_file.name,
> +        ]
> +        cls.output = execute_process(process_cmd)
> +        cmd_file.close()
> +        script_file.close()
> +
> +    def check(self):
> +        if LEGACY:
> +            self.assertRegexpMatches(self.output, self.pattern.strip())
> +        else:
> +            self.assertRegex(self.output, self.pattern.strip())
> +
> +
> +class TestLoad(TestCaseBase):
> +    extension_cmds = ''
> +    location = 'lj_cf_print'
> +    lua_script = 'print(1)'
> +    pattern = (
> +        r'lj-arch command initialized\n'
> +        r'lj-tv command initialized\n'
> +        r'lj-str command initialized\n'
> +        r'lj-tab command initialized\n'
> +        r'lj-stack command initialized\n'
> +        r'lj-state command initialized\n'
> +        r'lj-gc command initialized\n'
> +        r'.*is successfully loaded'
> +    )
> +
> +
> +class TestLJArch(TestCaseBase):
> +    extension_cmds = 'lj-arch'
> +    location = 'lj_cf_print'
> +    lua_script = 'print(1)'
> +    pattern = (
> +        r'LJ_64: (True|False), '
> +        r'LJ_GC64: (True|False), '
> +        r'LJ_DUALNUM: (True|False)'
> +    )
> +
> +
> +class TestLJState(TestCaseBase):
> +    extension_cmds = 'lj-state'
> +    location = 'lj_cf_print'
> +    lua_script = 'print(1)'
> +    pattern = (
> +        r'VM state: [A-Z]+\n'
> +        r'GC state: [A-Z]+\n'
> +        r'JIT state: [A-Z]+\n'
> +    )
> +
> +
> +class TestLJGC(TestCaseBase):
> +    extension_cmds = 'lj-gc'
> +    location = 'lj_cf_print'
> +    lua_script = 'print(1)'
> +    pattern = (
> +        r'GC stats: [A-Z]+\n'
> +        r'\ttotal: \d+\n'
> +        r'\tthreshold: \d+\n'
> +        r'\tdebt: \d+\n'
> +        r'\testimate: \d+\n'
> +        r'\tstepmul: \d+\n'
> +        r'\tpause: \d+\n'
> +        r'\tsweepstr: \d+/\d+\n'
> +        r'\troot: \d+ objects\n'
> +        r'\tgray: \d+ objects\n'
> +        r'\tgrayagain: \d+ objects\n'
> +        r'\tweak: \d+ objects\n'
> +        r'\tmmudata: \d+ objects\n'
> +    )
> +
> +
> +class TestLJStack(TestCaseBase):
> +    extension_cmds = 'lj-stack'
> +    location = 'lj_cf_print'
> +    lua_script = 'print(1)'
> +    pattern = (
> +        r'-+ Redzone:\s+\d+ slots -+\n'
> +        r'(' + RX_ADDR + r'\s+' + RX_FRAME + r' VALUE: nil\n?)*\n'
> +        r'-+Stack:\s+\d+ slots -+\n'
> +        r'(' + RX_ADDR + r'(:' + RX_ADDR + r')?\s+' + RX_FRAME + r'.*\n?)+\n'
> +    )
> +
> +
> +class TestLJTV(TestCaseBase):
> +    location = 'lj_cf_print'
> +    extension_cmds = (
> +        'lj-tv L->base\n'
> +        'lj-tv L->base + 1\n'
> +        'lj-tv L->base + 2\n'
> +        'lj-tv L->base + 3\n'
> +        'lj-tv L->base + 4\n'
> +        'lj-tv L->base + 5\n'
> +        'lj-tv L->base + 6\n'
> +        'lj-tv L->base + 7\n'
> +        'lj-tv L->base + 8\n'
> +        'lj-tv L->base + 9\n'
> +        'lj-tv L->base + 10\n'
> +        'lj-tv L->base + 11\n'
> +    )
> +
> +    lua_script = (
> +        'local ffi = require("ffi")\n'
> +        'print(\n'
> +        '  nil,\n'
> +        '  false,\n'
> +        '  true,\n'
> +        '  "hello",\n'
> +        '  {1},\n'
> +        '  1,\n'
> +        '  1.1,\n'
> +        '  coroutine.create(function() end),\n'
> +        '  ffi.new("int*"),\n'
> +        '  function() end,\n'
> +        '  print,\n'
> +        '  require\n'
> +        ')\n'
> +    )
> +
> +    pattern = (
> +        r'nil\n'
> +        r'false\n'
> +        r'true\n'
> +        r'string \"hello\" @ ' + RX_ADDR + r'\n'
> +        r'table @ ' + RX_ADDR + r' \(asize: \d+, hmask: ' + RX_HASH + r'\)\n'
> +        r'(number|integer) .*1.*\n'
> +        r'number 1.1\d+\n'
> +        r'thread @ ' + RX_ADDR + r'\n'
> +        r'cdata @ ' + RX_ADDR + r'\n'
> +        r'Lua function @ ' + RX_ADDR + r', [0-9]+ upvalues, .+:[0-9]+\n'
> +        r'fast function #[0-9]+\n'
> +        r'C function @ ' + RX_ADDR + r'\n'
> +    )
> +
> +
> +class TestLJStr(TestCaseBase):
> +    extension_cmds = (
> +        # XXX: Get the value to the stack slot for the variable.
> +        'n\n'
> +        'lj-str fname\n'
> +    )
> +    location = 'lj_cf_dofile'
> +    lua_script = 'pcall(dofile("name"))'
> +    pattern = r'String: .* \[\d+ bytes\] with hash ' + RX_HASH
> +
> +
> +class TestLJTab(TestCaseBase):
> +    extension_cmds = (
> +        # XXX: Get the value to the stack slot for the variable.
> +        'n\n'
> +        'lj-tab t\n'
> +    )
> +    location = 'lj_cf_unpack'
> +    lua_script = 'unpack({1; a = 1})'
> +    pattern = (
> +        r'Array part: 3 slots\n' +
> +        RX_ADDR + r': \[0\]: nil\n' +
> +        RX_ADDR + r': \[1\]: .+ 1\n' +
> +        RX_ADDR + r': \[2\]: nil\n' +
> +        r'Hash part: 2 nodes\n' +
> +        RX_ADDR + r': { string "a" @ ' + RX_ADDR + r' } => ' +
> +        r'{ .+ 1 }; next = 0x0\n' +
> +        RX_ADDR + r': { nil } => { nil }; next = 0x0\n'
> +    )
> +
> +
> +for test_cls in TestCaseBase.__subclasses__():
> +    test_cls.test = lambda self: self.check()
> +
> +# FIXME: skip for LLDB since most commands are not working anyway.
> +if __name__ == '__main__' and not LLDB:
> +    unittest.main(verbosity=2)

[-- Attachment #2: Type: text/html, Size: 35089 bytes --]

  reply	other threads:[~2026-05-20 13:38 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-19 12:39 [Tarantool-patches] [PATCH v2 luajit 0/6] Unified extension for debuggers Sergey Kaplun via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 1/6] test: introduce tests for debugging extensions Sergey Kaplun via Tarantool-patches
2026-05-20 13:38   ` Sergey Bronnikov via Tarantool-patches [this message]
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 2/6] lldb: refactor extension Sergey Kaplun via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 3/6] dbg: sort initialization of commands Sergey Kaplun via Tarantool-patches
2026-05-20 13:43   ` Sergey Bronnikov via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 4/6] lldb: support full-range 64-bit lightuserdata Sergey Kaplun via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 5/6] dbg: generalize extension Sergey Kaplun via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 6/6] ci: introduce workflow to test debugger extension Sergey Kaplun via Tarantool-patches
2026-05-20 13:52   ` Sergey Bronnikov via Tarantool-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3703735e-192b-4e3f-8842-2cff7a61e725@tarantool.org \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=sergeyb@tarantool.org \
    --cc=skaplun@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH v2 luajit 1/6] test: introduce tests for debugging extensions' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox