Tarantool development patches archive
 help / color / mirror / Atom feed
From: Sergey Kaplun via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Maxim Kokryashkin <max.kokryashkin@gmail.com>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH luajit v6 2/2] test: add tests for debugging extensions
Date: Wed, 17 Apr 2024 19:00:27 +0300	[thread overview]
Message-ID: <Zh_yGzhyJ30jsEha@root> (raw)
In-Reply-To: <121161805fff1e9b5855f1092bc16c24af3da3de.1712182830.git.m.kokryashkin@tarantool.org>

Hi, Maxim!
Thanks for the patch!
Please consider my comments below.

On 04.04.24, Maxim Kokryashkin wrote:
> From: Maksim Kokryashkin <max.kokryashkin@gmail.com>
> 
> This patch adds tests for LuaJIT debugging
> extensions for lldb and gdb.
> ---
>  .flake8rc                                     |   4 +
>  test/CMakeLists.txt                           |   1 +
>  .../CMakeLists.txt                            |  80 ++++++
>  .../debug-extension-tests.py                  | 250 ++++++++++++++++++
>  4 files changed, 335 insertions(+)
>  create mode 100644 test/LuaJIT-debug-extensions-tests/CMakeLists.txt
>  create mode 100644 test/LuaJIT-debug-extensions-tests/debug-extension-tests.py

I see no updates of the CI actions for these tests, so tests are skipped
if the CI runner has no installed python|lldb|gdb.

> 
> diff --git a/.flake8rc b/.flake8rc
> index 13e6178f..6766ed41 100644
> --- a/.flake8rc
> +++ b/.flake8rc
> @@ -3,3 +3,7 @@ extend-ignore =
>    # XXX: Suppress F821, since we have autogenerated names for
>    # 'ptr' type complements in luajit_lldb.py.
>    F821
> +per-file-ignores =
> +  # XXX: Flake considers regexp special characters to be
> +  # escape sequences.
> +  test/LuaJIT-debug-extensions-tests/debug-extension-tests.py:W605

Do we need this ignore?
IINM, we can just use the `r` prefix for all regex strings that produce
the warning. See [1].

> diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
> index 19726f5a..a3b48939 100644
> --- a/test/CMakeLists.txt
> +++ b/test/CMakeLists.txt
> @@ -148,6 +148,7 @@ add_subdirectory(PUC-Rio-Lua-5.1-tests)
>  add_subdirectory(lua-Harness-tests)
>  add_subdirectory(tarantool-c-tests)
>  add_subdirectory(tarantool-tests)
> +add_subdirectory(LuaJIT-debug-extensions-tests)

I suggest renaming like "LuaJIT-debug-extensions-tests" ->
"tarantool-debugger-tests" to be consistent with other of our tests
(tarantool-tests, tarantool-c-tests) since the debugger itself is a part
of our test suite, not LuaJIT's.

Also, please notice that entries are alphabetically sorted.

>  
>  # Each testsuite has its own CMake target, but combining these
>  # target into a single one is not desired, because each target
> diff --git a/test/LuaJIT-debug-extensions-tests/CMakeLists.txt b/test/LuaJIT-debug-extensions-tests/CMakeLists.txt
> new file mode 100644
> index 00000000..9ac626ec
> --- /dev/null
> +++ b/test/LuaJIT-debug-extensions-tests/CMakeLists.txt
> @@ -0,0 +1,80 @@
> +SET(TEST_SUITE_NAME "LuaJIT-dbg-extension-tests")
> +add_test_suite_target(LuaJIT-dbg-extension-tests
> +  LABELS ${TEST_SUITE_NAME}
> +  DEPENDS ${LUAJIT_TEST_BINARY}
> +)
> +
> +# Debug info is required for testing of extensions.

Strictly saying we can have debug symbols for the "RelWithDebInfo" build
too. OTOH, values in registers return <optimized> instead of the desired
output for the build, so skipping the test for non-Debug builds is not a
bad idea after all (at least until we noticed some regression related to
this build).

