Skip to content

Commit 8ca9b5c

Browse files
committed
Simplify and organize gdb tests
1 parent 1113679 commit 8ca9b5c

File tree

9 files changed

+262
-559
lines changed

9 files changed

+262
-559
lines changed

numba_dpex/examples/debug/side-by-side.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414

1515
def common_loop_body(param_a, param_b):
16-
param_c = param_a + 10 # Set breakpoint here
17-
param_d = param_b * 0.5
16+
param_c = param_a + numba.float32(10) # Set breakpoint here
17+
param_d = param_b * numba.float32(0.5)
1818
result = param_c + param_d
1919
return result
2020

numba_dpex/examples/debug/simple_sum.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#
33
# SPDX-License-Identifier: Apache-2.0
44

5-
import dpctl
65
import dpnp as np
76

87
import numba_dpex as ndpx

numba_dpex/tests/debugging/gdb.py

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@
1818
else:
1919
import pexpect
2020

21+
RE_DEVICE_ARRAY = (
22+
r"\{nitems = [0-9]+,\s+"
23+
r"itemsize = [0-9]+,\s+"
24+
r"data = 0x[0-9a-f]+,\s+"
25+
r"shape = \{[0-9]+\},\s+"
26+
r"strides = \{[0-9]+\}}"
27+
)
28+
29+
RE_DEVICE_ARRAY_TYPE = (
30+
r"DpnpNdArray\("
31+
r"dtype=[a-z0-9]+,\s+"
32+
r"ndim=[0-9]+,\s+"
33+
r"layout=[A-Z],\s+"
34+
r"address_space=[0-4],\s+"
35+
r"usm_type=[a-z]+,\s+"
36+
r"device=[a-z:0-9]+,\s+"
37+
r"sycl_queue=[A-Za-z:0-9 ]+\)"
38+
)
39+
2140

2241
class gdb:
2342
def __init__(self):
@@ -30,6 +49,7 @@ def spawn(self):
3049
env["NUMBA_DPEX_OPT"] = "0"
3150
env["NUMBA_EXTEND_VARIABLE_LIFETIMES"] = "1"
3251
env["NUMBA_DPEX_DEBUGINFO"] = "1"
52+
env["NUMBA_DEBUGINFO"] = "1"
3353

