diff --git a/scripts/Infrastructure/TemplateFactory.py b/scripts/Infrastructure/TemplateFactory.py
index c272cc7acf279d0349c9f88700ed189e860c810e..09f5742a7011b18b73903a50cb9f41a857850b51 100644
--- a/scripts/Infrastructure/TemplateFactory.py
+++ b/scripts/Infrastructure/TemplateFactory.py
@@ -2,6 +2,7 @@
from __future__ import annotations
import typing
+from typing import List, Tuple
from scripts.Infrastructure.AllocCall import AllocCall
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory
@@ -386,6 +387,55 @@ def get_send_recv_template(send_func: str = "mpi_isend", recv_func: str | typing
return tm
+
+def get_rma_template(win_alloc_func: str = "mpi_win_allocate", rmaop_func: str = "mpi_get", sync_mode: str = "fence"):
+ """
+ Contructs a default template for RMA communication.
+ Returns:
+ TemplateManager Initialized with a default template
+ """
+ tm = TemplateManager()
+ cf = CorrectParameterFactory()
+
+
+ # local buffer allocation, can be used by calls from different ranks
+ tm.register_instruction(AllocCall(
+ cf.dtype[0], cf.buf_size, cf.buf_var_name, use_malloc=False, identifier="RMA_LOCALBUF_ALLOC", rank="all"))
+
+ (win_declare, alloc_list, free_list) = get_allocated_window(win_alloc_func, cf.get("win"), cf.winbuf_var_name, "int", "10")
+ tm.register_instruction(win_declare, identifier="RMA_WIN_DECLARE")
+ tm.register_instruction(alloc_list, identifier="RMA_WIN_ALLOC")
+
+ if sync_mode == "fence":
+ tm.register_instruction(CorrectMPICallFactory.mpi_win_fence(), identifier="STARTRMAEPOCH")
+ elif sync_mode == "winlockall":
+ tm.register_instruction(CorrectMPICallFactory.mpi_win_lock_all(), identifier="STARTRMAEPOCH")
+ elif sync_mode == "winlock":
+ tm.register_instruction(CorrectMPICallFactory.mpi_win_lock(), identifier="STARTRMAEPOCH", rank_to_execute=0)
+
+ (alloc_list, inst_rma, inst_req) = get_rma_call(tm, rmaop_func, 0, "RMAOP")
+ tm.register_instruction(alloc_list, identifier="RMABUFALLOC")
+ tm.register_instruction(inst_rma, identifier="RMAOP")
+ if inst_req is not None:
+ tm.register_instruction(inst_req, identifier="RMAOPFINISH")
+
+ if sync_mode == "fence":
+ tm.register_instruction(CorrectMPICallFactory.mpi_win_fence(), identifier="ENDRMAEPOCH")
+ elif sync_mode == "winlockall":
+ tm.register_instruction(CorrectMPICallFactory.mpi_win_unlock_all(), identifier="ENDRMAEPOCH")
+ elif sync_mode == "winlock":
+ tm.register_instruction(CorrectMPICallFactory.mpi_win_unlock(), identifier="ENDRMAEPOCH", rank_to_execute=0)
+ # end MPI operation
+ # cleanup
+ tm.register_instruction(free_list, identifier="RMA_WIN_FREE")
+
+
+ return tm
+
+
+
+
+
def get_invalid_param_p2p_case(param, value, check_receive, send_func, recv_func):
tm = get_send_recv_template(send_func, recv_func)
@@ -399,7 +449,15 @@ def get_invalid_param_p2p_case(param, value, check_receive, send_func, recv_func
call.set_arg(param, value)
return tm
+def get_invalid_param_rma_case(param, rmaop_func, value):
+ tm = get_rma_template(rmaop_func=rmaop_func)
+ for call in tm.get_instruction(identifier="RMAOP", return_list=True):
+ if call.get_rank_executing() == 0:
+ assert call.has_arg(param)
+ call.set_arg(param, value)
+
+ return tm
def insert_probe(tm, probe_to_use, recv_call):
probe_call = CorrectMPICallFactory.get(probe_to_use)
@@ -570,7 +628,7 @@ def get_allocated_window(win_alloc_func, name, bufname, ctype, num_elements):
free_list = []
# declare window
- alloc_list.append(Instruction(f"MPI_Win {name};", identifier=identifier))
+ win_declare = Instruction(f"MPI_Win {name};", identifier=identifier)
# extract C data type and window buffer name
# dtype = CorrectParameterFactory().dtype[0]
@@ -579,8 +637,8 @@ def get_allocated_window(win_alloc_func, name, bufname, ctype, num_elements):
win_allocate_call = None
if win_alloc_func == "mpi_win_allocate":
- # MPI allocate, only declaration required
- alloc_list.append(Instruction(f"{ctype}* {bufname};", identifier))
+ # MPI allocate, only declaration of local buffer required
+ alloc_list.append(Instruction(f"{ctype}* {bufname};", "all", identifier))
win_allocate_call = CorrectMPICallFactory().mpi_win_allocate()
win_allocate_call.set_arg("baseptr", "&" + bufname)
elif win_alloc_func == "mpi_win_create":
@@ -604,15 +662,14 @@ def get_allocated_window(win_alloc_func, name, bufname, ctype, num_elements):
win_free_call.set_arg("win", "&" + name)
free_list.append(win_free_call)
- return (alloc_list, free_list)
+ return (win_declare, alloc_list, free_list)
-def get_rma_call(rma_func, rank):
- b = InstructionBlock(rma_func.replace('mpi_', ''))
-def get_rma_call(tm: TemplateManager, rma_func, rank, identifier="RMACall") -> Tuple[List[Instruction],List[Instruction]]:
- inst_rma_list: List[Instruction] = []
- # instructions required to finish RMA call (for request-based RMA, wait for requests)
- inst_rma_req_wait_list: List[Instruction] = []
+def get_rma_call(tm: TemplateManager, rma_func, rank, identifier="RMACall") -> Tuple[List[Instruction],Instruction, Instruction | None]:
+ # some RMA ops require buffer for result_addr and compare_addr
+ additional_alloc_list: List[Instruction] = []
+ # instruction required to finish RMA call (for request-based RMA, wait for request)
+ inst_rma_req_wait = None
cf = CorrectParameterFactory()
cfmpi = CorrectMPICallFactory()
@@ -625,14 +682,14 @@ def get_rma_call(tm: TemplateManager, rma_func, rank, identifier="RMACall") -> T
if rma_call.has_arg("request"):
req = tm.add_stack_variable("MPI_Request")
rma_call.set_arg("request", "&" + req)
- inst_rma_req_wait_list.append(Instruction(f"MPI_Wait(&{req}, MPI_STATUS_IGNORE);", rank=rank))
+ inst_rma_req_wait = Instruction(f"MPI_Wait(&{req}, MPI_STATUS_IGNORE);", rank=rank)
# some RMA ops require result_addr
if rma_call.has_arg("result_addr"):
result_addr = tm.add_stack_variable("int")
result_addr_alloc = AllocCall(cf.dtype[0], cf.buf_size, result_addr, rank=rank, identifier=identifier)
rma_call.set_arg("result_addr", result_addr)
- inst_rma_list.append(result_addr_alloc)
+ additional_alloc_list.append(result_addr_alloc)
# some RMA ops require compare_addr
@@ -640,11 +697,10 @@ def get_rma_call(tm: TemplateManager, rma_func, rank, identifier="RMACall") -> T
compare_addr = tm.add_stack_variable("int")
compare_addr_alloc = AllocCall(cf.dtype[0], cf.buf_size, compare_addr, rank=rank)
rma_call.set_arg("compare_addr", compare_addr)
- inst_rma_list.append(compare_addr_alloc)
+ additional_alloc_list.append(compare_addr_alloc)
- inst_rma_list.append(rma_call)
+ return (additional_alloc_list, rma_call, inst_rma_req_wait)
- return (inst_rma_list, inst_rma_req_wait_list)
def get_communicator(comm_create_func: str, tm: TemplateManager, before_idx: int = 0, identifier: str = "COMM"):
@@ -698,6 +754,7 @@ def get_intercomm(comm_create_func: str, tm: TemplateManager, before_idx: int =
:return: name of result variable
"""
assert comm_create_func in ["mpi_intercomm_create", "mpi_intercomm_create_from_groups", "mpi_intercomm_merge"]
+ assert name != "intercomm_base_comm"
if comm_create_func == "mpi_intercomm_create":
base_comm = tm.add_stack_variable("MPI_Comm")
diff --git a/scripts/errors/rma/EpochLifeCycle.py b/scripts/errors/rma/EpochLifeCycle.py
new file mode 100644
index 0000000000000000000000000000000000000000..789913e7bd912582a4bee01ba0175195e6d73cac
--- /dev/null
+++ b/scripts/errors/rma/EpochLifeCycle.py
@@ -0,0 +1,73 @@
+#! /usr/bin/python3
+from scripts.Infrastructure.Variables import *
+
+from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
+from scripts.Infrastructure.Instruction import Instruction
+
+from scripts.Infrastructure.MPICallFactory import CorrectMPICallFactory
+from scripts.Infrastructure.TemplateFactory import get_invalid_param_rma_case, get_rma_template
+from scripts.Infrastructure.MPICall import MPICall
+
+import copy
+
+
+class EpochLifeCycleRMA(ErrorGenerator):
+ def __init__(self):
+ pass
+
+ def get_feature(self):
+ return ["RMA"]
+
+ def generate(self, generate_level):
+ for sync_mode in ["fence", "winlockall", "winlock"]:
+ for rma_func in ["mpi_get", "mpi_put", "mpi_accumulate"]:
+ # epoch is not closed
+ tm = get_rma_template(rmaop_func=rma_func, sync_mode=sync_mode)
+ tm.remove_instruction("ENDRMAEPOCH")
+ # we set the corresponding RMA call as erroneous operation since it is not completed correctly
+ tm.get_instruction("RMAOP").set_has_error()
+ tm.set_description("EpochLifeCycle", "RMA epoch not closed")
+ yield tm
+
+ # epoch is not opened
+ tm = get_rma_template(rmaop_func=rma_func, sync_mode=sync_mode)
+ tm.remove_instruction("STARTRMAEPOCH")
+ # we set the corresponding RMA call as erroneous operation since it is not completed correctly
+ tm.get_instruction("RMAOP").set_has_error()
+ tm.set_description("EpochLifeCycle", "RMA epoch not closed")
+ yield tm
+
+ # double open of epoch
+ tm = get_rma_template(rmaop_func=rma_func, sync_mode=sync_mode)
+ # workaround to double epoch instruction
+ startrmaepoch = copy.deepcopy(tm.get_instruction("STARTRMAEPOCH"))
+ startrmaepoch.set_rank_executing(0)
+ startrmaepoch.set_has_error()
+ tm.insert_instruction(new_instruction=startrmaepoch, after_instruction="STARTRMAEPOCH")
+ tm.set_description("EpochLifeCycle", "RMA epoch opened twice")
+ yield tm
+
+
+ for rma_func in ["mpi_get", "mpi_put", "mpi_accumulate"]:
+ # mix fence with lockall, this should not be done at all
+ tm = get_rma_template(rmaop_func=rma_func, sync_mode="fence")
+ lock_all = CorrectMPICallFactory.mpi_win_lock_all()
+ lock_all.set_has_error()
+ unlock_all = CorrectMPICallFactory.mpi_win_unlock_all()
+ tm.insert_instruction(new_instruction=lock_all, after_instruction="STARTRMAEPOCH")
+ tm.insert_instruction(new_instruction=unlock_all, before_instruction="ENDRMAEPOCH")
+ tm.set_description("EpochLifeCycle", "Mixing fence with lock_all synchronization")
+ yield tm
+
+ # mix fence with lock, this should not be done at all
+ tm = get_rma_template(rmaop_func=rma_func, sync_mode="fence")
+ lock = CorrectMPICallFactory.mpi_win_lock()
+ lock.set_has_error()
+ lock.set_rank_executing(0)
+ unlock = CorrectMPICallFactory.mpi_win_unlock()
+ unlock.set_rank_executing(0)
+ tm.insert_instruction(new_instruction=lock, after_instruction="STARTRMAEPOCH")
+ tm.insert_instruction(new_instruction=unlock, before_instruction="ENDRMAEPOCH")
+ tm.set_description("EpochLifeCycle", "Mixing fence with lock synchronization")
+
+ yield tm
diff --git a/scripts/errors/rma/GlobalConcurrency.py b/scripts/errors/rma/GlobalConcurrency.py
index 743e76e0b7e7731b79d470715a9e67276e395987..dd4a39f7e827530d60e5630376fd1493e9cddd78 100644
--- a/scripts/errors/rma/GlobalConcurrency.py
+++ b/scripts/errors/rma/GlobalConcurrency.py
@@ -46,11 +46,12 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
def get_feature(self):
return ["RMA"]
- def fence(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def fence(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# open access epoch + sync
tm.register_instruction(self.cfmpi.mpi_win_fence())
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, add fence
@@ -58,6 +59,7 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(
self.cfmpi.mpi_win_fence(), rank_to_execute="all")
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -65,12 +67,13 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
return True
- def lockall(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def lockall(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# open access epoch + sync
tm.register_instruction(
self.cfmpi.mpi_win_lock_all(), rank_to_execute="all")
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
tm.register_instruction(
@@ -81,6 +84,7 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(
self.cfmpi.mpi_barrier(), rank_to_execute="all")
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -89,7 +93,7 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
return True
- def lockflush(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def lockflush(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
lock0 = self.cfmpi.mpi_win_lock()
unlock0 = self.cfmpi.mpi_win_unlock()
lock1 = self.cfmpi.mpi_win_lock()
@@ -107,36 +111,40 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(alloc_inst)
tm.register_instruction(lock0, rank_to_execute=0)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
tm.register_instruction(unlock0, rank_to_execute=0)
tm.register_instruction(
- lock1, rank_to_execute=op2[-1].get_rank_executing())
+ lock1, rank_to_execute=op2.get_rank_executing())
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
tm.register_instruction(
- unlock1, rank_to_execute=op2[-1].get_rank_executing())
+ unlock1, rank_to_execute=op2.get_rank_executing())
return True
- def request(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def request(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# only consider combination where the first operation is a request-based RMA call
- if not isinstance(op1[-1], MPICall) or not op1[-1].has_arg("request"):
+ if not isinstance(op1, MPICall) or not op1.has_arg("request"):
return False
# we assume that the first operation is request-based
wait = self.cfmpi.mpi_wait()
- wait.set_arg("request", op1[-1].get_arg("request"))
+ wait.set_arg("request", op1.get_arg("request"))
# open access epoch + sync
tm.register_instruction(self.cfmpi.mpi_win_lock_all())
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, wait for local completion of operation here
if shouldsync:
tm.register_instruction(wait, rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -144,7 +152,7 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
return True
- def pscw(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def pscw(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
tm.register_instruction("MPI_Group world_group;")
tm.register_instruction(
"MPI_Comm_group(MPI_COMM_WORLD, &world_group);")
@@ -155,6 +163,7 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
self.cfmpi.mpi_win_start(), rank_to_execute=0)
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, end access epoch here
@@ -162,6 +171,7 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(
self.cfmpi.mpi_win_complete(), rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# if accesses should not be synced, end access epoch here
@@ -178,11 +188,11 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
return True
- def get_mem_op(self, name: str, rank) -> Tuple[List[Instruction], List[Instruction]]:
+ def get_mem_op(self, name: str, rank) -> Tuple[List[Instruction], Instruction, Instruction | None]:
if name.startswith("mpi"):
return get_rma_call(self.tm, name, rank, name.replace("mpi_", ""))
else:
- return ([self.buf_instructions[name]], [])
+ return ([], self.buf_instructions[name], None)
def generate(self, generate_level, real_world_score_table):
@@ -240,7 +250,7 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
for shouldsync in [False, True]:
for (op1, op2) in itertools.product(ops1, ops2):
self.tm = TemplateManager(min_ranks=3)
- (win_alloc, win_free) = get_allocated_window(
+ (win_declare, win_alloc, win_free) = get_allocated_window(
"mpi_win_create", cf.get("win"), cf.winbuf_var_name, "int", "10")
# window allocation boilerplate
self.tm.register_instruction(win_alloc)
@@ -251,8 +261,8 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
op1_name = op1.replace("mpi_", "")
op2_name = op2.replace("mpi_", "")
- inst1, inst1_free = self.get_mem_op(op1, 0)
- inst2, inst2_free = self.get_mem_op(op2, 2)
+ alloc1, inst1, inst1_free = self.get_mem_op(op1, 0)
+ alloc2, inst2, inst2_free = self.get_mem_op(op2, 2)
# if the operations are not conflicting and we should sync, we do not have to generate this test case
if not hasconflict and shouldsync:
@@ -260,22 +270,24 @@ class GlobalConcurrencyErrorRMA(ErrorGenerator):
# if the operations are conflicting *and* we perform no synchronization between them, we have a race
if hasconflict and not shouldsync:
- inst1[-1].set_has_error(True)
- inst2[-1].set_has_error(True)
+ inst1.set_has_error(True)
+ inst2.set_has_error(True)
else:
- inst1[-1].set_has_error(False)
- inst2[-1].set_has_error(False)
+ inst1.set_has_error(False)
+ inst2.set_has_error(False)
# generate code for the given sync_mode
- valid_case = sync_mode(self.tm, alloc_inst, inst1, inst2, shouldsync)
+ valid_case = sync_mode(self.tm, alloc_inst, alloc1, inst1, alloc2, inst2, shouldsync)
if not valid_case:
# this case is not possible / redundant for this sync_mode, continue
continue
# finalize RMA call (if needed)
- self.tm.register_instruction(inst1_free)
- self.tm.register_instruction(inst2_free)
+ if inst1_free is not None:
+ self.tm.register_instruction(inst1_free)
+ if inst2_free is not None:
+ self.tm.register_instruction(inst2_free)
# window free boilerplate
self.tm.register_instruction(win_free)
diff --git a/scripts/errors/rma/InvalidBuffer.py b/scripts/errors/rma/InvalidBuffer.py
new file mode 100644
index 0000000000000000000000000000000000000000..77ff11daedc5e09d40ecda426b4065f09d200c57
--- /dev/null
+++ b/scripts/errors/rma/InvalidBuffer.py
@@ -0,0 +1,42 @@
+#! /usr/bin/python3
+from scripts.Infrastructure.Variables import *
+
+from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
+from scripts.Infrastructure.Instruction import Instruction
+
+from scripts.Infrastructure.MPICallFactory import CorrectMPICallFactory
+from scripts.Infrastructure.TemplateFactory import get_invalid_param_rma_case, get_rma_template
+from scripts.Infrastructure.MPICall import MPICall
+
+
+class InvalidBufferErrorRMA(ErrorGenerator):
+ def __init__(self):
+ pass
+
+ def get_feature(self):
+ return ["RMA"]
+
+ def generate(self, generate_level):
+ rma_funcs = ["mpi_get", "mpi_rget", "mpi_put", "mpi_rput", "mpi_accumulate", "mpi_raccumulate",
+ "mpi_get_accumulate", "mpi_rget_accumulate", "mpi_fetch_and_op", "mpi_compare_and_swap"]
+
+ # go through alloc functions (Win_alloc, Win_create) and set NULL
+ for alloc_call in ["mpi_win_allocate", "mpi_win_create"]:
+ tm = get_rma_template(win_alloc_func=alloc_call)
+ for call in tm.get_instruction(identifier="RMA_WIN_ALLOC", return_list=True):
+ for buffer_arg in ["base", "baseptr"]:
+ if isinstance(call, MPICall) and call.has_arg(buffer_arg):
+ call.set_arg(buffer_arg, "NULL")
+ call.set_has_error()
+ tm.set_description("InvalidBuffer",
+ "Invalid Buffer in " + call.get_function())
+ yield tm
+
+ # go through RMA op buffers and set NULL
+ for func in rma_funcs:
+ tm = get_invalid_param_rma_case("origin_addr", func, "NULL")
+ tm.get_instruction("RMAOP").set_has_error()
+ tm.set_description("InvalidBuffer",
+ "Invalid Buffer in " + func)
+ yield tm
+
diff --git a/scripts/errors/rma/InvalidDataType.py b/scripts/errors/rma/InvalidDataType.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d53f364bf2e93a14dd83aa2210c9f013a7dc07d
--- /dev/null
+++ b/scripts/errors/rma/InvalidDataType.py
@@ -0,0 +1,43 @@
+#! /usr/bin/python3
+from scripts.Infrastructure.Variables import *
+
+from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
+from scripts.Infrastructure.Instruction import Instruction
+
+from scripts.Infrastructure.MPICallFactory import CorrectMPICallFactory
+from scripts.Infrastructure.TemplateFactory import get_invalid_param_rma_case
+
+
+class InvalidDatatypeErrorRMA(ErrorGenerator):
+ def __init__(self):
+ pass
+
+ def get_feature(self):
+ return ["RMA"]
+
+ def generate(self, generate_level):
+ rma_funcs = []
+ if generate_level <= BASIC_TEST_LEVEL:
+ rma_funcs = ["mpi_get", "mpi_put", "mpi_accumulate"]
+ else:
+ rma_funcs = ["mpi_get", "mpi_rget", "mpi_put", "mpi_rput", "mpi_accumulate", "mpi_raccumulate",
+ "mpi_get_accumulate", "mpi_rget_accumulate", "mpi_fetch_and_op", "mpi_compare_and_swap"]
+
+ for func in rma_funcs:
+ if getattr(CorrectMPICallFactory, func)().has_arg("target_datatype"):
+ # Use MPI_DATATYPE_NULL for target_datatype, TODO: Is this really undefined?
+ tm = get_invalid_param_rma_case("target_datatype", func, "MPI_DATATYPE_NULL")
+ tm.get_instruction("RMAOP").set_has_error()
+ tm.set_description("InvalidDatataype",
+ "Invalid Datatype: MPI_DATATYPE_NULL")
+ yield tm
+
+ # # Use freed datatype
+ tm = get_invalid_param_rma_case("target_datatype", func, "type")
+ datatype_register_free = Instruction("MPI_Datatype type; MPI_Type_contiguous (2, MPI_INT, &type); MPI_Type_commit(&type);MPI_Type_free(&type);", rank=0)
+ tm.get_instruction("RMAOP").set_has_error()
+ tm.insert_instruction(before_instruction="RMAOP", new_instruction=datatype_register_free)
+ tm.set_description("InvalidDatataype",
+ "Invalid Datatype: Datatype is freed before it is actually used")
+ yield tm
+
diff --git a/scripts/errors/rma/InvalidRank.py b/scripts/errors/rma/InvalidRank.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ffe6e10df92100a84e7efb0c050c067ba51b4d0
--- /dev/null
+++ b/scripts/errors/rma/InvalidRank.py
@@ -0,0 +1,35 @@
+#! /usr/bin/python3
+from scripts.Infrastructure.Variables import *
+
+from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
+from scripts.Infrastructure.Instruction import Instruction
+
+from scripts.Infrastructure.MPICallFactory import CorrectMPICallFactory
+from scripts.Infrastructure.TemplateFactory import get_invalid_param_rma_case
+
+
+class InvalidRankErrorRMA(ErrorGenerator):
+ invalid_ranks = ["-1", "nprocs", "MPI_PROC_NULL"]
+
+ def __init__(self):
+ pass
+
+ def get_feature(self):
+ return ["RMA"]
+
+ def generate(self, generate_level):
+ rma_funcs = []
+ if generate_level <= BASIC_TEST_LEVEL:
+ rma_funcs = ["mpi_get", "mpi_put", "mpi_accumulate"]
+ else:
+ rma_funcs = ["mpi_get", "mpi_rget", "mpi_put", "mpi_rput", "mpi_accumulate", "mpi_raccumulate",
+ "mpi_get_accumulate", "mpi_rget_accumulate", "mpi_fetch_and_op", "mpi_compare_and_swap"]
+
+ for func in rma_funcs:
+ for rank_to_use in self.invalid_ranks:
+ tm = get_invalid_param_rma_case("target_rank", func, rank_to_use)
+ tm.get_instruction("RMAOP").set_has_error()
+ tm.set_description("InvalidParam-Rank",
+ "Invalid Rank: %s" % rank_to_use)
+
+ yield tm
\ No newline at end of file
diff --git a/scripts/errors/rma/InvalidWin.py b/scripts/errors/rma/InvalidWin.py
new file mode 100644
index 0000000000000000000000000000000000000000..d7a725f42c432a3d67be20491589fd21aa5d2c22
--- /dev/null
+++ b/scripts/errors/rma/InvalidWin.py
@@ -0,0 +1,38 @@
+#! /usr/bin/python3
+from scripts.Infrastructure.Variables import *
+
+from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
+from scripts.Infrastructure.Instruction import Instruction
+
+from scripts.Infrastructure.MPICallFactory import CorrectMPICallFactory
+from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory
+from scripts.Infrastructure.TemplateFactory import get_invalid_param_rma_case, get_rma_template
+from scripts.Infrastructure.MPICall import MPICall
+
+
+class InvalidWinErrorRMA(ErrorGenerator):
+ def __init__(self):
+ pass
+
+ def get_feature(self):
+ return ["RMA"]
+
+ def generate(self, generate_level):
+ tm = get_rma_template()
+ tm.remove_instruction("RMA_WIN_ALLOC") # remove window allocation
+ # opening epoch on non-initialized window is the actual error
+ tm.get_instruction("STARTRMAEPOCH").set_has_error()
+ tm.set_description("InvalidWin",
+ "RMA on non-initialized window")
+ yield tm
+
+ # free window too early
+ tm = get_rma_template()
+ win_free_early = Instruction(f"MPI_Win_free(&{CorrectParameterFactory().get("win")});")
+ win_free_early.set_has_error()
+ tm.insert_instruction(new_instruction=win_free_early, before_instruction="STARTRMAEPOCH")
+
+ tm.set_description("InvalidWin",
+ "RMA on freed window")
+
+ yield tm
diff --git a/scripts/errors/rma/LocalConcurrency.py b/scripts/errors/rma/LocalConcurrency.py
index a4485402cc3e68de9ecf6f04b8d989ac419cced2..606a7c9fbc43a933597f70a3d8db5bc155503838 100644
--- a/scripts/errors/rma/LocalConcurrency.py
+++ b/scripts/errors/rma/LocalConcurrency.py
@@ -27,11 +27,12 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
def get_feature(self):
return ["RMA"]
- def fence(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def fence(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# open access epoch + sync
tm.register_instruction(self.cfmpi.mpi_win_fence())
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, add another fence (rank 0)
@@ -39,6 +40,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(
self.cfmpi.mpi_win_fence(), rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# if accesses should be synced, add another fence (rank 1)
@@ -51,11 +53,12 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
return True
- def lockallflush(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def lockallflush(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# open access epoch + sync
tm.register_instruction(self.cfmpi.mpi_win_lock_all())
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, add flush
@@ -63,6 +66,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(
self.cfmpi.mpi_win_flush_all(), rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -70,7 +74,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
return True
- def lockallflushlocal(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def lockallflushlocal(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# should sync: MPI_Win_lock_all - op1 - MPI_Win_flush_local_all - op2 - MPI_Win_unlock_all
# shold not sync: MPI_Win_lock_all - op1 - op2 - MPI_Win_unlock_all
@@ -78,6 +82,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(self.cfmpi.mpi_win_lock_all())
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, add flush_local
@@ -85,6 +90,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(
self.cfmpi.mpi_win_flush_local_all(), rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -92,7 +98,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
return True
- def lockunlock(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def lockunlock(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# should sync: MPI_Win_lock - op1 - MPI_Win_unlock - op2
# shold not sync: MPI_Win_lock - op1 - op2 - MPI_Win_unlock
@@ -104,19 +110,22 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(lock, rank_to_execute=0)
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, add flush here
if shouldsync:
tm.register_instruction(unlock, rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
else:
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
tm.register_instruction(unlock, rank_to_execute=0)
return True
- def lockflush(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def lockflush(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
lock = self.cfmpi.mpi_win_lock()
flush = self.cfmpi.mpi_win_flush()
unlock = self.cfmpi.mpi_win_unlock()
@@ -127,12 +136,14 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(lock, rank_to_execute=0)
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, add flush here
if shouldsync:
tm.register_instruction(flush, rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -140,7 +151,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
return True
- def lockflushlocal(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def lockflushlocal(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
lock = self.cfmpi.mpi_win_lock()
flush_local = self.cfmpi.mpi_win_flush_local()
unlock = self.cfmpi.mpi_win_unlock()
@@ -151,12 +162,14 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(lock, rank_to_execute=0)
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, add flush here
if shouldsync:
tm.register_instruction(flush_local, rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -164,25 +177,27 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
return True
- def request(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def request(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
# only consider combination where the first operation is a request-based RMA call
- if not isinstance(op1[-1], MPICall) or not op1[-1].has_arg("request"):
+ if not isinstance(op1, MPICall) or not op1.has_arg("request"):
return False
# we assume that the first operation is request-based
wait = self.cfmpi.mpi_wait()
- wait.set_arg("request", op1[-1].get_arg("request"))
+ wait.set_arg("request", op1.get_arg("request"))
# open access epoch + sync
tm.register_instruction(self.cfmpi.mpi_win_lock_all())
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, wait for local completion of operation here
if shouldsync:
tm.register_instruction(wait, rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# finish access epoch + sync
@@ -190,7 +205,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
return True
- def pscw(self, tm: TemplateManager, alloc_inst: Instruction, op1: List[Instruction], op2: List[Instruction], shouldsync: bool):
+ def pscw(self, tm: TemplateManager, alloc_inst: Instruction, alloc1: List[Instruction], op1: Instruction, alloc2: List[Instruction], op2: Instruction, shouldsync: bool):
tm.register_instruction("MPI_Group world_group;")
tm.register_instruction(
"MPI_Comm_group(MPI_COMM_WORLD, &world_group);")
@@ -201,6 +216,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
self.cfmpi.mpi_win_start(), rank_to_execute=0)
tm.register_instruction(alloc_inst)
+ tm.register_instruction(alloc1)
tm.register_instruction(op1, "OP1")
# if accesses should be synced, end access epoch here
@@ -208,6 +224,7 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
tm.register_instruction(
self.cfmpi.mpi_win_complete(), rank_to_execute=0)
+ tm.register_instruction(alloc2)
tm.register_instruction(op2, "OP2")
# if accesses should not be synced, end access epoch here
@@ -224,11 +241,11 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
return True
- def get_mem_op(self, name: str, rank) -> Tuple[List[Instruction], List[Instruction]]:
+ def get_mem_op(self, name: str, rank) -> Tuple[List[Instruction], Instruction, Instruction | None]:
if name.startswith("mpi"):
return get_rma_call(self.tm, name, rank, name.replace("mpi_", ""))
else:
- return ([self.buf_instructions[name]], [])
+ return ([], self.buf_instructions[name], None)
def generate(self, generate_level, real_world_score_table):
# build set of calls based on generate level, for level 1 just a few basic calls,
@@ -283,9 +300,10 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
for shouldsync in [False, True]:
for (op1, op2) in itertools.product(ops1, ops2):
self.tm = TemplateManager()
- (win_alloc, win_free) = get_allocated_window(
+ (win_declare, win_alloc, win_free) = get_allocated_window(
"mpi_win_create", cf.get("win"), cf.winbuf_var_name, "int", "10")
# window allocation boilerplate
+ self.tm.register_instruction(win_declare)
self.tm.register_instruction(win_alloc)
# local buffer allocation
@@ -294,8 +312,8 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
op1_name = op1.replace("mpi_", "")
op2_name = op2.replace("mpi_", "")
- inst1, inst1_free = self.get_mem_op(op1, 0)
- inst2, inst2_free = self.get_mem_op(op2, 0)
+ alloc1, inst1, inst1_free = self.get_mem_op(op1, 0)
+ alloc2, inst2, inst2_free = self.get_mem_op(op2, 0)
# if the operations are not conflicting and we should sync, we do not have to generate this test case
if not hasconflict and shouldsync:
@@ -303,22 +321,24 @@ class LocalConcurrencyErrorRMA(ErrorGenerator):
# if the operations are conflicting *and* we perform no synchronization between them, we have a race
if hasconflict and not shouldsync:
- inst1[-1].set_has_error(True)
- inst2[-1].set_has_error(True)
+ inst1.set_has_error(True)
+ inst2.set_has_error(True)
else:
- inst1[-1].set_has_error(False)
- inst2[-1].set_has_error(False)
+ inst1.set_has_error(False)
+ inst2.set_has_error(False)
# generate code for the given sync_mode
- valid_case = sync_mode(self.tm, alloc_inst, inst1, inst2, shouldsync)
+ valid_case = sync_mode(self.tm, alloc_inst, alloc1, inst1, alloc2, inst2, shouldsync)
if not valid_case:
# this case is not possible / redundant for this sync_mode, continue
continue
# finalize RMA call (if needed)
- self.tm.register_instruction(inst1_free)
- self.tm.register_instruction(inst2_free)
+ if inst1_free is not None:
+ self.tm.register_instruction(inst1_free)
+ if inst2_free is not None:
+ self.tm.register_instruction(inst2_free)
# window free boilerplate
self.tm.register_instruction(win_free)