Adjust a comment if you still don't want to test the RelWithDebInfo build.

> +if(NOT (CMAKE_BUILD_TYPE MATCHES Debug))
> +  message(WARNING
> +    "not a DEBUG build, LuaJIT-lldb-extension-tests and "

I see a lot of usage of these names below. It will be better to set
variables for them.

Also, the names were changed after the previous review iteration, so
please update the names to be up-to-date.

> +    "LuaJIT-gdb-extension-tests are dummy"
> +  )
> +  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.

Minor: I suppose we should add the corresponding ticket to the
tarantool-qa repo. It's understandable that MacOS isn't a top priority,
so probably this will not be done soon, but having a ticket with a
rationale and using the link to it here will still be nice.

> +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND DEFINED ENV{CI})
> +  message(WARNING
> +    "Interactive debugging is unavailable for macOS CI builds,"
> +    "LuaJIT-lldb-extension-tests is dummy"
> +  )
> +  return()
> +endif()
> +
> +find_package(PythonInterp)

Please fix the following warning policy.
| CMake Warning (dev) at test/LuaJIT-debug-extensions-tests/CMakeLists.txt:28 (find_package):
|   Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
|   are removed.  Run "cmake --help-policy CMP0148" for policy details.  Use
|   the cmake_policy command to set the policy and suppress this warning.
|
| This warning is for project developers.  Use -Wno-dev to suppress it.

IINM, you may workaround this for old CMake versions like the
following [1]:

| if(POLICY CMP0148)
|   # This policy is not known to old CMake.
|   cmake_policy(SET CMP0148 OLD)
| endif()

> +if(NOT PYTHONINTERP_FOUND)
> +  message(WARNING
> +    "`python` is not found, LuaJIT-lldb-extension-tests and "
> +    "LuaJIT-gdb-extension-tests are dummy"
> +  )
> +  return()
> +endif()
> +
> +set(DEBUGGER_TEST_ENV
> +  "LUAJIT_TEST_BINARY=${LUAJIT_TEST_BINARY}"
> +  # Suppresses __pycache__ generation.
> +  "PYTHONDONTWRITEBYTECODE=1"

As an alternative it may be done via the -B flag.
Since the -E flag will(?) ignore the value of this env variable, I
suggest to using flags instead.

> +  "DEBUGGER_EXTENSION_PATH=${PROJECT_SOURCE_DIR}/src/luajit_dbg.py"
> +)
> +
> +set(TEST_SCRIPT_PATH
> +  ${PROJECT_SOURCE_DIR}/test/LuaJIT-debug-extensions-tests/debug-extension-tests.py

Why don't use ${CMAKE_CURRENT_SOURCE_DIR} instead?

> +)
> +
> +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}

I think it will be nice to add the -E option here to avoid interference
with the user's env variables that are set locally.

> +    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
> +  )
> +  set_tests_properties("${test_title}" PROPERTIES
> +    ENVIRONMENT "${GDB_TEST_ENV}"
> +    LABELS ${TEST_SUITE_NAME}
> +    DEPENDS LuaJIT-dbg-extension-tests-deps
> +  )
> +else()
> +  message(WARNING "`gdb' is not found, so LuaJIT-gdb-extension-tests is dummy")
> +endif()
> +
> +find_program(LLDB lldb)

May we use some macro here to avoid copy-pasting?
I suggest using the following prototype:
| macro(AddTestDebugger target dbg)
| # ...
| endmacro()

Side note: The macro presumes that target goes first to be similar to
the rest of the CMake semantics.

> +if(LLDB)

<snipped>

> diff --git a/test/LuaJIT-debug-extensions-tests/debug-extension-tests.py b/test/LuaJIT-debug-extensions-tests/debug-extension-tests.py
> new file mode 100644
> index 00000000..6ef87473
> --- /dev/null
> +++ b/test/LuaJIT-debug-extensions-tests/debug-extension-tests.py
> @@ -0,0 +1,250 @@
> +# 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)