3454
self.child = pexpect.spawn(
3555
"gdb-oneapi -q python", env=env, encoding="utf-8"
@@ -38,10 +58,8 @@ def spawn(self):
3858
self.child.logfile = sys.stdout
3959

4060
def setup_gdb(self):
41-
self.child.expect("(gdb)", timeout=5)
42-
self.child.sendline("set breakpoint pending on")
43-
self.child.expect("(gdb)", timeout=5)
44-
self.child.sendline("set style enabled off") # disable colors symbols
61+
self._command("set breakpoint pending on")
62+
self._command("set style enabled off") # disable colors symbols
4563

4664
def teardown_gdb(self):
4765
self.child.sendintr()
@@ -59,17 +77,58 @@ def _command(self, command):
5977
def set_environment(self, varname, value):
6078
self._command(f"set environment {varname} {value}")
6179

62-
def breakpoint(self, breakpoint):
63-
self._command("break " + breakpoint)
80+
def breakpoint(
81+
self,
82+
location: str,
83+
condition: str = None,
84+
expected_location=None,
85+
expected_line: int = None,
86+
):
87+
cmd = f"break {location}"
88+
if condition is not None:
89+
cmd += f" if {condition}"
90+
self._command(cmd)
91+
92+
if expected_location is not None:
93+
self.child.expect(
94+
rf"Thread .* hit Breakpoint .* at {expected_location}"
95+
)
96+
97+
if expected_line is not None:
98+
self.child.expect(f"{expected_line}")
6499

65100
def run(self, script):
66101
self._command("run " + self.script_path(script))
67102

103+
def expect(self, pattern, with_eol=False, **kw):
104+
self.child.expect(pattern, **kw)
105+
if with_eol:
106+
self.expect_eol()
107+
108+
def expect_eol(self):
109+
self.child.expect(r"[^\n]*\n")
110+
111+
def expect_hit_breakpoint(self, expected_location=None):
112+
expect = r"Thread [0-9A-Za-z \"]+ hit Breakpoint [0-9\.]+"
113+
if expected_location is not None:
114+
# function name + args could be long, so we have to assume that
115+
# the message may be splitted in multiple lines. It potentially can
116+
# cause messed up buffer reading, but it must be extremely rare.
117+
expect += f".* at {expected_location}"
118+
self.child.expect(expect)
119+
self.expect_eol()
120+
68121
def backtrace(self):
69122
self._command("backtrace")
70123

71-
def print(self, var):
124+
def print(self, var, expected=None):
72125
self._command("print " + var)
126+
if expected is not None:
127+
self.child.expect(rf"\$[0-9]+ = {expected}")
128+
self.expect_eol()
129+
130+
def set_variable(self, var, val):
131+
self._command(f"set variable {var} = {val}")
73132

74133
def info_args(self):
75134
self._command("info args")
@@ -83,6 +142,9 @@ def info_locals(self):
83142
def next(self):
84143
self._command("next")
85144

145+
def nexti(self):
146+
self._command("nexti")
147+
86148
def ptype(self, var):
87149
self._command("ptype " + var)
88150

@@ -106,52 +168,3 @@ def script_path(script):
106168
def script_path(script):
107169
package_path = pathlib.Path(numba_dpex.__file__).parent
108170
return str(package_path / "examples/debug" / script)
109-
110-
111-
def line_number(file_path, text):
112-
"""Return line number of the text in the file"""
113-
with open(file_path, "r") as lines:
114-
for line_number, line in enumerate(lines):
115-
if text in line:
116-
return line_number + 1
117-
118-
raise RuntimeError(f"Can not find {text} in {file_path}")
119-
120-
121-
def breakpoint_by_mark(script, mark, offset=0):
122-
"""Return breakpoint for the mark in the script
123-
124-
Example: breakpoint_by_mark("script.py", "Set here") -> "script.py:25"
125-
"""
126-
return f"{script}:{line_number(script_path(script), mark) + offset}"
127-
128-
129-
def breakpoint_by_function(script, function):
130-
"""Return breakpoint for the function in the script"""
131-
return breakpoint_by_mark(script, f"def {function}", 1)
132-
133-
134-
def setup_breakpoint(
135-
app: gdb,
136-
breakpoint: str,
137-
script=None,
138-
expected_location=None,
139-
expected_line=None,
140-
):
141-
if not script:
142-
script = breakpoint.split(" ")[0].split(":")[0]
143-
144-
if not expected_location:
145-
expected_location = breakpoint.split(" ")[0]
146-
if not expected_location.split(":")[-1].isnumeric():
147-
expected_location = breakpoint_by_function(
148-
script, expected_location.split(":")[-1]
149-
)
150-
151-
app.breakpoint(breakpoint)
152-
app.run(script)
153-
154-
app.child.expect(rf"Thread .* hit Breakpoint .* at {expected_location}")
155-
156-
if expected_line:
157-
app.child.expect(expected_line)

numba_dpex/tests/debugging/test_backtraces.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
from numba_dpex.tests._helper import skip_no_gdb
1313

14-
from .gdb import setup_breakpoint
15-
1614
pytestmark = skip_no_gdb
1715

1816

@@ -21,12 +19,13 @@ def test_backtrace(app):
2119
2220
commands/backtrace
2321
"""
24-
setup_breakpoint(
25-
app,
26-
"simple_dpex_func.py:12",
27-
expected_line=r"12\s+result = a_in_func \+ b_in_func",
28-
)
22+
app.breakpoint("simple_dpex_func.py:12")
23+
app.run("simple_dpex_func.py")
24+
app.expect_hit_breakpoint("simple_dpex_func.py:12")
2925

3026
app.backtrace()
3127

32-
app.child.expect(r"#0.*__main__::func_sum.* at simple_dpex_func.py:12")
28+
app.expect(
29+
r"#0.*__main__::func_sum.* at simple_dpex_func.py:12", with_eol=True
30+
)
31+
app.expect(r"#1.*__main__::kernel_sum", with_eol=True)

numba_dpex/tests/debugging/test_breakpoints.py

Lines changed: 50 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,37 @@
1313

1414
from numba_dpex.tests._helper import skip_no_gdb
1515

16-
from .gdb import breakpoint_by_function, breakpoint_by_mark, setup_breakpoint
16+
from .gdb import gdb
1717

1818
pytestmark = skip_no_gdb
1919

2020

21-
side_by_side_breakpoint = breakpoint_by_function(
22-
"side-by-side.py", "common_loop_body"
21+
@pytest.mark.parametrize(
22+
"breakpoint",
23+
[
24+
"side-by-side.py:16",
25+
"common_loop_body",
26+
"side-by-side.py:common_loop_body",
27+
],
2328
)
24-
25-
simple_sum_condition_breakpoint = breakpoint_by_mark(
26-
"simple_sum.py", "Condition breakpoint location"
29+
@pytest.mark.parametrize(
30+
"api",
31+
[
32+
"numba",
33+
"numba-ndpx-kernel",
34+
],
2735
)
28-
29-
common_loop_body_native_function_name = {
30-
"numba": "common_loop_body",
31-
"numba-ndpx-kernel": "common_loop_body",
32-
}
33-
34-
breakpoint_api_cases = [
35-
(side_by_side_breakpoint, "numba"),
36-
(side_by_side_breakpoint, "numba-ndpx-kernel"),
37-
*((fn, api) for api, fn in common_loop_body_native_function_name.items()),
38-
*(
39-
(f"side-by-side.py:{fn}", api)
40-
for api, fn in common_loop_body_native_function_name.items()
41-
),
42-
]
43-
44-
45-
@pytest.mark.parametrize("breakpoint, api", breakpoint_api_cases)
46-
def test_breakpoint_with_condition_by_function_argument(app, breakpoint, api):
36+
@pytest.mark.parametrize(
37+
"condition, exp_var, exp_val",
38+
[
39+
("param_a == 3", "param_a", 3),
40+
("param_a == 7", "param_a", 7),
41+
(None, "param_a", r"[0-9]+"), # No condition
42+
],
43+
)
44+
def test_device_func_breakpoint(
45+
app: gdb, breakpoint, api, condition, exp_var, exp_val
46+
):
4747
"""Function breakpoints and argument initializing
4848
4949
Test that it is possible to set conditional breakpoint at the beginning
@@ -56,71 +56,40 @@ def test_breakpoint_with_condition_by_function_argument(app, breakpoint, api):
5656
SAT-4449
5757
"""
5858

59-
if api == "numba-ndpx-kernel":
60-
if (
61-
breakpoint == "side-by-side.py:common_loop_body"
62-
or breakpoint == "common_loop_body"
63-
):
64-
pytest.xfail(
65-
"Breakpoint by function name not working for numba-dpex."
66-
) # TODO: https://github.com/IntelPython/numba-dpex/issues/1242
59+
if api == "numba-ndpx-kernel" and breakpoint != "side-by-side.py:16":
60+
pytest.skip(
61+
"Breakpoint by function name not working for numba-dpex."
62+
) # TODO: https://github.com/IntelPython/numba-dpex/issues/1242
6763

68-
variable_name = "param_a"
69-
variable_value = "3"
70-
condition = f"{variable_name} == {variable_value}"
71-
72-
app.breakpoint(f"{breakpoint} if {condition}")
64+
app.breakpoint(breakpoint, condition=condition)
7365
app.run(f"side-by-side.py --api={api}")
74-
75-
app.child.expect(
76-
rf"Thread .* hit Breakpoint .* at {side_by_side_breakpoint}"
77-
)
78-
79-
app.print(variable_name)
80-
81-
app.child.expect(rf"\$1 = {variable_value}")
66+
app.expect_hit_breakpoint("side-by-side.py:16")
67+
if exp_var is not None:
68+
app.print(exp_var, expected=exp_val)
8269

8370

8471
@pytest.mark.parametrize(
85-
"breakpoint, script",
72+
"condition, exp_var, exp_val",
8673
[
87-
# location specified by file name and function name
88-
# commands/break_file_func # noqa: E800
89-
("simple_sum.py:data_parallel_sum", None),
90-
# location specified by function name
91-
# commands/break_func # noqa: E800
92-
("data_parallel_sum", "simple_sum.py"),
93-
# location specified by file name and nested function name
94-
# commands/break_nested_func # noqa: E800
95-
("simple_dpex_func.py:func_sum", None),
74+
("i == 3", "i", 3),
75+
(None, "i", r"[0-9]+"), # No condition
9676
],
9777
)
98-
def test_breakpoint_common(app, breakpoint, script):
99-
"""Set a breakpoint in the given script."""
100-
101-
pytest.xfail(
102-
"Breakpoint by function name not working for numba-dpex."
103-
) # TODO: https://github.com/IntelPython/numba-dpex/issues/1242
104-
setup_breakpoint(app, breakpoint, script=script)
105-
78+
def test_kernel_breakpoint(app: gdb, condition, exp_var, exp_val):
79+
"""Function breakpoints and argument initializing
10680
107-
@pytest.mark.parametrize(
108-
"breakpoint, variable_name, variable_value",
109-
[
110-
# commands/break_conditional # noqa: E800
111-
(f"{simple_sum_condition_breakpoint} if i == 1", "i", "1"),
112-
],
113-
)
114-
def test_breakpoint_with_condition_common(
115-
app,
116-
breakpoint,
117-
variable_name,
118-
variable_value,
119-
):
120-
"""Set a breakpoint with condition and check value of variable."""
81+
Test that it is possible to set conditional breakpoint at the beginning
82+
of the function and use a function argument in the condition.
12183
122-
setup_breakpoint(app, breakpoint)
84+
It is important that breakpoint by function name hits at the first line in
85+
the function body and not at the function definition line.
12386
124-
app.print(variable_name)
87+
Test for https://github.com/numba/numba/issues/7415
88+
SAT-4449
89+
"""
12590

126-
app.child.expect(rf"\$1 = {variable_value}")
91+
app.breakpoint("simple_sum.py:13", condition=condition)
92+
app.run("simple_sum.py")
93+
app.expect_hit_breakpoint("simple_sum.py:13")
94+
if exp_var is not None:
95+
app.print(exp_var, expected=exp_val)

0 commit comments

Comments
 (0)