Skip to content

Commit f595cd9

Browse files
authored
Improvements to gdb-oneapi support (#53)
* Improvement in getting threads list for the gdb-oneapi support Replaced the MI command "-thread-info" with "thread apply" for retrieving the thread list. The previous MI command output could grow excessively large, making it inefficient for the readlines function to process. Now after this change the output of "thread apply" command is only a list of threads and will take less time in readlines function. The test file oneapi_gdb_test.py has been updated to test these changes. Signed-off-by: Abdul Basit Ijaz <[email protected]> * Improvements in getting backtrace for the gdb-oneapi support The backtrace command now uses the "with print frame-arguments none --" option to enhance performance, as frame argument details are unnecessary for the STAT tool. Signed-off-by: Abdul Basit Ijaz <[email protected]> * Remove STAT_COLLECT_SIMD_BT from gdb-oneapi support The "STAT_COLLECT_SIMD_BT" option in gdb-oneapi STAT support is used to add lane information in thread id. So, using this environment increases the STAT capture time due to sequential calls per lane of a thread for the collection of the call stack. It is removed in this change because the function call stacks collected for lanes are identical , except for function argument values, which are not taken into consideration by STAT anyway. Signed-off-by: Abdul Basit Ijaz <[email protected]> --------- Signed-off-by: Abdul Basit Ijaz <[email protected]>
1 parent 7444310 commit f595cd9

File tree

2 files changed

+54
-191
lines changed

2 files changed

+54
-191
lines changed

scripts/oneapi_gdb.py

Lines changed: 15 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -31,107 +31,6 @@
3131
import logging
3232
import re
3333
from gdb import GdbDriver, check_lines
34-
from pygdbmi import gdbmiparser
35-
36-
def expand_simd_spec_mi(simd_width, emask):
37-
"""
38-
Converts a simd_width and exection_mask input to list
39-
of lanes.
40-
"""
41-
42-
lane = 0
43-
lanes = []
44-
while emask != 0:
45-
if (emask & 0x1) != 0:
46-
lanes.append(lane)
47-
48-
lane += 1
49-
emask >>= 1
50-
51-
return lanes
52-
53-
def is_gpu_thread_found(name):
54-
"""
55-
Returns 'True' if the Thread name contains 'ZE' character.
56-
"""
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')
77-
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
89-
if not parse_simd_lanes:
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
13534

13635
def clean_cpp_template_brackets_and_call_signature(string):
13736
"""
@@ -197,53 +96,44 @@ class OneAPIGdbDriver(GdbDriver):
19796
""" A class to drive Intel(R) Distribution for GDB* """
19897

19998
gdb_command = 'gdb-oneapi'
200-
parse_simd_lanes = False
201-
if 'STAT_COLLECT_SIMD_BT' in os.environ and \
202-
os.environ['STAT_COLLECT_SIMD_BT'] == "1":
203-
parse_simd_lanes = True
20499

205100
def get_thread_list(self):
206101
"""
207102
Gets the list of threads in the target process. For
208-
Intel(R) Distribution for GDB* this function extracts SIMD
209-
lane information as part of a thread ID.
210-
It is to improve the resulting representation by adding the
211-
information on number of active SIMD lanes along with stack
212-
trace information.
103+
Intel(R) Distribution for GDB* this function extracts active
104+
threads ID list.
213105
"""
214-
cmd = "-thread-info --stopped"
215-
logging.info('gdb-oneapi: interpreter-exec mi %s' % cmd)
216-
tids = []
217-
lines = self.communicate('interpreter-exec mi "%s"' % cmd)
218-
logging.debug('%s', repr(lines))
106+
cmd = (
107+
'thread apply all -q -c printf "%d.%d\\n", '
108+
'$_inferior, $_thread'
109+
)
110+
111+
logging.info(f'gdb-oneapi: {cmd}')
219112

220-
if not lines:
113+
tids = self.communicate(cmd)
114+
if not tids:
221115
return []
222116

223-
mi_cmd_response = gdbmiparser.parse_response(lines[0])
117+
logging.debug(f'{tids}')
224118

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)
230119
return tids
231120

232121
def bt(self, thread_id):
233122
"""
234123
Gets a backtrace from the requested thread id.
235124
returns list of frames, where each frame is a map of attributes.
236125
"""
237-
logging.info('GDB thread bt ID %s' %(thread_id))
126+
logging.info(f'GDB thread bt ID {thread_id}')
238127
ret = []
239128

240-
lines = self.communicate("thread apply %s bt" %thread_id)
129+
wpfa = "with print frame-arguments none --"
130+
lines = self.communicate(f"{wpfa} thread apply {thread_id} bt")
241131

242132
lines = lines[2:]
243133
for line_num, line in enumerate(lines):
244134
if line[0] != '#':
245135
continue
246-
logging.debug('Parsing line #%d: %d', line_num, line)
136+
logging.debug(f'Parsing line #{line_num}: {line}')
247137
ret.append(parse_frameinfo_from_backtrace(line))
248138
return ret
249139

scripts/oneapi_gdb_test.py

Lines changed: 39 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -27,88 +27,61 @@
2727
__version__ = "%d.%d.%d" %(__version_major__, __version_minor__, __version_revision__)
2828

2929
import unittest
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}
30+
from unittest.mock import Mock
31+
from oneapi_gdb import OneAPIGdbDriver, parse_frameinfo_from_backtrace
4032

4133
class TestGDBParsing(unittest.TestCase):
4234
"""
4335
Testing OneAPIGdbDriver and utility functions
4436
in oneapi_gdb module
4537
"""
38+
39+
def setUp(self):
40+
"""Initializes environment for tests"""
41+
self.gdb_driver = OneAPIGdbDriver(0, 'debug', 'log.txt')
42+
self.gdb_driver.communicate = Mock()
43+
4644
def test_oneapigdbdriver_class_statics(self):
4745
"""
4846
Tests class static variables
4947
"""
5048
self.assertEqual(OneAPIGdbDriver.gdb_command, "gdb-oneapi")
51-
self.assertFalse(OneAPIGdbDriver.parse_simd_lanes)
5249

5350
def test_info_threads(self):
5451
"""
55-
Tests parse_thread_info_mi method
52+
Tests get_thread_list method.
5653
"""
5754

58-
tids = parse_thread_info_mi(
59-
{'threads':[t1_gpu,t2_gpu,t3_gpu]},
60-
parse_simd_lanes=False)
61-
self.assertEqual(tids, ["3.1", "3.2", "3.5"])
62-
63-
tids = parse_thread_info_mi(
64-
{'threads':[t1_gpu,t2_gpu,t3_gpu], 'current-thread-id':'"8"'},
65-
parse_simd_lanes=False)
66-
self.assertEqual(tids, ["3.1", "3.2", "3.5"])
67-
68-
tids = parse_thread_info_mi(
69-
{'threads':[t_cpu]},
70-
parse_simd_lanes=True)
71-
self.assertEqual(tids, ["1"])
72-
73-
tids = parse_thread_info_mi(
74-
{'threads':[t_cpu], 'current-thread-id':"1"},
75-
parse_simd_lanes=True)
76-
self.assertEqual(tids, ["1"])
77-
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"])
82-
83-
tids = parse_thread_info_mi(
84-
{'threads':[t1_gpu, t2_gpu], 'current-thread-id':"8"},
85-
parse_simd_lanes=True)
86-
self.assertEqual(tids, ["3.1:0", "3.1:1", "3.1:2", "3.1:3", "3.2:0"])
87-
88-
tids = parse_thread_info_mi(
89-
{'threads':[t1_gpu, t3_gpu]},
90-
parse_simd_lanes=True)
91-
self.assertEqual(tids, ["3.1:0", "3.1:1", "3.1:2", "3.1:3", "3.5:0", "3.5:1"])
92-
93-
tids = parse_thread_info_mi(
94-
{'threads':[t2_gpu, t4_gpu_running]},
95-
parse_simd_lanes=True)
96-
self.assertEqual(tids, ["3.2:0", "3.6:0", "3.6:1"])
97-
98-
tids = parse_thread_info_mi(
99-
{'threads':[t2_gpu, t5_gpu_unavailable]},
100-
parse_simd_lanes=True)
101-
self.assertEqual(tids, ["3.2:0"])
102-
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"])
107-
108-
tids = parse_thread_info_mi(
109-
{'threads':[t_cpu, t2_gpu]},
110-
parse_simd_lanes=True)
111-
self.assertEqual(tids, ["1", "3.2:0"])
55+
# Mock the communicate method to return sample thread data
56+
self.gdb_driver.communicate.return_value = [
57+
"1.1",
58+
"1.2",
59+
"2.1"
60+
]
61+
62+
result = self.gdb_driver.get_thread_list()
63+
64+
# Verify the command was constructed correctly
65+
expected_cmd = (
66+
'thread apply all -q -c printf "%d.%d\\n", '
67+
'$_inferior, $_thread'
68+
)
69+
self.gdb_driver.communicate.assert_called_once_with(
70+
expected_cmd
71+
)
72+
73+
# Verify results
74+
self.assertEqual(result, ["1.1", "1.2", "2.1"])
75+
76+
def test_info_threads_empty_tid_list(self):
77+
"""
78+
Tests get_thread_list method with empty response
79+
"""
80+
81+
self.gdb_driver.communicate.return_value = []
82+
result = self.gdb_driver.get_thread_list()
83+
84+
self.assertEqual(result, [])
11285

11386
def test_bt(self):
11487
"""

0 commit comments

Comments
 (0)