Have we any runners with Python 2? If the answer is "no", do we need
these workarounds if there are no guarantees that tests will work for
Python 2?

> +
> +LUAJIT_BINARY = os.environ['LUAJIT_TEST_BINARY']
> +EXTENSION = os.environ['DEBUGGER_EXTENSION_PATH']
> +DEBUGGER = os.environ['DEBUGGER_COMMAND']
> +LLDB = 'lldb' in DEBUGGER
> +TIMEOUT = 10

Side note: I suppose the timeout should be adjusted after adding the CI
checks.

> +
> +RUN_CMD_FILE = '-s' if LLDB else '-x'
> +INFERIOR_ARGS = '--' if LLDB else '--args'

It will be nice to add some flags to avoid loading of users .gdbinit
(`--nx` [3][4]) and .lldbinit (`--no-lldbinit` [5]).

> +PROCESS_RUN = 'process launch' if LLDB else 'r'
> +LOAD_EXTENSION = (
> +    'command script import {ext}' if LLDB else 'source {ext}'
> +).format(ext=EXTENSION)
> +
> +
> +def persist(data):
> +    tmp = tempfile.NamedTemporaryFile(mode='w')
> +    tmp.write(data)
> +    tmp.flush()
> +    return tmp
> +
> +

<snipped>

> +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 = filter_debugger_output(execute_process(process_cmd))
> +        cmd_file.close()
> +        script_file.close()

It would be nice to mention that `NamedTemporaryFile()` has
the `delete=True` default to remove those files.

> +
> +    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)'

I suppose we may use these values of `location` and `lua_script` as
defaults.
It is usefull for base tests and simplifies reading.

Also, we may add the comment there that `print()` isn't actually
executed since we quit before it finishes, so it's not spoiling the
output.

> +    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'
> +    )
> +
> +
> +class TestLJArch(TestCaseBase):

<snipped>

> +    )
> +
> +
> +class TestLJState(TestCaseBase):

<snipped>

> +    )
> +
> +
> +class TestLJGC(TestCaseBase):

<snipped>

> +    )
> +
> +
> +class TestLJStack(TestCaseBase):
> +    extension_cmds = 'lj-stack'
> +    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'

I suppose you mean 0x[a-fA-F0-9]?
Here and below.

I see that this pattern for hexademic address is used a lot. Can we
predefine it above? Same for double usage of the stack pattern.

> +        '-+ 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'
> +    )
> +
> +
> +class TestLJTV(TestCaseBase):
> +    location = 'lj_cf_print'
> +    lua_script = 'print(1)'
> +    extension_cmds = (
> +        'lj-tv L->base\n'

<snipped>

> +        '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'

It would be nice to add a check for userdata too, using `newproxy()`.
Also, it would be nice to sort the inputs in LJ_T* order. See
<test/tarantool-tests/lj-351-print-tostring-number.test.lua>, for
example.

> +    )
> +
> +    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'
> +    )
> +
> +
> +class TestLJStr(TestCaseBase):

I've got the following timeouts locally, when build in debug mode:

