From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 9AE5A722123; Wed, 6 Dec 2023 17:16:26 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 9AE5A722123 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1701872186; bh=4DIagIfDbwazL5lnwAg/f2agiPneZMf6mBtK+zcH5cM=; h=To:Cc:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=vm0z3Z1h+sgoD4XWMSpNylSBQt9T+V3stHp171B11td1ZRijmkvbNi6ti+CP42hVt gF3z0fHBM6rpbD9mjVZ4ZO4x6X5KZC0f7rhftb5GXJyK/LkbDaxbHFKBYS1qaX6MDS W1vJPsem99t8USWr7b75yScDCnXH3wOk4yHS1h7U= Received: from mail-lj1-f172.google.com (mail-lj1-f172.google.com [209.85.208.172]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id BFD10722122 for ; Wed, 6 Dec 2023 17:15:28 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org BFD10722122 Received: by mail-lj1-f172.google.com with SMTP id 38308e7fff4ca-2c9fe0ef02aso9982221fa.0 for ; Wed, 06 Dec 2023 06:15:28 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701872128; x=1702476928; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=wKos93LWknvSusYtKldx3VOE7lWDLtvHWoy9bSOML+U=; b=UCD9K91QcYedfITw5rIp9eKDiDna+jiYQBhWa4BXrDDQ30aDToUK4bCjDgm1sH/ggT mIYzI29Rs2CGQ+Ng5pe+kO2GyUkagT/xobVMoeQj/kru6SIH8AFESzbWxaPquAQ7EMNN ciTWXrp7pN1CehC3z24myBdwzYDIZUdsdcr6xTv4AgdLlvflM6E6VzwdgS3STlOaq8v+ /QwpT/65hvGl0NUQem9AknBMKVs1LgTZlY1YOrX1KC7qoaVYt1qbNNuKsMHOEfJwr44o B7+du5nN2beartVDlvDPcqATMd5DYHfTkQnwK269+/BH9jC3hxvrCZlYY45ZedCkx6eR Dr1A== X-Gm-Message-State: AOJu0YxdhOGcGBI98zIbaekvTo7LsdbdtvFeDOWb1T7v5OtlLXXq0aWt L7cmhf+ubkJWV9tCiWjBgqAI0ZkQYdaqeQ== X-Google-Smtp-Source: AGHT+IHdfN0u6KPbFeql8s48ZrV0Hds1ynz9jbTvVG/vR3ZYZj/GJYTTqtBEZ+Mm62jifQrW5AvaIw== X-Received: by 2002:a2e:8610:0:b0:2ca:4bb:a89d with SMTP id a16-20020a2e8610000000b002ca04bba89dmr2275817lji.7.1701872127671; Wed, 06 Dec 2023 06:15:27 -0800 (PST) Received: from fckxorg.mail.msk ([2a00:1148:b0ba:16:a3e8:bdc1:dbed:dbc8]) by smtp.gmail.com with ESMTPSA id u7-20020a2ea167000000b002c9fc51f34csm1158048ljl.32.2023.12.06.06.15.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Dec 2023 06:15:27 -0800 (PST) X-Google-Original-From: Maxim Kokryashkin To: tarantool-patches@dev.tarantool.org, skaplun@tarantool.org, sergeyb@tarantool.org, imun@tarantool.org Cc: Maksim Kokryashkin Date: Wed, 6 Dec 2023 17:14:56 +0300 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH luajit v5 2/2] test: add tests for debugging extensions X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Maxim Kokryashkin via Tarantool-patches Reply-To: Maxim Kokryashkin Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Maksim Kokryashkin This patch adds tests for LuaJIT debugging extensions for lldb and gdb. --- .flake8rc | 4 + src/luajit_dbg.py | 11 +- test/CMakeLists.txt | 3 + .../CMakeLists.txt | 102 +++++++ .../debug-extension-tests.py | 262 ++++++++++++++++++ 5 files changed, 376 insertions(+), 6 deletions(-) create mode 100644 test/LuaJIT-debug-extensions-tests/CMakeLists.txt create mode 100644 test/LuaJIT-debug-extensions-tests/debug-extension-tests.py 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 diff --git a/src/luajit_dbg.py b/src/luajit_dbg.py index e79b7c32..a42d8f25 100644 --- a/src/luajit_dbg.py +++ b/src/luajit_dbg.py @@ -40,14 +40,13 @@ class Debugger(object): lib = None try: lib = import_module(name) - except ImportError: + if healthcheck(lib): + setattr(self, name.upper(), True) + globals()[name] = lib + self.name = name + except Exception: continue - if healthcheck(lib): - setattr(self, name.upper(), True) - globals()[name] = lib - self.name = name - assert self.LLDB != self.GDB def setup_target(self, debugger): diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8afc42df..83d4bfc7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -79,6 +79,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) add_custom_target(${PROJECT_NAME}-test DEPENDS LuaJIT-tests @@ -86,6 +87,8 @@ add_custom_target(${PROJECT_NAME}-test DEPENDS lua-Harness-tests tarantool-c-tests tarantool-tests + LuaJIT-lldb-extension-tests + LuaJIT-gdb-extension-tests ) if(LUAJIT_USE_TEST) diff --git a/test/LuaJIT-debug-extensions-tests/CMakeLists.txt b/test/LuaJIT-debug-extensions-tests/CMakeLists.txt new file mode 100644 index 00000000..78eafd02 --- /dev/null +++ b/test/LuaJIT-debug-extensions-tests/CMakeLists.txt @@ -0,0 +1,102 @@ +add_custom_target(LuaJIT-gdb-extension-tests + DEPENDS ${LUAJIT_TEST_BINARY} +) + +add_custom_target(LuaJIT-lldb-extension-tests + DEPENDS ${LUAJIT_TEST_BINARY} +) + +set(DUMMY_GDB_MSG "LuaJIT-gdb-extension-tests is a dummy target") +set(DUMMY_LLDB_MSG "LuaJIT-lldb-extension-tests is a dummy target") + +# Debug info is required for testing of extensions. +if(NOT (CMAKE_BUILD_TYPE MATCHES Debug)) + message(WARNING + "not a DEBUG build, LuaJIT-lldb-extension-tests and " + "LuaJIT-gdb-extension-tests are dummy" + ) + add_custom_command(TARGET LuaJIT-gdb-extension-tests + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red ${DUMMY_GDB_MSG} + ) + add_custom_command(TARGET LuaJIT-lldb-extension-tests + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red ${DUMMY_LLDB_MSG} + ) + 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," + "LuaJIT-lldb-extension-tests is dummy" + ) + add_custom_command(TARGET LuaJIT-lldb-extension-tests + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red ${DUMMY_LLDB_MSG} + ) + return() +endif() + +find_package(PythonInterp) +if(NOT PYTHONINTERP_FOUND) + message(WARNING + "`python` is not found, LuaJIT-lldb-extension-tests and " + "LuaJIT-gdb-extension-tests are dummy" + ) + add_custom_command(TARGET LuaJIT-gdb-extension-tests + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red ${DUMMY_GDB_MSG} + ) + add_custom_command(TARGET LuaJIT-lldb-extension-tests + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red ${DUMMY_LLDB_MSG} + ) + return() +endif() + +set(DEBUGGER_TEST_ENV + "LUAJIT_TEST_BINARY=${LUAJIT_TEST_BINARY}" + # Suppresses __pycache__ generation. + "PYTHONDONTWRITEBYTECODE=1" + "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 +) + +find_program(GDB gdb) +if(GDB) + set(GDB_TEST_ENV ${DEBUGGER_TEST_ENV} + "DEBUGGER_COMMAND=${GDB}" + ) + add_custom_command(TARGET LuaJIT-gdb-extension-tests + COMMENT "Running luajit_dbg.py tests with gdb" + COMMAND + ${GDB_TEST_ENV} ${PYTHON_EXECUTABLE} ${TEST_SCRIPT_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +else() + message(WARNING "`gdb' is not found, so LuaJIT-gdb-extension-tests is dummy") + add_custom_command(TARGET LuaJIT-gdb-extension-tests + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red ${DUMMY_GDB_MSG} + ) +endif() + +find_program(LLDB lldb) +if(LLDB) + set(LLDB_TEST_ENV ${DEBUGGER_TEST_ENV} + "DEBUGGER_COMMAND=${LLDB}" + ) + add_custom_command(TARGET LuaJIT-lldb-extension-tests + COMMENT "Running luajit_dbg.py tests with lldb" + COMMAND + ${LLDB_TEST_ENV} ${PYTHON_EXECUTABLE} ${TEST_SCRIPT_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +else() + message(WARNING "`lldb' is not found, so LuaJIT-lldb-extension-tests is dummy") + add_custom_command(TARGET LuaJIT-lldb-extension-tests + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red ${DUMMY_LLDB_MSG} + ) +endif() 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..818247ca --- /dev/null +++ b/test/LuaJIT-debug-extensions-tests/debug-extension-tests.py @@ -0,0 +1,262 @@ +# 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 = os.environ['DEBUGGER_EXTENSION_PATH'] +DEBUGGER = os.environ['DEBUGGER_COMMAND'] +LLDB = 'lldb' in DEBUGGER +TIMEOUT = 10 + +RUN_CMD_FILE = '-s' if LLDB else '-x' +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) + + +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, + ) + timer = Timer(TIMEOUT, process.kill) + timer.start() + stdout, stderr = 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'), stderr.decode('ascii') + else: + process = subprocess.run(cmd, capture_output=True, timeout=TIMEOUT) + return process.stdout.decode('ascii'), process.stderr.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), + ), + ) + + +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, cls.stderr = execute_process(process_cmd) + cls.output = filter_debugger_output(cls.output) + cmd_file.close() + script_file.close() + + def check(self): + if self.stderr != '': + raise self.failureException( + 'Non-empty stderr: {}'.format(self.stderr), + ) + + pattern = self.pattern.strip() + match = re.search(pattern, self.output) + + if not match: + fmt = '\nEXPECTED PATTERN:\n{}\n\nGOT OUTPUT:\n{}' + raise self.failureException(fmt.format(pattern, self.output)) + + +class TestLoad(TestCaseBase): + extension_cmds = '' + 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' + ) + + +class TestLJArch(TestCaseBase): + extension_cmds = 'lj-arch' + location = 'lj_cf_print' + lua_script = 'print(1)' + pattern = ( + 'LJ_64: (True|False), ' + 'LJ_GC64: (True|False), ' + 'LJ_DUALNUM: (True|False)' + ) + + +class TestLJState(TestCaseBase): + extension_cmds = 'lj-state' + location = 'lj_cf_print' + lua_script = 'print(1)' + pattern = ( + 'VM state: [A-Z]+\n' + 'GC state: [A-Z]+\n' + 'JIT state: [A-Z]+\n' + ) + + +class TestLJGC(TestCaseBase): + extension_cmds = 'lj-gc' + 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' + ) + + +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' + '-+ 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' + '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 = ( + '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): + extension_cmds = 'lj-str fname' + location = 'lj_cf_dofile' + lua_script = 'pcall(dofile("name"))' + 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.43.0