diff --git a/.github/workflows/conda-package.yml b/.github/workflows/conda-package.yml index 4556abcd78..74ed576dde 100644 --- a/.github/workflows/conda-package.yml +++ b/.github/workflows/conda-package.yml @@ -254,8 +254,11 @@ jobs: - name: Run gdb tests if: ${{ matrix.scope == 'gdb' }} env: - GDB_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/fcea1bcd-6a63-4849-b304-906ff71dc2c0/l_dpcpp_dbg_p_2023.2.0.49333_offline.sh - GDB_INSTALLER: l_dpcpp_dbg_p_2023.2.0.49333_offline.sh + GDB_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/72038301-92f9-4db2-8fe0-db1fe9e9bca9/l_dpcpp_dbg_p_2024.0.1.9_offline.sh + GDB_INSTALLER: l_dpcpp_dbg_p_2024.0.1.9_offline.sh + # To read gdb communication in case test fails to determine what the + # issue is + NUMBA_DPEX_TESTING_LOG_DEBUGGING: 1 run: | conda install pexpect wget -nc -q ${{ env.GDB_URL }} @@ -264,6 +267,14 @@ jobs: export ONEAPI_ROOT=/tmp/gdb ./${{ env.GDB_INSTALLER }} -a -s --eula accept --install-dir $ONEAPI_ROOT source $ONEAPI_ROOT/debugger/latest/env/vars.sh + # We match only major and minor version because latest gdb is not + # often available. + gdb_version=$(echo "$GDB_INSTALLER" | grep -oP '\d+\.\d+' | head -n 1) + icpx_version=$(conda list dpcpp-cpp-rt | tail -n 1 | awk '{print $2}' | grep -oP '\d+\.\d+') + if [ "$gdb_version" != "$icpx_version" ]; then + echo "Error: GDB version ($gdb_version) does not match icpx version ($icpx_version)" + exit 1 + fi pytest -q -ra --disable-warnings --pyargs ${{ env.MODULE_NAME }}.tests.debugging -vv upload_anaconda: diff --git a/numba_dpex/examples/debug/side-by-side-2.py b/numba_dpex/examples/debug/side-by-side-2.py index ce0144d7a6..15301b7a3e 100644 --- a/numba_dpex/examples/debug/side-by-side-2.py +++ b/numba_dpex/examples/debug/side-by-side-2.py @@ -5,6 +5,7 @@ import argparse import dpctl +import dpnp import numba import numpy as np @@ -24,23 +25,31 @@ def scenario(api): print("Using API:", api) global_size = 10 - a, b, c = arguments(global_size) if api == "numba-ndpx-kernel": + a, b, c = ndpx_arguments(global_size) ndpx_func_driver(a, b, c) else: + a, b, c = numba_arguments(global_size) numba_func_driver(a, b, c) print(a, b, c, sep="\n") -def arguments(N, dtype=np.float32): +def numba_arguments(N, dtype=np.float32): a = np.arange(N, dtype=dtype) b = np.arange(N, dtype=dtype) c = np.empty_like(a) return a, b, c +def ndpx_arguments(N, dtype=dpnp.float32): + a = dpnp.arange(N, dtype=dtype) + b = dpnp.arange(N, dtype=dtype) + c = dpnp.empty_like(a) + return a, b, c + + @numba.njit(debug=True) def numba_func_driver(a, b, c): for i in range(len(c)): @@ -48,9 +57,7 @@ def numba_func_driver(a, b, c): def ndpx_func_driver(a, b, c): - device = dpctl.select_default_device() - with dpctl.device_context(device): - kernel[ndpx.Range(len(c))](a, b, c) + kernel[ndpx.Range(len(c))](a, b, c) @ndpx.kernel(debug=True) diff --git a/numba_dpex/examples/debug/side-by-side.py b/numba_dpex/examples/debug/side-by-side.py index 514df9c9fa..0c26d5be71 100644 --- a/numba_dpex/examples/debug/side-by-side.py +++ b/numba_dpex/examples/debug/side-by-side.py @@ -5,6 +5,7 @@ import argparse import dpctl +import dpnp import numba import numpy as np @@ -12,8 +13,8 @@ def common_loop_body(param_a, param_b): - param_c = param_a + 10 # Set breakpoint here - param_d = param_b * 0.5 + param_c = param_a + numba.float32(10) # Set breakpoint here + param_d = param_b * numba.float32(0.5) result = param_c + param_d return result @@ -22,23 +23,31 @@ def scenario(api): print("Using API:", api) global_size = 10 - a, b, c = arguments(global_size) if api == "numba-ndpx-kernel": + a, b, c = ndpx_arguments(global_size) ndpx_func_driver(a, b, c) else: + a, b, c = numba_arguments(global_size) numba_func_driver(a, b, c) print(a, b, c, sep="\n") -def arguments(N, dtype=np.float32): +def numba_arguments(N, dtype=np.float32): a = np.arange(N, dtype=dtype) b = np.arange(N, dtype=dtype) c = np.empty_like(a) return a, b, c +def ndpx_arguments(N, dtype=dpnp.float32): + a = dpnp.arange(N, dtype=dtype) + b = dpnp.arange(N, dtype=dtype) + c = dpnp.empty_like(a) + return a, b, c + + @numba.njit(debug=True) def numba_func_driver(a, b, c): for i in range(len(c)): @@ -46,9 +55,7 @@ def numba_func_driver(a, b, c): def ndpx_func_driver(a, b, c): - device = dpctl.select_default_device() - with dpctl.device_context(device): - kernel[ndpx.Range(len(c))](a, b, c) + kernel[ndpx.Range(len(c))](a, b, c) @ndpx.kernel(debug=True) diff --git a/numba_dpex/examples/debug/simple_sum.py b/numba_dpex/examples/debug/simple_sum.py index bcdb2f0c98..0bfa88396f 100644 --- a/numba_dpex/examples/debug/simple_sum.py +++ b/numba_dpex/examples/debug/simple_sum.py @@ -2,7 +2,6 @@ # # SPDX-License-Identifier: Apache-2.0 -import dpctl import dpnp as np import numba_dpex as ndpx diff --git a/numba_dpex/tests/_helper.py b/numba_dpex/tests/_helper.py index 2d86d5e77a..db9f17f913 100644 --- a/numba_dpex/tests/_helper.py +++ b/numba_dpex/tests/_helper.py @@ -134,16 +134,11 @@ def is_windows(): pytest.param("level_zero:gpu:0", marks=skip_no_level_zero_gpu), ] -skip_no_numba056 = pytest.mark.skipif( - numba_sem_version < (0, 56), reason="Need Numba 0.56 or higher" -) - skip_no_gdb = pytest.mark.skipif( config.TESTING_SKIP_NO_DEBUGGING and not shutil.which("gdb-oneapi"), reason="IntelĀ® Distribution for GDB* is not available", ) - decorators = [ pytest.param(dpjit, id="dpjit"), pytest.param( diff --git a/numba_dpex/tests/debugging/common.py b/numba_dpex/tests/debugging/common.py deleted file mode 100644 index 4ffd0c2a26..0000000000 --- a/numba_dpex/tests/debugging/common.py +++ /dev/null @@ -1,65 +0,0 @@ -#! /usr/bin/env python - -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -"""Common tools for testing debugging""" - -import pathlib - -import numba_dpex - - -def script_path(script): - package_path = pathlib.Path(numba_dpex.__file__).parent - return str(package_path / "examples/debug" / script) - - -def line_number(file_path, text): - """Return line number of the text in the file""" - with open(file_path, "r") as lines: - for line_number, line in enumerate(lines): - if text in line: - return line_number + 1 - - raise RuntimeError(f"Can not find {text} in {file_path}") - - -def breakpoint_by_mark(script, mark, offset=0): - """Return breakpoint for the mark in the script - - Example: breakpoint_by_mark("script.py", "Set here") -> "script.py:25" - """ - return f"{script}:{line_number(script_path(script), mark) + offset}" - - -def breakpoint_by_function(script, function): - """Return breakpoint for the function in the script""" - return breakpoint_by_mark(script, f"def {function}", 1) - - -def setup_breakpoint( - app, - breakpoint: str, - script=None, - expected_location=None, - expected_line=None, -): - if not script: - script = breakpoint.split(" ")[0].split(":")[0] - - if not expected_location: - expected_location = breakpoint.split(" ")[0] - if not expected_location.split(":")[-1].isnumeric(): - expected_location = breakpoint_by_function( - script, expected_location.split(":")[-1] - ) - - app.breakpoint(breakpoint) - app.run(script) - - app.child.expect(rf"Thread .* hit Breakpoint .* at {expected_location}") - - if expected_line: - app.child.expect(expected_line) diff --git a/numba_dpex/tests/debugging/conftest.py b/numba_dpex/tests/debugging/conftest.py index 2c8d857fcb..3d9907b09c 100644 --- a/numba_dpex/tests/debugging/conftest.py +++ b/numba_dpex/tests/debugging/conftest.py @@ -6,9 +6,13 @@ import pytest +from .gdb import gdb + @pytest.fixture def app(): - from .gdb import gdb + g = gdb() + + yield g - return gdb() + g.teardown_gdb() diff --git a/numba_dpex/tests/debugging/gdb.py b/numba_dpex/tests/debugging/gdb.py index 1b3b6f8951..f47fad117b 100644 --- a/numba_dpex/tests/debugging/gdb.py +++ b/numba_dpex/tests/debugging/gdb.py @@ -5,33 +5,51 @@ # SPDX-License-Identifier: Apache-2.0 import os +import pathlib import sys import pytest +import numba_dpex from numba_dpex.core import config -from .common import script_path - if config.TESTING_SKIP_NO_DEBUGGING: pexpect = pytest.importorskip("pexpect") else: import pexpect +RE_DEVICE_ARRAY = ( + r"\{\s*nitems\s+=\s+[0-9]+,\s+" + r"itemsize\s+=\s+[0-9]+,\s+" + r"data\s+=\s+0x[0-9a-f]+,\s+" + r"shape\s+=\s+\{\s*[0-9]+\s*\},\s+" + r"strides\s+=\s+\{\s*[0-9]+\s*\}\s*}" +) + +RE_DEVICE_ARRAY_TYPE = ( + r"DpnpNdArray\(" + r"dtype=[a-z0-9]+,\s+" + r"ndim=[0-9]+,\s+" + r"layout=[A-Z],\s+" + r"address_space=[0-4],\s+" + r"usm_type=[a-z]+,\s+" + r"device=[a-z:0-9]+,\s+" + r"sycl_queue=[A-Za-z:0-9 ]+\)" +) + class gdb: def __init__(self): self.spawn() self.setup_gdb() - def __del__(self): - self.teardown_gdb() - def spawn(self): env = os.environ.copy() env["NUMBA_OPT"] = "0" + env["NUMBA_DPEX_OPT"] = "0" env["NUMBA_EXTEND_VARIABLE_LIFETIMES"] = "1" env["NUMBA_DPEX_DEBUGINFO"] = "1" + env["NUMBA_DEBUGINFO"] = "1" self.child = pexpect.spawn( "gdb-oneapi -q python", env=env, encoding="utf-8" @@ -40,17 +58,17 @@ def spawn(self): self.child.logfile = sys.stdout def setup_gdb(self): - self.child.expect("(gdb)", timeout=5) - self.child.sendline("set breakpoint pending on") - self.child.expect("(gdb)", timeout=5) - self.child.sendline("set style enabled off") # disable colors symbols + self._command("set breakpoint pending on") + self._command("set style enabled off") # disable colors symbols def teardown_gdb(self): self.child.sendintr() self.child.expect("(gdb)", timeout=5) self.child.sendline("quit") - self.child.expect("Quit anyway?", timeout=5) - self.child.sendline("y") + # We want to force quit only if program did not do it itself. + index = self.child.expect(["Quit anyway?", pexpect.EOF], timeout=5) + if index == 0: + self.child.sendline("y") def _command(self, command): self.child.expect("(gdb)", timeout=5) @@ -59,17 +77,58 @@ def _command(self, command): def set_environment(self, varname, value): self._command(f"set environment {varname} {value}") - def breakpoint(self, breakpoint): - self._command("break " + breakpoint) + def breakpoint( + self, + location: str, + condition: str = None, + expected_location=None, + expected_line: int = None, + ): + cmd = f"break {location}" + if condition is not None: + cmd += f" if {condition}" + self._command(cmd) + + if expected_location is not None: + self.child.expect( + rf"Thread .* hit Breakpoint .* at {expected_location}" + ) + + if expected_line is not None: + self.child.expect(f"{expected_line}") def run(self, script): self._command("run " + self.script_path(script)) + def expect(self, pattern, with_eol=False, **kw): + self.child.expect(pattern, **kw) + if with_eol: + self.expect_eol() + + def expect_eol(self): + self.child.expect(r"[^\n]*\n") + + def expect_hit_breakpoint(self, expected_location=None): + expect = r"Thread [0-9A-Za-z \"]+ hit Breakpoint [0-9\.]+" + if expected_location is not None: + # function name + args could be long, so we have to assume that + # the message may be splitted in multiple lines. It potentially can + # cause messed up buffer reading, but it must be extremely rare. + expect += f".* at {expected_location}" + self.child.expect(expect) + self.expect_eol() + def backtrace(self): self._command("backtrace") - def print(self, var): + def print(self, var, expected=None): self._command("print " + var) + if expected is not None: + self.child.expect(rf"\$[0-9]+ = {expected}") + self.expect_eol() + + def set_variable(self, var, val): + self._command(f"set variable {var} = {val}") def info_args(self): self._command("info args") @@ -83,6 +142,9 @@ def info_locals(self): def next(self): self._command("next") + def nexti(self): + self._command("nexti") + def ptype(self, var): self._command("ptype " + var) @@ -95,6 +157,14 @@ def step(self): def stepi(self): self._command("stepi") + def set_scheduler_lock(self): + self._command("set scheduler-locking step") + @staticmethod def script_path(script): return script_path(script) + + +def script_path(script): + package_path = pathlib.Path(numba_dpex.__file__).parent + return str(package_path / "examples/debug" / script) diff --git a/numba_dpex/tests/debugging/test_backtraces.py b/numba_dpex/tests/debugging/test_backtraces.py index 34676c0787..117eccddc0 100644 --- a/numba_dpex/tests/debugging/test_backtraces.py +++ b/numba_dpex/tests/debugging/test_backtraces.py @@ -8,30 +8,24 @@ https://www.sourceware.org/gdb/onlinedocs/gdb/Backtrace.html """ -import pytest from numba_dpex.tests._helper import skip_no_gdb -from .common import setup_breakpoint - pytestmark = skip_no_gdb -@pytest.mark.xfail # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 def test_backtrace(app): """Simple test for backtrace. commands/backtrace """ - setup_breakpoint( - app, - "simple_dpex_func.py:13", - expected_line=r"13\s+result = a_in_func \+ b_in_func", - ) + app.breakpoint("simple_dpex_func.py:12") + app.run("simple_dpex_func.py") + app.expect_hit_breakpoint("simple_dpex_func.py:12") app.backtrace() - app.child.expect(r"#0.*__main__::func_sum .* at simple_dpex_func.py:13") - - -# app.child.expect(r"#1.*__main__::kernel_sum .* at simple_dpex_func.py:20") + app.expect( + r"#0.*__main__::func_sum.* at simple_dpex_func.py:12", with_eol=True + ) + app.expect(r"#1.*__main__::kernel_sum", with_eol=True) diff --git a/numba_dpex/tests/debugging/test_breakpoints.py b/numba_dpex/tests/debugging/test_breakpoints.py index adf0d07473..bcb87fe179 100644 --- a/numba_dpex/tests/debugging/test_breakpoints.py +++ b/numba_dpex/tests/debugging/test_breakpoints.py @@ -9,112 +9,87 @@ https://www.sourceware.org/gdb/onlinedocs/gdb/Set-Breaks.html """ - import pytest -from numba_dpex.tests._helper import skip_no_gdb, skip_no_numba056 +from numba_dpex.tests._helper import skip_no_gdb -from .common import breakpoint_by_function, breakpoint_by_mark, setup_breakpoint +from .gdb import gdb pytestmark = skip_no_gdb -side_by_side_breakpoint = breakpoint_by_function( - "side-by-side.py", "common_loop_body" +@pytest.mark.parametrize( + "breakpoint", + [ + "side-by-side.py:16", + "common_loop_body", + "side-by-side.py:common_loop_body", + ], ) - -simple_sum_condition_breakpoint = breakpoint_by_mark( - "simple_sum.py", "Condition breakpoint location" +@pytest.mark.parametrize( + "api", + [ + "numba", + "numba-ndpx-kernel", + ], ) - -common_loop_body_native_function_name = { - "numba": "common_loop_body", - "numba-dpex-kernel": "common_loop_body", -} - -breakpoint_api_cases = [ - (side_by_side_breakpoint, "numba"), - (side_by_side_breakpoint, "numba-dpex-kernel"), - *((fn, api) for api, fn in common_loop_body_native_function_name.items()), - *( - (f"side-by-side.py:{fn}", api) - for api, fn in common_loop_body_native_function_name.items() - ), -] - - -@skip_no_numba056 -@pytest.mark.parametrize("breakpoint, api", breakpoint_api_cases) -def test_breakpoint_with_condition_by_function_argument(app, breakpoint, api): +@pytest.mark.parametrize( + "condition, exp_var, exp_val", + [ + ("param_a == 3", "param_a", 3), + ("param_a == 7", "param_a", 7), + (None, "param_a", r"[0-9]+"), # No condition + ], +) +def test_device_func_breakpoint( + app: gdb, breakpoint, api, condition, exp_var, exp_val +): """Function breakpoints and argument initializing Test that it is possible to set conditional breakpoint at the beginning of the function and use a function argument in the condition. - It is important that breakpoint by function name hits at the firts line in + It is important that breakpoint by function name hits at the first line in the function body and not at the function definition line. Test for https://github.com/numba/numba/issues/7415 SAT-4449 """ - if api == "numba-dpex-kernel": - pytest.xfail( - "Wrong name for kernel api." - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - variable_name = "param_a" - variable_value = "3" - condition = f"{variable_name} == {variable_value}" + if api == "numba-ndpx-kernel" and breakpoint != "side-by-side.py:16": + pytest.skip( + "Breakpoint by function name not working for numba-dpex." + ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1242 - app.breakpoint(f"{breakpoint} if {condition}") + app.breakpoint(breakpoint, condition=condition) app.run(f"side-by-side.py --api={api}") + app.expect_hit_breakpoint("side-by-side.py:16") + if exp_var is not None: + app.print(exp_var, expected=exp_val) - app.child.expect( - rf"Thread .* hit Breakpoint .* at {side_by_side_breakpoint}" - ) - - app.print(variable_name) - app.child.expect(rf"\$1 = {variable_value}") - - -@pytest.mark.xfail # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 @pytest.mark.parametrize( - "breakpoint, script", + "condition, exp_var, exp_val", [ - # location specified by file name and function name - # commands/break_file_func # noqa: E800 - ("simple_sum.py:data_parallel_sum", None), - # location specified by function name - # commands/break_func # noqa: E800 - ("data_parallel_sum", "simple_sum.py"), - # location specified by file name and nested function name - # commands/break_nested_func # noqa: E800 - ("simple_dpex_func.py:func_sum", None), + ("i == 3", "i", 3), + (None, "i", r"[0-9]+"), # No condition ], ) -def test_breakpoint_common(app, breakpoint, script): - """Set a breakpoint in the given script.""" - setup_breakpoint(app, breakpoint, script=script) - +def test_kernel_breakpoint(app: gdb, condition, exp_var, exp_val): + """Function breakpoints and argument initializing -@pytest.mark.parametrize( - "breakpoint, variable_name, variable_value", - [ - # commands/break_conditional # noqa: E800 - (f"{simple_sum_condition_breakpoint} if i == 1", "i", "1"), - ], -) -def test_breakpoint_with_condition_common( - app, - breakpoint, - variable_name, - variable_value, -): - """Set a breakpoint with condition and check value of variable.""" + Test that it is possible to set conditional breakpoint at the beginning + of the function and use a function argument in the condition. - setup_breakpoint(app, breakpoint) + It is important that breakpoint by function name hits at the first line in + the function body and not at the function definition line. - app.print(variable_name) + Test for https://github.com/numba/numba/issues/7415 + SAT-4449 + """ - app.child.expect(rf"\$1 = {variable_value}") + app.breakpoint("simple_sum.py:13", condition=condition) + app.run("simple_sum.py") + app.expect_hit_breakpoint("simple_sum.py:13") + if exp_var is not None: + app.print(exp_var, expected=exp_val) diff --git a/numba_dpex/tests/debugging/test_common.py b/numba_dpex/tests/debugging/test_common.py deleted file mode 100644 index 45ba586fcd..0000000000 --- a/numba_dpex/tests/debugging/test_common.py +++ /dev/null @@ -1,61 +0,0 @@ -#! /usr/bin/env python - -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -"""Tests for common tools""" - -import pytest - -from numba_dpex.tests._helper import skip_no_gdb - -from .common import breakpoint_by_function, breakpoint_by_mark, setup_breakpoint - -pytestmark = skip_no_gdb - - -@pytest.mark.parametrize( - "file_name, mark, expected", - [("side-by-side.py", "Set breakpoint here", "side-by-side.py:15")], -) -def test_breakpoint_by_mark(file_name, mark, expected): - assert expected == breakpoint_by_mark(file_name, mark) - - -@pytest.mark.parametrize( - "file_name, function, expected", - [("side-by-side.py", "common_loop_body", "side-by-side.py:15")], -) -def test_breakpoint_by_function(file_name, function, expected): - assert expected == breakpoint_by_function(file_name, function) - - -@pytest.mark.parametrize( - "breakpoint, script, expected_location, expected_line", - [ - ( - "simple_sum.py:14", - "simple_sum.py", - "simple_sum.py:14", - r"14\s+c\[i\] = a\[i\] \+ b\[i\]", - ), - ("simple_sum.py:14", "simple_sum.py", "simple_sum.py:14", None), - ("simple_sum.py:14", "simple_sum.py", None, None), - ("simple_sum.py:14", None, None, None), - ("simple_sum.py:data_parallel_sum", None, None, None), - ("data_parallel_sum", "simple_sum.py", None, None), - ], -) -def test_setup_breakpoint( - app, breakpoint, script, expected_location, expected_line -): - if ( - breakpoint == "simple_sum.py:data_parallel_sum" - or breakpoint == "data_parallel_sum" - ): - pytest.xfail( - "Expected failures for these files." - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - - setup_breakpoint(app, breakpoint, script, expected_location, expected_line) diff --git a/numba_dpex/tests/debugging/test_info.py b/numba_dpex/tests/debugging/test_info.py index 297c2653ca..e09d37858a 100644 --- a/numba_dpex/tests/debugging/test_info.py +++ b/numba_dpex/tests/debugging/test_info.py @@ -11,342 +11,146 @@ import pytest -from numba_dpex.tests._helper import skip_no_gdb, skip_no_numba056 +from numba_dpex.tests._helper import skip_no_gdb -from .common import setup_breakpoint -from .test_breakpoints import side_by_side_breakpoint +from .gdb import RE_DEVICE_ARRAY, RE_DEVICE_ARRAY_TYPE, gdb pytestmark = skip_no_gdb -def side_by_side_case(api): - return ( - side_by_side_breakpoint, - f"side-by-side.py --api={api}", - None, - ( - r"param_a = 0", - r"param_b = 0", - ), - ( - "param_a", - r"\$1 = 0", - r"type = float32", - r"type = float32", - ), - ) - - -@skip_no_numba056 @pytest.mark.parametrize( - "breakpoint, script, expected_line, expected_args, expected_info", + "breakpoint, script, expected_line, expected_args", [ ( - "simple_dpex_func.py:19", + "simple_dpex_func.py:18", "simple_dpex_func.py", - r"19\s+i = dpex.get_global_id\(0\)", - ( - r"a_in_kernel = {meminfo = ", - r"b_in_kernel = {meminfo = ", - r"c_in_kernel = {meminfo = ", - ), - ( - "a_in_kernel", - r"\$1 = {meminfo = ", - r"type = struct array\(float32, 1d, C\).*}\)", - r"type = array\(float32, 1d, C\) \({.*}\)", - ), - ), - side_by_side_case("numba"), - side_by_side_case("numba-dpex-kernel"), - ], -) -def test_info_args( - app, breakpoint, script, expected_line, expected_args, expected_info -): - """Test for info args command. - - SAT-4462 - Issue: https://github.com/numba/numba/issues/7414 - Fix: https://github.com/numba/numba/pull/7177 - """ - if ( - script == "simple_dpex_func.py" - or script == "side-by-side.py --api=numba-dpex-kernel" - ): - pytest.xfail( - "Expected Failure for these files." - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - - setup_breakpoint(app, breakpoint, script, expected_line=expected_line) - - app.info_args() - - for arg in expected_args: - app.child.expect(arg) - - variable, expected_print, expected_ptype, expected_whatis = expected_info - - app.print(variable) - app.child.expect(expected_print) - - app.ptype(variable) - app.child.expect(expected_ptype) - - app.whatis(variable) - app.child.expect(expected_whatis) - - -@pytest.mark.xfail # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 -@skip_no_numba056 -def test_info_functions(app): - expected_line = r"13\s+i = dpex.get_global_id\(0\)" - setup_breakpoint(app, "simple_sum.py:13", expected_line=expected_line) - - app.info_functions("data_parallel_sum") - - app.child.expect(r"12:\s+.*__main__::data_parallel_sum\(.*\)") - - -# FIXME: gdb-oneapi isn't stoping with condition -def side_by_side_info_locals_case(api): - return ( - {"NUMBA_OPT": 0}, - "side-by-side.py:17 if param_a == 5", - f"side-by-side.py --api={api}", - None, - ( - r"param_c = 15", - r"param_d = 2.5", - r"result = 0", - ), - (), - ) - - -# FIXME: gdb-oneapi isn't stoping with condition -def side_by_side_2_info_locals_case(api): - return ( - {"NUMBA_OPT": 0}, - "side-by-side-2.py:19 if param_a == 5", - f"side-by-side-2.py --api={api}", - None, - ( - r"param_a = 5", - r"param_b = 5", - r"param_c = 15", - r"param_d = 2.5", - r"result = 0", - ), - ( - ( - r"a", - r"\$1 = {meminfo = ", - r"type = struct array\(float32, 1d, C\)", - r"type = array\(float32, 1d, C\)", - ), - ), - ) - - -@skip_no_numba056 -@pytest.mark.parametrize( - "env, breakpoint, script, expected_line, expected_info_locals, expected_info", - [ - pytest.param( - {"NUMBA_OPT": 0}, - "sum_local_vars.py:16", - "sum_local_vars.py", - r"16\s+c\[i\] = l1 \+ l2", - ( - r"i = 0", - r"l1 = [0-9]\.[0-9]{3}", - r"l2 = [0-9]\.[0-9]{3}", - ), - ( + r"18\s+i = ndpx\.get_global_id\(0\)", + [ ( - "a", - r"\$1 = {meminfo = ", - r"type = struct array\(float32, 1d, C\).*}\)", - r"type = array\(float32, 1d, C\) \({.*}\)", + "a_in_kernel", + RE_DEVICE_ARRAY, + "type = struct " + RE_DEVICE_ARRAY_TYPE, + "type = " + RE_DEVICE_ARRAY_TYPE, ), ( - "l1", - r"\$2 = [0-9]\.[0-9]{3}", - r"type = float64", - r"type = float64", + "b_in_kernel", + RE_DEVICE_ARRAY, + "type = struct " + RE_DEVICE_ARRAY_TYPE, + "type = " + RE_DEVICE_ARRAY_TYPE, ), ( - "l2", - r"\$3 = [0-9]\.[0-9]{3}", - r"type = float64", - r"type = float64", + "c_in_kernel", + RE_DEVICE_ARRAY, + "type = struct " + RE_DEVICE_ARRAY_TYPE, + "type = " + RE_DEVICE_ARRAY_TYPE, ), - ), - marks=pytest.mark.xfail, - ), - # FIXME: NUMBA_OPT=1 will not able to stop at breakpoint - pytest.param( - {"NUMBA_OPT": 1}, - "sum_local_vars.py:16", - "sum_local_vars.py", - r"16\s+c\[i\] = l1 \+ l2", - ("No locals.",), - (), - marks=pytest.mark.xfail, # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - ), - pytest.param( - {"NUMBA_EXTEND_VARIABLE_LIFETIMES": 1}, - "side-by-side.py:18", - "side-by-side.py --api=numba-dpex-kernel", - None, - (r"param_c = 0", r"param_d = 0", r"result = 10"), - (), - marks=pytest.mark.xfail, # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - ), - pytest.param( - {"NUMBA_EXTEND_VARIABLE_LIFETIMES": 0}, - "side-by-side.py:18", - "side-by-side.py --api=numba-dpex-kernel", - None, - (r"param_c = 0", r"param_d = 0", r"result = 10"), - (), - marks=pytest.mark.xfail, # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 + ], ), - side_by_side_info_locals_case("numba"), - pytest.param( - *side_by_side_info_locals_case("numba-dpex-kernel"), - marks=[ - pytest.mark.xfail( - reason="dpex isn't stoping with condition" - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 + ( + "side-by-side.py:16", + "side-by-side.py --api=numba", + r"16\s+param_c = param_a \+ numba\.float32\(10\)", + [ + ("param_a", r"[0-9]+", r"type = float32", r"type = float32"), + ("param_b", r"[0-9]+", r"type = float32", r"type = float32"), ], ), - side_by_side_2_info_locals_case("numba"), - pytest.param( - *side_by_side_2_info_locals_case("numba-dpex-kernel"), - marks=[ - pytest.mark.xfail( - reason="dpex isn't stoping with condition" - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 + ( + "side-by-side.py:16", + "side-by-side.py --api=numba-ndpx-kernel", + r"16\s+param_c = param_a \+ numba\.float32\(10\)", + [ + ("param_a", r"[0-9]+", r"type = float32", r"type = float32"), + ("param_b", r"[0-9]+", r"type = float32", r"type = float32"), ], ), ], ) -def test_info_locals( - app, - env, - breakpoint, - script, - expected_line, - expected_info_locals, - expected_info, +@pytest.mark.parametrize( + "kind", + [ + "print", + "info", + ], +) +def test_info_args( + app: gdb, breakpoint, script, expected_line, expected_args, kind ): - """Test info locals with different environment variables. - - commands/local_variables_0 - commands/local_variables_1 - - SAT-4454 - Provide information about variables (arrays). - Issue: https://github.com/numba/numba/issues/7414 - Fix: https://github.com/numba/numba/pull/7177 - https://github.com/numba/numba/pull/7421 - """ - - for varname, value in env.items(): - app.set_environment(varname, value) + app.breakpoint(breakpoint) + app.run(script) + app.expect_hit_breakpoint(breakpoint) + app.expect(expected_line, with_eol=True) - setup_breakpoint(app, breakpoint, script, expected_line=expected_line) + if kind == "info": + app.info_args() + for var, val, _, _ in expected_args: + app.expect(f"{var} = {val}", with_eol=True) + else: + for var, val, exp_ptype, exp_whatis in expected_args: + app.print(var, expected=val) - app.info_locals() + app.ptype(var) + app.expect(exp_ptype) - for variable in expected_info_locals: - app.child.expect(variable) + app.whatis(var) + app.expect(exp_whatis) - for info in expected_info: - variable, expected_print, expected_ptype, expected_whatis = info - app.print(variable) - app.child.expect(expected_print) - - app.ptype(variable) - app.child.expect(expected_ptype) - - app.whatis(variable) - app.child.expect(expected_whatis) +def test_info_functions(app): + app.breakpoint("simple_sum.py:12") + app.run("simple_sum.py") + app.expect_hit_breakpoint("simple_sum.py:12") + app.expect(r"12\s+i = ndpx.get_global_id\(0\)", with_eol=True) + app.info_functions("data_parallel_sum") -def side_by_side_2_print_array_element_case(api): - return ( - "side-by-side-2.py:19 if param_a == 5", - f"side-by-side-2.py --api={api}", - [(r"a.data[5]", r"\$1 = 5")], - ) + app.child.expect(r"11:\s+[a-z 0-9\*]+__main__::data_parallel_sum") @pytest.mark.parametrize( - "breakpoint, script, expected_info", + "api", [ - side_by_side_2_print_array_element_case("numba"), - pytest.param( - *side_by_side_2_print_array_element_case("numba-dpex-kernel"), - marks=[ - pytest.mark.xfail( - reason="dpex isn't stoping with condition" - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - ], - ), + "numba", + "numba-ndpx-kernel", ], ) -def test_print_array_element(app, breakpoint, script, expected_info): +def test_print_array_element(app, api): """Test access to array elements""" - setup_breakpoint(app, breakpoint, script) - - for info in expected_info: - variable, expected_print = info - - app.print(variable) - app.child.expect(expected_print) - + app.breakpoint("side-by-side-2.py:17 if param_a == 5") + app.run(f"side-by-side-2.py --api={api}") + app.expect_hit_breakpoint("side-by-side-2.py:17") -# FIXME: crashes test execution -@pytest.mark.skip # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 -def side_by_side_2_assignment_to_variable_case(api): - return ( - "side-by-side-2.py:19 if param_a == 5", - f"side-by-side-2.py --api={api}", - [ - (r"param_c", r"\$1 = 15"), - (r"param_c=150", r"\$2 = 150"), - (r"param_c", r"\$3 = 150"), - (r"i", r"\$4 = 5"), - (r"i=50", r"\$5 = 50"), - (r"i", r"\$6 = 50"), - ], - ) + # We can access only c_array, not python style array + app.print("b.data[5]", 5) @pytest.mark.parametrize( - "breakpoint, script, expected_info", + "api", [ - side_by_side_2_assignment_to_variable_case("numba"), - pytest.param( - *side_by_side_2_assignment_to_variable_case("numba-dpex-kernel"), - marks=[ - pytest.mark.xfail( - reason="dpex isn't stoping with condition" - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - ], - ), + "numba", + "numba-ndpx-kernel", ], ) -def test_assignment_to_variable(app, breakpoint, script, expected_info): - setup_breakpoint(app, breakpoint, script) - - for info in expected_info: - variable, expected_print = info - - app.print(variable) - app.child.expect(expected_print) +@pytest.mark.parametrize( + "assign", + [ + "print", + "set_variable", + ], +) +def test_assignment_to_variable(app, api, assign): + app.breakpoint("side-by-side-2.py:18", condition="param_a == 5") + app.run(f"side-by-side-2.py --api={api}") + app.expect_hit_breakpoint("side-by-side-2.py:18") + + app.print("param_a", expected=5) + if assign == "print": + app.print("param_a=15") + else: + app.set_variable("param_a", 15) + app.print("param_a", expected=15) + + # Check that we updated actual value, not gdb environment variable + app.next() + app.print("param_c", expected=25) diff --git a/numba_dpex/tests/debugging/test_side_by_side.py b/numba_dpex/tests/debugging/test_side_by_side.py deleted file mode 100644 index 330a640e00..0000000000 --- a/numba_dpex/tests/debugging/test_side_by_side.py +++ /dev/null @@ -1,30 +0,0 @@ -#! /usr/bin/env python - -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -"""Tests for side-by-side.py script""" - - -import pytest - -from numba_dpex.tests._helper import skip_no_gdb - -pytestmark = skip_no_gdb - - -@pytest.mark.parametrize("api", ["numba", "numba-dpex-kernel"]) -def test_breakpoint_row_number(app, api): - """Test for checking numba and numba-dpex debugging side-by-side.""" - - if api == "numba-dpex-kernel": - pytest.xfail( - "Wrong name for kernel api." - ) # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 - - app.breakpoint("side-by-side.py:15") - app.run("side-by-side.py --api={api}".format(api=api)) - - app.child.expect(r"Breakpoint .* at side-by-side.py:15") - app.child.expect(r"15\s+param_c = param_a \+ 10") diff --git a/numba_dpex/tests/debugging/test_stepping.py b/numba_dpex/tests/debugging/test_stepping.py index d33ca28733..44f7e89246 100644 --- a/numba_dpex/tests/debugging/test_stepping.py +++ b/numba_dpex/tests/debugging/test_stepping.py @@ -13,61 +13,58 @@ from numba_dpex.tests._helper import skip_no_gdb -from .common import setup_breakpoint +from .gdb import gdb pytestmark = skip_no_gdb -# commands/next -@pytest.mark.xfail # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 -def test_next(app): - setup_breakpoint( - app, - "simple_dpex_func.py:20", - expected_line=r"20\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)", - ) - +def test_next(app: gdb): + app.breakpoint("simple_dpex_func.py:18") + app.run("simple_dpex_func.py") + app.expect_hit_breakpoint("simple_dpex_func.py:18") + app.expect(r"18\s+i = ndpx.get_global_id\(0\)", with_eol=True) + app.set_scheduler_lock() app.next() + app.expect( + r"19\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)", + with_eol=True, + ) + # checking that we did not step in app.next() - - app.child.expect(r"Done\.\.\.") + app.expect(r"in _ZN8__main__21kernel_sum_", with_eol=True) -# commands/step_dpex_func -@pytest.mark.xfail # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 -def test_step(app): - setup_breakpoint( - app, - "simple_dpex_func.py:20", - expected_line=r"20\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)", - ) - +def test_step(app: gdb): + app.breakpoint("simple_dpex_func.py:18") + app.run("simple_dpex_func.py") + app.expect_hit_breakpoint("simple_dpex_func.py:18") + app.expect(r"18\s+i = ndpx.get_global_id\(0\)", with_eol=True) + app.set_scheduler_lock() app.step() - app.step() - - app.child.expect(r"__main__::func_sum \(.*\) at simple_dpex_func.py:13") - app.child.expect(r"13\s+result = a_in_func \+ b_in_func") - - -# commands/stepi -@pytest.mark.xfail # TODO: https://github.com/IntelPython/numba-dpex/issues/1216 -def test_stepi(app): - setup_breakpoint( - app, - "simple_dpex_func.py:20", - expected_line=r"20\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)", + app.expect( + r"19\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)", + with_eol=True, ) - - app.stepi() - - app.child.expect( - r"0x[0-f]+\s+20\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)" + app.step() + app.expect(r"__main__::func_sum.* at simple_dpex_func.py:12", with_eol=True) + app.expect(r"12\s+result = a_in_func \+ b_in_func", with_eol=True) + + +@pytest.mark.parametrize("func", ["stepi", "nexti"]) +def test_stepi(app: gdb, func: str): + app.breakpoint("simple_dpex_func.py:19") + app.run("simple_dpex_func.py") + app.expect_hit_breakpoint("simple_dpex_func.py:19") + app.expect( + r"19\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)", + with_eol=True, ) - - app.stepi() - - app.child.expect(r"Switching to Thread") - app.child.expect(r"Thread .* hit Breakpoint .* at simple_dpex_func.py:20") - app.child.expect( - r"20\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)" + app.set_scheduler_lock() + # Stepi/nexti steps over instruction, so the same source code line is + # reached, but with a different instruction address. + f = getattr(app, func) + f() + app.expect( + r"19\s+c_in_kernel\[i\] = func_sum\(a_in_kernel\[i\], b_in_kernel\[i\]\)", + with_eol=True, )