| 3/3 Test #2: test/LuaJIT-dbg-extension-tests/gdb ....***Failed  304.54 sec
| test (__main__.TestLJArch.test) ... ok
| test (__main__.TestLJGC.test) ... ok
| test (__main__.TestLJStack.test) ... ok
| test (__main__.TestLJState.test) ... ok
| setUpClass (__main__.TestLJStr) ... ERROR
| test (__main__.TestLJTV.test) ... ok
| test (__main__.TestLJTab.test) ... ok
| test (__main__.TestLoad.test) ... ok
| test (__main__.TestLJTab.test) ... ok
|
| ======================================================================
| ERROR: setUpClass (__main__.TestLJStr)
| ----------------------------------------------------------------------
| Traceback (most recent call last):
|   File "/home/burii/reviews/luajit/lj-dbg/test/LuaJIT-debug-extensions-tests/debug-extension-tests.py", line 88, in setUpClass
|     cls.output = filter_debugger_output(execute_process(process_cmd))
|                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|   File "/home/burii/reviews/luajit/lj-dbg/test/LuaJIT-debug-extensions-tests/debug-extension-tests.py", line 50, in execute_process
|     process = subprocess.run(cmd, capture_output=True, timeout=TIMEOUT)
|               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|   File "/usr/lib/python3.11/subprocess.py", line 550, in run
|     stdout, stderr = process.communicate(input, timeout=timeout)
|                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|   File "/usr/lib/python3.11/subprocess.py", line 1209, in communicate
|     stdout, stderr = self._communicate(input, endtime, timeout)
|                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|   File "/usr/lib/python3.11/subprocess.py", line 2114, in _communicate
|     self._check_timeout(endtime, orig_timeout, stdout, stderr)
|   File "/usr/lib/python3.11/subprocess.py", line 1253, in _check_timeout
|     raise TimeoutExpired(

The problem is that the `fname` contains invalid address at the moment
of breakpoint (for the Debug build), so the error is raised.
| /tmp/tmpltp1awoe:5: Error in sourced command file:


The following dirty-patch fixes the issue for me:
|  class TestLJStr(TestCaseBase):
| -    extension_cmds = 'lj-str fname'
| +    extension_cmds = 'n\nlj-str fname'

I suppose other places where we use local variables should be adjusted
as well.

> +    extension_cmds = 'lj-str fname'
> +    location = 'lj_cf_dofile'
> +    lua_script = 'pcall(dofile("name"))'

I suppose you mean:
| pcall(dofile, "name")

If we are not interested in the status of child process we may just
avoid the `pcall()` at all.
Also, I suppose we may use something from the `string` built-in library
to check the `lj-str` command.

> +    pattern = 'String: .* \[\d+ bytes\] with hash 0x[a-zA-Z0-9]+'
> +
> +
> +class TestLJTab(TestCaseBase):
> +    extension_cmds = 'lj-tab t'
> +    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'
> +    )
> +
> +
> +for test_cls in TestCaseBase.__subclasses__():
> +    test_cls.test = lambda self: self.check()
> +
> +if __name__ == '__main__':
> +    unittest.main(verbosity=2)
> -- 
> 2.44.0
> 

[1]: https://www.flake8rules.com/rules/W605.html
[2]: https://cmake.org/cmake/help/book/mastering-cmake/chapter/Policies.html#supporting-multiple-cmake-versions
[3]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Mode-Options.html
[4]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Initialization-Files.html#Initialization-Files
[5]: https://lldb.llvm.org/man/lldb.html

-- 
Best regards,
Sergey Kaplun

      parent reply	other threads:[~2024-04-17 16:04 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-03 22:21 [Tarantool-patches] [PATCH luajit v6 0/2] debug: generalized extension Maxim Kokryashkin via Tarantool-patches
2024-04-03 22:21 ` [Tarantool-patches] [PATCH luajit v6 1/2] " Maxim Kokryashkin via Tarantool-patches
2024-04-04 10:14   ` Sergey Bronnikov via Tarantool-patches
2024-04-17 16:00   ` Sergey Kaplun via Tarantool-patches
2024-04-17 22:42     ` Maxim Kokryashkin via Tarantool-patches
2024-04-18  8:00       ` Sergey Kaplun via Tarantool-patches
2024-04-03 22:21 ` [Tarantool-patches] [PATCH luajit v6 2/2] test: add tests for debugging extensions Maxim Kokryashkin via Tarantool-patches
2024-04-04 10:27   ` Sergey Bronnikov via Tarantool-patches
2024-04-08  9:45     ` Maxim Kokryashkin via Tarantool-patches
2024-04-17 16:00   ` Sergey Kaplun via Tarantool-patches [this message]

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=Zh_yGzhyJ30jsEha@root \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=max.kokryashkin@gmail.com \
    --cc=skaplun@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH luajit v6 2/2] test: add 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