xref: /llvm-project/openmp/libompd/gdb-plugin/ompd/ompd_address_space.py (revision 51d3f421f48f7c888c37a13be049a4ca8b61436e)
1from __future__ import print_function
2import ompdModule
3from ompd_handles import ompd_thread, ompd_task, ompd_parallel
4import gdb
5import sys
6import traceback
7from enum import Enum
8
9class ompd_scope(Enum):
10	ompd_scope_global = 1
11	ompd_scope_address_space = 2
12	ompd_scope_thread = 3
13	ompd_scope_parallel = 4
14	ompd_scope_implicit_task = 5
15	ompd_scope_task = 6
16
17class ompd_address_space(object):
18
19	def __init__(self):
20		"""Initializes an ompd_address_space object by calling ompd_initialize
21		in ompdModule.c
22		"""
23		self.addr_space = ompdModule.call_ompd_initialize()
24		# maps thread_num (thread id given by gdb) to ompd_thread object with thread handle
25		self.threads = {}
26		self.states = None
27		self.icv_map = None
28		self.ompd_tool_test_bp = None
29		self.scope_map = {1:'global', 2:'address_space', 3:'thread', 4:'parallel', 5:'implicit_task', 6:'task'}
30		self.sched_map = {1:'static', 2:'dynamic', 3:'guided', 4:'auto'}
31		gdb.events.stop.connect(self.handle_stop_event)
32		self.new_thread_breakpoint = gdb.Breakpoint("ompd_bp_thread_begin", internal=True)
33		tool_break_symbol = gdb.lookup_global_symbol("ompd_tool_break")
34		if (tool_break_symbol is not None):
35			self.ompd_tool_test_bp = gdb.Breakpoint("ompd_tool_break", internal=True)
36
37	def handle_stop_event(self, event):
38		"""Sets a breakpoint at different events, e.g. when a new OpenMP
39		thread is created.
40		"""
41		if (isinstance(event, gdb.BreakpointEvent)):
42			# check if breakpoint has already been hit
43			if (self.new_thread_breakpoint in event.breakpoints):
44				self.add_thread()
45				gdb.execute('continue')
46				return
47			elif (self.ompd_tool_test_bp is not None and self.ompd_tool_test_bp in event.breakpoints):
48				try:
49					self.compare_ompt_data()
50					gdb.execute('continue')
51				except():
52					traceback.print_exc()
53		elif (isinstance(event, gdb.SignalEvent)):
54			# TODO: what do we need to do on SIGNALS?
55			pass
56		else:
57			# TODO: probably not possible?
58			pass
59
60	def get_icv_map(self):
61		"""Fills ICV map.
62		"""
63		self.icv_map = {}
64		current = 0
65		more = 1
66		while more > 0:
67			tup = ompdModule.call_ompd_enumerate_icvs(self.addr_space, current)
68			(current, next_icv, next_scope, more) = tup
69			self.icv_map[next_icv] = (current, next_scope, self.scope_map[next_scope])
70		print('Initialized ICV map successfully for checking OMP API values.')
71
72	def compare_ompt_data(self):
73		"""Compares OMPT tool data about parallel region to data returned by OMPD functions.
74		"""
75		# make sure all threads and states are set
76		self.list_threads(False)
77
78		thread_id = gdb.selected_thread().ptid[1]
79		curr_thread = self.get_curr_thread()
80
81		# check if current thread is LWP thread; return if "ompd_rc_unavailable"
82		thread_handle = ompdModule.get_thread_handle(thread_id, self.addr_space)
83		if thread_handle == -1:
84			print("Skipping OMPT-OMPD checks for non-LWP thread.")
85			return
86
87		print('Comparing OMPT data to OMPD data...')
88		field_names = [i.name for i in gdb.parse_and_eval('thread_data').type.fields()]
89		thread_data = gdb.parse_and_eval('thread_data')
90
91		if self.icv_map is None:
92			self.get_icv_map()
93
94		# compare state values
95		if 'ompt_state' in field_names:
96			if self.states is None:
97				self.enumerate_states()
98			ompt_state = str(thread_data['ompt_state'])
99			ompd_state = str(self.states[curr_thread.get_state()[0]])
100			if ompt_state != ompd_state:
101				print('OMPT-OMPD mismatch: ompt_state (%s) does not match OMPD state (%s)!' % (ompt_state, ompd_state))
102
103		# compare wait_id values
104		if 'ompt_wait_id' in field_names:
105			ompt_wait_id = thread_data['ompt_wait_id']
106			ompd_wait_id = curr_thread.get_state()[1]
107			if ompt_wait_id != ompd_wait_id:
108				print('OMPT-OMPD mismatch: ompt_wait_id (%d) does not match OMPD wait id (%d)!' % (ompt_wait_id, ompd_wait_id))
109
110		# compare thread id
111		if 'omp_thread_num' in field_names and 'thread-num-var' in self.icv_map:
112			ompt_thread_num = thread_data['omp_thread_num']
113			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.thread_handle, self.icv_map['thread-num-var'][1], self.icv_map['thread-num-var'][0])
114			if ompt_thread_num != icv_value:
115				print('OMPT-OMPD mismatch: omp_thread_num (%d) does not match OMPD thread num according to ICVs (%d)!' % (ompt_thread_num, icv_value))
116
117		# compare thread data
118		if 'ompt_thread_data' in field_names:
119			ompt_thread_data = thread_data['ompt_thread_data'].dereference()['value']
120			ompd_value = ompdModule.call_ompd_get_tool_data(3, curr_thread.thread_handle)[0]
121			if ompt_thread_data != ompd_value:
122				print('OMPT-OMPD mismatch: value of ompt_thread_data (%d) does not match that of OMPD data union (%d)!' % (ompt_thread_data, ompd_value))
123
124		# compare number of threads
125		if 'omp_num_threads' in field_names and 'team-size-var' in self.icv_map:
126			ompt_num_threads = thread_data['omp_num_threads']
127			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.get_current_parallel_handle(), self.icv_map['team-size-var'][1], self.icv_map['team-size-var'][0])
128			if ompt_num_threads != icv_value:
129				print('OMPT-OMPD mismatch: omp_num_threads (%d) does not match OMPD num threads according to ICVs (%d)!' % (ompt_num_threads, icv_value))
130
131		# compare omp level
132		if 'omp_level' in field_names and 'levels-var' in self.icv_map:
133			ompt_levels = thread_data['omp_level']
134			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.get_current_parallel_handle(), self.icv_map['levels-var'][1], self.icv_map['levels-var'][0])
135			if ompt_levels != icv_value:
136				print('OMPT-OMPD mismatch: omp_level (%d) does not match OMPD levels according to ICVs (%d)!' % (ompt_levels, icv_value))
137
138		# compare active level
139		if 'omp_active_level' in field_names and 'active-levels-var' in self.icv_map:
140			ompt_active_levels = thread_data['omp_active_level']
141			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.get_current_parallel_handle(), self.icv_map['active-levels-var'][1], self.icv_map['active-levels-var'][0])
142			if ompt_active_levels != icv_value:
143				print('OMPT-OMPD mismatch: active levels (%d) do not match active levels according to ICVs (%d)!' % (ompt_active_levels, icv_value))
144
145		# compare parallel data
146		if 'ompt_parallel_data' in field_names:
147			ompt_parallel_data = thread_data['ompt_parallel_data'].dereference()['value']
148			current_parallel_handle = curr_thread.get_current_parallel_handle()
149			ompd_value = ompdModule.call_ompd_get_tool_data(4, current_parallel_handle)[0]
150			if ompt_parallel_data != ompd_value:
151				print('OMPT-OMPD mismatch: value of ompt_parallel_data (%d) does not match that of OMPD data union (%d)!' % (ompt_parallel_data, ompd_value))
152
153		# compare max threads
154		if 'omp_max_threads' in field_names and 'nthreads-var' in self.icv_map:
155			ompt_max_threads = thread_data['omp_max_threads']
156			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.thread_handle, self.icv_map['nthreads-var'][1], self.icv_map['nthreads-var'][0])
157			if icv_value is None:
158				icv_string = ompdModule.call_ompd_get_icv_string_from_scope(curr_thread.thread_handle, self.icv_map['nthreads-var'][1], self.icv_map['nthreads-var'][0])
159				if icv_string is None:
160					print('OMPT-OMPD mismatch: omp_max_threads (%d) does not match OMPD thread limit according to ICVs (None Object)' % (ompt_max_threads))
161				else:
162					if ompt_max_threads != int(icv_string.split(',')[0]):
163						print('OMPT-OMPD mismatch: omp_max_threads (%d) does not match OMPD thread limit according to ICVs (%d)!' % (ompt_max_threads, int(icv_string.split(',')[0])))
164			else:
165				if ompt_max_threads != icv_value:
166					print('OMPT-OMPD mismatch: omp_max_threads (%d) does not match OMPD thread limit according to ICVs (%d)!' % (ompt_max_threads, icv_value))
167
168		# compare omp_parallel
169		# NOTE: omp_parallel = true if active-levels-var > 0
170		if 'omp_parallel' in field_names:
171			ompt_parallel = thread_data['omp_parallel']
172			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.get_current_parallel_handle(), self.icv_map['active-levels-var'][1], self.icv_map['active-levels-var'][0])
173			if ompt_parallel == 1 and icv_value <= 0 or ompt_parallel == 0 and icv_value > 0:
174				print('OMPT-OMPD mismatch: ompt_parallel (%d) does not match OMPD parallel according to ICVs (%d)!' % (ompt_parallel, icv_value))
175
176		# compare omp_final
177		if 'omp_final' in field_names and 'final-task-var' in self.icv_map:
178			ompt_final = thread_data['omp_final']
179			current_task_handle = curr_thread.get_current_task_handle()
180			icv_value = ompdModule.call_ompd_get_icv_from_scope(current_task_handle, self.icv_map['final-task-var'][1], self.icv_map['final-task-var'][0])
181			if icv_value != ompt_final:
182				print('OMPT-OMPD mismatch: omp_final (%d) does not match OMPD final according to ICVs (%d)!' % (ompt_final, icv_value))
183
184		# compare omp_dynamic
185		if 'omp_dynamic' in field_names and 'dyn-var' in self.icv_map:
186			ompt_dynamic = thread_data['omp_dynamic']
187			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.thread_handle, self.icv_map['dyn-var'][1], self.icv_map['dyn-var'][0])
188			if icv_value != ompt_dynamic:
189				print('OMPT-OMPD mismatch: omp_dynamic (%d) does not match OMPD dynamic according to ICVs (%d)!' % (ompt_dynamic, icv_value))
190
191		# compare omp_max_active_levels
192		if 'omp_max_active_levels' in field_names and 'max-active-levels-var' in self.icv_map:
193			ompt_max_active_levels = thread_data['omp_max_active_levels']
194			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.get_current_task_handle(), self.icv_map['max-active-levels-var'][1], self.icv_map['max-active-levels-var'][0])
195			if ompt_max_active_levels != icv_value:
196				print('OMPT-OMPD mismatch: omp_max_active_levels (%d) does not match OMPD max active levels (%d)!' % (ompt_max_active_levels, icv_value))
197
198                # compare omp_kind: TODO: Add the test for monotonic/nonmonotonic modifier
199		if 'omp_kind' in field_names and 'run-sched-var' in self.icv_map:
200			ompt_sched_kind = thread_data['omp_kind']
201			icv_value = ompdModule.call_ompd_get_icv_string_from_scope(curr_thread.get_current_task_handle(), self.icv_map['run-sched-var'][1], self.icv_map['run-sched-var'][0])
202			ompd_sched_kind = icv_value.split(',')[0]
203			if self.sched_map.get(int(ompt_sched_kind)) != ompd_sched_kind:
204				print('OMPT-OMPD mismatch: omp_kind kind (%s) does not match OMPD schedule kind according to ICVs (%s)!' % (self.sched_map.get(int(ompt_sched_kind)), ompd_sched_kind))
205
206		# compare omp_modifier
207		if 'omp_modifier' in field_names and 'run-sched-var' in self.icv_map:
208			ompt_sched_mod = thread_data['omp_modifier']
209			icv_value = ompdModule.call_ompd_get_icv_string_from_scope(curr_thread.get_current_task_handle(), self.icv_map['run-sched-var'][1], self.icv_map['run-sched-var'][0])
210			token = icv_value.split(',')[1]
211			if token is not None:
212				ompd_sched_mod = int(token)
213			else:
214				ompd_sched_mod = 0
215			if ompt_sched_mod != ompd_sched_mod:
216				print('OMPT-OMPD mismatch: omp_kind modifier does not match OMPD schedule modifier according to ICVs!')
217
218		# compare omp_proc_bind
219		if 'omp_proc_bind' in field_names and 'bind-var' in self.icv_map:
220			ompt_proc_bind = thread_data['omp_proc_bind']
221			icv_value = ompdModule.call_ompd_get_icv_from_scope(curr_thread.get_current_task_handle(), self.icv_map['bind-var'][1], self.icv_map['bind-var'][0])
222			if icv_value is None:
223				icv_string = ompdModule.call_ompd_get_icv_string_from_scope(curr_thread.get_current_task_handle(), self.icv_map['bind-var'][1], self.icv_map['bind-var'][0])
224				if icv_string is None:
225					print('OMPT-OMPD mismatch: omp_proc_bind (%d) does not match OMPD proc bind according to ICVs (None Object)' % (ompt_proc_bind))
226				else:
227					if ompt_proc_bind != int(icv_string.split(',')[0]):
228						print('OMPT-OMPD mismatch: omp_proc_bind (%d) does not match OMPD proc bind according to ICVs (%d)!' % (ompt_proc_bind, int(icv_string.split(',')[0])))
229			else:
230				if ompt_proc_bind != icv_value:
231					print('OMPT-OMPD mismatch: omp_proc_bind (%d) does not match OMPD proc bind according to ICVs (%d)!' % (ompt_proc_bind, icv_value))
232
233		# compare enter and exit frames
234		if 'ompt_frame_list' in field_names:
235			ompt_task_frame_dict = thread_data['ompt_frame_list'].dereference()
236			ompt_task_frames = (int(ompt_task_frame_dict['enter_frame'].cast(gdb.lookup_type('long'))), int(ompt_task_frame_dict['exit_frame'].cast(gdb.lookup_type('long'))))
237			current_task = curr_thread.get_current_task()
238			ompd_task_frames = current_task.get_task_frame()
239			if ompt_task_frames != ompd_task_frames:
240				print('OMPT-OMPD mismatch: ompt_task_frames (%s) do not match OMPD task frames (%s)!' % (ompt_task_frames, ompd_task_frames))
241
242		# compare task data
243		if 'ompt_task_data' in field_names:
244			ompt_task_data = thread_data['ompt_task_data'].dereference()['value']
245			current_task_handle = curr_thread.get_current_task_handle()
246			ompd_value = ompdModule.call_ompd_get_tool_data(6, current_task_handle)[0]
247			if ompt_task_data != ompd_value:
248				print('OMPT-OMPD mismatch: value of ompt_task_data (%d) does not match that of OMPD data union (%d)!' % (ompt_task_data, ompd_value))
249
250	def save_thread_object(self, thread_num, thread_id, addr_space):
251		"""Saves thread object for thread_num inside threads dictionary.
252		"""
253		thread_handle = ompdModule.get_thread_handle(thread_id, addr_space)
254		self.threads[int(thread_num)] = ompd_thread(thread_handle)
255
256	def get_thread(self, thread_num):
257		""" Get thread object from map.
258		"""
259		return self.threads[int(thread_num)]
260
261	def get_curr_thread(self):
262		""" Get current thread object from map or add new one to map, if missing.
263		"""
264		thread_num = int(gdb.selected_thread().num)
265		if thread_num not in self.threads:
266			self.add_thread()
267		return self.threads[thread_num]
268
269	def add_thread(self):
270		"""Add currently selected (*) thread to dictionary threads.
271		"""
272		inf_thread = gdb.selected_thread()
273		try:
274			self.save_thread_object(inf_thread.num, inf_thread.ptid[1], self.addr_space)
275		except:
276			traceback.print_exc()
277
278	def list_threads(self, verbose):
279		"""Prints OpenMP threads only that are being tracking inside the "threads" dictionary.
280		See handle_stop_event and add_thread.
281		"""
282		list_tids = []
283		curr_inferior = gdb.selected_inferior()
284
285		for inf_thread in curr_inferior.threads():
286			list_tids.append((inf_thread.num, inf_thread.ptid))
287		if verbose:
288			if self.states is None:
289				self.enumerate_states()
290			for (thread_num, thread_ptid) in sorted(list_tids):
291				if thread_num in self.threads:
292					try:
293						print('Thread %i (%i) is an OpenMP thread; state: %s' % (thread_num, thread_ptid[1], self.states[self.threads[thread_num].get_state()[0]]))
294					except:
295						traceback.print_exc()
296				else:
297					print('Thread %i (%i) is no OpenMP thread' % (thread_num, thread_ptid[1]))
298
299	def enumerate_states(self):
300		"""Helper function for list_threads: initializes map of OMPD states for output of
301		'ompd threads'.
302		"""
303		if self.states is None:
304			self.states = {}
305			current = int("0x102", 0)
306			count = 0
307			more = 1
308
309			while more > 0:
310				tup = ompdModule.call_ompd_enumerate_states(self.addr_space, current)
311				(next_state, next_state_name, more) = tup
312
313				self.states[next_state] = next_state_name
314				current = next_state
315