Skip to content

Commit 6c83af9

Browse files
authored
Use MI command for thread info in gdb-oneapi (#52)
The "info thread" command is replaced with "-thread-info" command. For this purpose, now gdb-oneapi script have a dependency on an open source python module "pygdbmi". This module provides the interface to parse the "-thread-info" MI Command response using its "gdbmiparser" function. The SIMD lane information is then extracted from it using the exectution_mask and simd_width info available for each thread. Also the test file oneapi_gdb_test.py is updated to test this change. Signed-off-by: Abdul Basit Ijaz <[email protected]>
1 parent 5aa0d93 commit 6c83af9

File tree

3 files changed

+159
-87
lines changed

3 files changed

+159
-87
lines changed

INSTALL

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ Dependent Packages
3737
Python module can optionally be installed to allow the STAT GUI
3838
to perform syntax highlighting of source code. Another GUI
3939
requirement is the Graphviz package to render the DOT format
40-
graph files.
40+
graph files. The Python module "pygdbmi" is required for using
41+
gdb-oneapi.
4142

4243
STAT can also be optionally built with the Fast Global File
4344
Status (FGFS) library. This library helps STAT identify when a

scripts/oneapi_gdb.py

Lines changed: 108 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"""@package STATview
44
Visualizes dot graphs outputted by STAT."""
55

6-
__copyright__ = """Modifications Copyright (C) 2022 Intel Corporation
6+
__copyright__ = """Modifications Copyright (C) 2022-2025 Intel Corporation
77
SPDX-License-Identifier: BSD-3-Clause"""
88
__license__ = """Produced by Intel Corporation for Lawrence Livermore National Security, LLC.
9-
Written by M. Oguzhan Karakaya [email protected]
9+
Written by Matti Puputti [email protected], M. Oguzhan Karakaya [email protected]
1010
LLNL-CODE-750488.
1111
All rights reserved.
1212
@@ -20,7 +20,7 @@
2020
2121
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2222
"""
23-
__author__ = ["M. Oguzhan Karakaya <[email protected]>"]
23+
__author__ = ["Abdul Basit Ijaz <[email protected]>", "Matti Puputti <[email protected]>", "M. Oguzhan Karakaya <[email protected]>"]
2424
__version_major__ = 4
2525
__version_minor__ = 1
2626
__version_revision__ = 0
@@ -31,47 +31,107 @@
3131
import logging
3232
import re
3333
from gdb import GdbDriver, check_lines
34+
from pygdbmi import gdbmiparser
3435

35-
def expand_simd_spec(simd):
36+
def expand_simd_spec_mi(simd_width, emask):
3637
"""
37-
Converts a merged simd specifier into a list of individual simd lanes.
38-
`simd` string is expected to be either an individual lane number
39-
or a comma-separated range list inside square brackets.
38+
Converts a simd_width and exection_mask input to list
39+
of lanes.
4040
"""
41-
if simd[0] == '[':
42-
simd = simd[1:]
41+
42+
lane = 0
4343
lanes = []
44-
for item1 in simd.split(" "):
45-
for item2 in item1.split(","):
46-
if item2.isnumeric():
47-
lanes += item2
48-
else:
49-
pair = item2.split("-")
50-
for num in range(int(pair[0]), int(pair[1])+1):
51-
lanes += str(num)
44+
while emask != 0:
45+
if (emask & 0x1) != 0:
46+
lanes.append(lane)
47+
48+
lane += 1
49+
emask >>= 1
50+
5251
return lanes
5352

54-
def parse_thread_info_line(line, parse_simd_lanes = False):
53+
def is_gpu_thread_found(name):
5554
"""
56-
Extracts thread IDs from a `thread info` output produced by
57-
Intel(R) Distribution for GDB*. See oneapi_gdb_test.py for
58-
sample inputs and outputs.
55+
Returns 'True' if the Thread name contains 'ZE' character.
5956
"""
60-
tid_single_simd = re.compile(
61-
r"^[\s|*]*(\d+(\.?\d+)?)(?:(?::(\d+))?)\s*(?:Thread|LWP)")
62-
tid_multiple_simd = re.compile(
63-
r"^[\s|*]*(\d+(\.?\d+)?)(?:(?::\[(.*?)\])?)\s*(?:Thread|LWP)")
64-
match = re.search(tid_multiple_simd, line)
65-
if not match:
57+
return any([x in name for x in ('ZE')])
58+
59+
def is_cpu_thread_found(name):
60+
"""
61+
Returns 'True' if Thread name contains any of the matching
62+
characters 'LWP', 'Thread'.
63+
"""
64+
return any([x in name for x in ('LWP', 'Thread')])
65+
66+
def parse_thread_info_mi(mi_rsp, parse_simd_lanes = False):
67+
"""
68+
The function takes the MI command response in Python dictionary
69+
object as an input and returns the list of thread ids. The SIMD
70+
lane information is also added if the optional 'parse_simd_lanes'
71+
argument is true.
72+
"""
73+
74+
key, value = list(mi_rsp.items())[0]
75+
if key != "threads":
76+
logging.info('GDB MI response does not has any thread info')
6677
return []
78+
79+
thread_ids_gpu = [thread['name'].split(" ")[0].replace('"', "")
80+
for thread in value if ('name' in thread
81+
and is_gpu_thread_found(thread['name'])
82+
and 'state' in thread
83+
and "unavailable" not in thread['state'])]
84+
thread_ids_cpu = [str(thread['id']) for thread in value
85+
if ('name' in thread and not is_gpu_thread_found(thread['name'])
86+
and is_cpu_thread_found(thread['target-id'])
87+
and 'state' in thread and "unavailable" not in thread['state'])]
88+
thread_ids = thread_ids_cpu + thread_ids_gpu
6789
if not parse_simd_lanes:
68-
return [ match[1] ]
69-
if not match[3]:
70-
match = re.search(tid_single_simd, line)
71-
if not match[3]:
72-
return [ match[1] ]
73-
return [ match[1] + ":" + match[3]]
74-
return [ match[1] + ":" + x for x in expand_simd_spec(match[3]) ]
90+
return thread_ids
91+
92+
simd_widths = dict()
93+
simd_found = False
94+
95+
for tid in thread_ids:
96+
simd = [thread['simd-width'] for thread in value
97+
if ('simd-width' in thread
98+
and tid == thread['name'].split(" ")[0].replace('"', ""))]
99+
emask = [thread['execution-mask'] for thread in value
100+
if ('execution-mask' in thread
101+
and tid == thread['name'].split(" ")[0].replace('"', ""))]
102+
103+
simd_widths[tid] = []
104+
if simd and emask:
105+
simd_widths[tid].append(str(simd[0]).replace('"',''))
106+
simd_widths[tid].append(str(emask[0]).replace('"', ''))
107+
simd_found = True
108+
else:
109+
simd_widths[tid].append("0")
110+
simd_widths[tid].append("0")
111+
112+
if not simd_found:
113+
return thread_ids
114+
115+
thread_ids_simd = []
116+
for tid, value in simd_widths.items():
117+
if not value:
118+
continue
119+
120+
simd = value[0]
121+
emask = value[1]
122+
123+
if int(simd) == 0 and int(emask) == 0:
124+
thread_ids_simd.append(f'{tid}')
125+
continue
126+
127+
simd_ulimit = int(simd) - 1
128+
emask_hex = int(emask, base=16)
129+
lanes = expand_simd_spec_mi(simd_ulimit, emask_hex)
130+
131+
for lane in lanes:
132+
thread_ids_simd.append(f"{tid}:{lane}")
133+
134+
return thread_ids_simd
75135

76136
def clean_cpp_template_brackets_and_call_signature(string):
77137
"""
@@ -151,14 +211,22 @@ def get_thread_list(self):
151211
information on number of active SIMD lanes along with stack
152212
trace information.
153213
"""
154-
logging.info('gdb-oneapi: info threads')
214+
cmd = "-thread-info --stopped"
215+
logging.info('gdb-oneapi: interpreter-exec mi %s' % cmd)
155216
tids = []
156-
lines = self.communicate("info threads")
217+
lines = self.communicate('interpreter-exec mi "%s"' % cmd)
157218
logging.debug('%s', repr(lines))
158-
for line in lines:
159-
if "inactive" in line:
160-
continue
161-
tids += parse_thread_info_line(line, self.parse_simd_lanes)
219+
220+
if not lines:
221+
return []
222+
223+
mi_cmd_response = gdbmiparser.parse_response(lines[0])
224+
225+
if mi_cmd_response is not None:
226+
if mi_cmd_response['message'] == 'error':
227+
logging.error('Failed to execute the command: "%s"' % cmd)
228+
return []
229+
tids = parse_thread_info_mi(mi_cmd_response['payload'], self.parse_simd_lanes)
162230
return tids
163231

164232
def bt(self, thread_id):

scripts/oneapi_gdb_test.py

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"""@package STATview
44
Visualizes dot graphs outputted by STAT."""
55

6-
__copyright__ = """Modifications Copyright (C) 2022 Intel Corporation
6+
__copyright__ = """Modifications Copyright (C) 2022-2025 Intel Corporation
77
SPDX-License-Identifier: BSD-3-Clause"""
88
__license__ = """Produced by Intel Corporation for Lawrence Livermore National Security, LLC.
9-
Written by M. Oguzhan Karakaya [email protected]
9+
Written by Matti Puputti [email protected], M. Oguzhan Karakaya [email protected]
1010
LLNL-CODE-750488.
1111
All rights reserved.
1212
@@ -20,21 +20,29 @@
2020
2121
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2222
"""
23-
__author__ = ["M. Oguzhan Karakaya <[email protected]>"]
23+
__author__ = ["Abdul Basit Ijaz <[email protected]>", "Matti Puputti <[email protected]>", "M. Oguzhan Karakaya <[email protected]>"]
2424
__version_major__ = 4
2525
__version_minor__ = 1
2626
__version_revision__ = 0
2727
__version__ = "%d.%d.%d" %(__version_major__, __version_minor__, __version_revision__)
2828

2929
import unittest
30-
from oneapi_gdb import OneAPIGdbDriver, parse_thread_info_line, parse_frameinfo_from_backtrace
30+
from oneapi_gdb import OneAPIGdbDriver, parse_frameinfo_from_backtrace, parse_thread_info_mi
31+
32+
t1_gpu = {'id':8,'target-id':'"Thread 8"','name':'"3.1 (ZE 0.0.0.0)"','execution-mask':'"0xf"','simd-width':4,'state':'"stopped"'}
33+
t2_gpu = {'id':9,'target-id':'"Thread 9"','name':'"3.2 (ZE 0.0.0.1)"','execution-mask':'"0x1"','simd-width':4,'state':'"stopped"'}
34+
t3_gpu = {'id':12,'target-id':'"Thread 12"','name':'"3.5 (ZE 0.0.0.4)"','execution-mask':'"0x3"','simd-width':4,'state':'"stopped"'}
35+
t4_gpu_running = {'id':13,'target-id':'"Thread 12"','name':'"3.6 (ZE 0.0.0.4)"','execution-mask':'"0x3"','simd-width':4,'state':'"running"'}
36+
t5_gpu_unavailable = {'id':13,'target-id':'"Thread 12"','name':'"3.6 (ZE 0.0.0.4)"','execution-mask':'"0x3"','simd-width':4,'state':'"unavailable"'}
37+
# Test for expand_simd_spec_mi function.
38+
t6_gpu_zero_emask = {'id':13,'target-id':'"Thread 13"','name':'"3.6 (ZE 0.0.0.4)"','simd-width':32, 'execution-mask':'"0x0', 'state':'"running"'}
39+
t_cpu = {'id':1,'target-id':'"Thread 1"','name':'"main"', 'state':'"stopped"','core':53}
3140

3241
class TestGDBParsing(unittest.TestCase):
3342
"""
3443
Testing OneAPIGdbDriver and utility functions
3544
in oneapi_gdb module
3645
"""
37-
3846
def test_oneapigdbdriver_class_statics(self):
3947
"""
4048
Tests class static variables
@@ -44,68 +52,63 @@ def test_oneapigdbdriver_class_statics(self):
4452

4553
def test_info_threads(self):
4654
"""
47-
Tests parse_thread_info_line method
55+
Tests parse_thread_info_mi method
4856
"""
4957

50-
tids = parse_thread_info_line(
51-
"2.1 Thread 1.1073741824",
58+
tids = parse_thread_info_mi(
59+
{'threads':[t1_gpu,t2_gpu,t3_gpu]},
5260
parse_simd_lanes=False)
53-
self.assertEqual(tids, ["2.1"])
61+
self.assertEqual(tids, ["3.1", "3.2", "3.5"])
5462

55-
tids = parse_thread_info_line(
56-
" * 2.1 Thread 1.1073741824",
63+
tids = parse_thread_info_mi(
64+
{'threads':[t1_gpu,t2_gpu,t3_gpu], 'current-thread-id':'"8"'},
5765
parse_simd_lanes=False)
58-
self.assertEqual(tids, ["2.1"])
66+
self.assertEqual(tids, ["3.1", "3.2", "3.5"])
5967

60-
tids = parse_thread_info_line(
61-
"2.1 Thread 1.1073741824",
68+
tids = parse_thread_info_mi(
69+
{'threads':[t_cpu]},
6270
parse_simd_lanes=True)
63-
self.assertEqual(tids, ["2.1"])
71+
self.assertEqual(tids, ["1"])
6472

65-
tids = parse_thread_info_line(
66-
" * 2.1 Thread 1.1073741824",
73+
tids = parse_thread_info_mi(
74+
{'threads':[t_cpu], 'current-thread-id':"1"},
6775
parse_simd_lanes=True)
68-
self.assertEqual(tids, ["2.1"])
76+
self.assertEqual(tids, ["1"])
6977

70-
tids = parse_thread_info_line(
71-
"2.1:[1-7] Thread 1.1073741824",
72-
parse_simd_lanes=False)
73-
self.assertEqual(tids, ["2.1"])
78+
tids = parse_thread_info_mi(
79+
{'threads':[t1_gpu]},
80+
parse_simd_lanes=True)
81+
self.assertEqual(tids, ["3.1:0", "3.1:1", "3.1:2", "3.1:3"])
7482

75-
tids = parse_thread_info_line(
76-
"2.1:[1-7] Thread 1.1073741824",
83+
tids = parse_thread_info_mi(
84+
{'threads':[t1_gpu, t2_gpu], 'current-thread-id':"8"},
7785
parse_simd_lanes=True)
78-
self.assertEqual(tids, ["2.1:1",
79-
"2.1:2", "2.1:3", "2.1:4",
80-
"2.1:5", "2.1:6", "2.1:7" ])
86+
self.assertEqual(tids, ["3.1:0", "3.1:1", "3.1:2", "3.1:3", "3.2:0"])
8187

82-
tids = parse_thread_info_line(
83-
"2.1:[1,3-4,7] Thread 1.1073741824",
88+
tids = parse_thread_info_mi(
89+
{'threads':[t1_gpu, t3_gpu]},
8490
parse_simd_lanes=True)
85-
self.assertEqual(tids, ["2.1:1",
86-
"2.1:3", "2.1:4", "2.1:7" ])
91+
self.assertEqual(tids, ["3.1:0", "3.1:1", "3.1:2", "3.1:3", "3.5:0", "3.5:1"])
8792

88-
tids = parse_thread_info_line(
89-
"2.1:[1 3 5 7] Thread 1.1073741824",
93+
tids = parse_thread_info_mi(
94+
{'threads':[t2_gpu, t4_gpu_running]},
9095
parse_simd_lanes=True)
91-
self.assertEqual(tids, ["2.1:1",
92-
"2.1:3", "2.1:5", "2.1:7" ])
96+
self.assertEqual(tids, ["3.2:0", "3.6:0", "3.6:1"])
9397

94-
tids = parse_thread_info_line(
95-
"96:[0-2] LWP 1073742144",
98+
tids = parse_thread_info_mi(
99+
{'threads':[t2_gpu, t5_gpu_unavailable]},
96100
parse_simd_lanes=True)
97-
self.assertEqual(tids, ["96:0",
98-
"96:1", "96:2"])
101+
self.assertEqual(tids, ["3.2:0"])
99102

100-
tids = parse_thread_info_line(
101-
"96:[0-2] LWP 1073742144",
102-
parse_simd_lanes=False)
103-
self.assertEqual(tids, ["96"])
103+
tids = parse_thread_info_mi(
104+
{'threads':[t2_gpu, t6_gpu_zero_emask]},
105+
parse_simd_lanes=True)
106+
self.assertEqual(tids, ["3.2:0"])
104107

105-
tids = parse_thread_info_line(
106-
" wiID=\"2.1\") at test.cpp:72",
108+
tids = parse_thread_info_mi(
109+
{'threads':[t_cpu, t2_gpu]},
107110
parse_simd_lanes=True)
108-
self.assertEqual(tids, [])
111+
self.assertEqual(tids, ["1", "3.2:0"])
109112

110113
def test_bt(self):
111114
"""

0 commit comments

Comments
 (0)