Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • coll
  • devel-ES
  • devel-TJ
  • dtypes
  • fix-rma-lockunlock
  • fortran
  • infrasructure-patch-1
  • infrastructure-patch-3
  • infrastructure-patch2
  • instructionblock-lists
  • main
  • mbi
  • merged
  • must-json
  • must-toolcoverage
  • p2p
  • paper_repro
  • parcoach
  • rma
  • toolcoverage
  • tools
  • tools-parallel
  • usertypes
23 results

Target

Select target project
  • hpc-public/mpi-bugbench
1 result
Select Git revision
  • coll
  • devel-ES
  • devel-TJ
  • dtypes
  • fix-rma-lockunlock
  • fortran
  • infrasructure-patch-1
  • infrastructure-patch-3
  • infrastructure-patch2
  • instructionblock-lists
  • main
  • mbi
  • merged
  • must-json
  • must-toolcoverage
  • p2p
  • paper_repro
  • parcoach
  • rma
  • toolcoverage
  • tools
  • tools-parallel
  • usertypes
23 results
Show changes

Commits on Source 42

Showing
with 1666 additions and 858 deletions
#! /usr/bin/python3
from __future__ import annotations
from typing_extensions import override
from scripts.Infrastructure.Instruction import Instruction
from scripts.Infrastructure.Variables import ERROR_MARKER_COMMENT
from scripts.Infrastructure.Variables import ERROR_MARKER_COMMENT_BEGIN, ERROR_MARKER_COMMENT_END
alloc_template = """
@{TYPE}@* @{NAME}@ = (@{TYPE}@*) @{FUNCTION}@(@{NUM}@ @{SEP}@ sizeof(@{TYPE}@));
......@@ -25,7 +27,8 @@ alloc_template = """
class AllocCall(Instruction):
@override
def __init__(self, type: str, num_elements: str, name: str = "buf", use_malloc: bool = False):
def __init__(self, type: str, num_elements: str, name: str = "buf", use_malloc: bool = False,
rank: str | int = 'all', identifier: str = None):
"""
Creates a New allocation Call
......@@ -35,7 +38,7 @@ class AllocCall(Instruction):
name: name of buffer variable
use_malloc: True: use Malloc, False: use calloc for allocation
"""
super().__init__("")
super().__init__("", rank,identifier)
self._use_malloc = use_malloc
self._type = type
self._num_elements = num_elements
......@@ -58,7 +61,7 @@ class AllocCall(Instruction):
.replace("@{SEP}@", delim))
if self._has_error:
s += ERROR_MARKER_COMMENT
s = ERROR_MARKER_COMMENT_BEGIN + s + ERROR_MARKER_COMMENT_END
return s
......
......@@ -61,6 +61,10 @@ class CorrectParameterFactory:
return "&group"
if param in ["color"]:
return "1"
if param in ["message"]:
return "&msg"
if param in ["flag"]:
return "&flag"
if param in ["split_type"]:
return "MPI_COMM_TYPE_SHARED"
if param in ["key"]:
......
......@@ -69,7 +69,8 @@ class GeneratorManager:
return case_name + "-" + str(num).zfill(digits_to_use) + suffix
def generate(self, outpath: str | Path | os.PathLike[str], filterlist_:typing.Sequence[str]=None, print_progress_bar:bool=True, overwrite:bool=True, generate_full_set:bool=False,
def generate(self, outpath: str | Path | os.PathLike[str], filterlist_: typing.Sequence[str] = None,
print_progress_bar: bool = True, overwrite: bool = True, generate_full_set: bool = False,
try_compile: bool = False, max_mpi_version: str = "4.0", use_clang_format: bool = True):
"""
Generates test cases based on the specified parameters.
......@@ -115,6 +116,9 @@ class GeneratorManager:
cases_generated = 0
for generator in generators_to_use:
# use first feature as category if generatro has multiple
categroy_path = os.path.join(outpath, generator.get_feature()[0])
os.makedirs(categroy_path, exist_ok=True)
for result_error in generator.generate(generate_full_set):
assert isinstance(result_error, TemplateManager)
......@@ -122,7 +126,7 @@ class GeneratorManager:
if not float(result_error.get_version()) > float(max_mpi_version):
case_name = result_error.get_short_descr()
fname = self.get_filename(case_name)
full_name = os.path.join(outpath, fname)
full_name = os.path.join(categroy_path, fname)
if not overwrite and os.path.isfile(full_name):
assert False and "File Already exists"
......
#! /usr/bin/python3
from scripts.Infrastructure.Variables import ERROR_MARKER_COMMENT
from __future__ import annotations
from scripts.Infrastructure.Variables import ERROR_MARKER_COMMENT_BEGIN, ERROR_MARKER_COMMENT_END
class Instruction(object):
"""
Base class to represent an Instruction
the identifier is used, in order to reference that instruction in the Template Manager (e.g. to change it). can be None
"""
def __init__(self, str_representation):
def __init__(self, str_representation: str, rank: str | int = 'all', identifier: str = None):
self._str_representation = str_representation
self._has_error = False
self._identifier = identifier
if isinstance(rank, str):
assert rank in ['all', 'not0']
self._rank = rank
def set_has_error(self, has_error: bool = True):
self._has_error = has_error
def get_identifier(self) -> str:
return self._identifier
def set_identifier(self, identifier: str):
self._identifier = identifier
def get_ranks_executing(self) -> str | int:
return self._rank
def set_ranks_executing(self, rank: str | int):
if isinstance(rank, str):
assert rank in ['all', 'not0']
self._rank = rank
def __str__(self):
if self._has_error:
return self._str_representation + ERROR_MARKER_COMMENT
return ERROR_MARKER_COMMENT_BEGIN + self._str_representation + ERROR_MARKER_COMMENT_END
else:
return self._str_representation
from __future__ import annotations
import typing
from scripts.Infrastructure.Instruction import Instruction
from scripts.Infrastructure.MPICall import MPI_Call
class InstructionBlock:
"""
Class Overview:
The `InstructionBlock` class represents a block of instructions in a Testcase (to be registered for a template).
First, the Instructions for all ranks are executed (in the order they are registered)
Then each thread executes the instructions registered to this specific rank
If one need a different Order: use multiple Instruction Blocks
Methods:
- `__init__(self)`: Initializes a new instance of the InstructionBlock class.
- `register_operation(self, op, kind='all')`: Registers an operation based on rank.
- `get_version(self)`: Retrieves required MPI version
- `__str__(self)`: Converts the InstructionBlock instance to a string, replacing placeholders.
"""
def __init__(self, name: str = None):
"""
Initialize an empty InstructionBlock
Parameters:
- name (str): The name of the block (for referencing this block with the template Manager)
May be None, does not influence the code generated
"""
self.operations = {'all': [], 'not0': [], }
assert not isinstance(name, int)
self.name = name
def register_instruction(self, op: str | Instruction | typing.List[Instruction], kind: str | int = 'all'):
"""
Registers an operation based on rank.
Parameters:
- op: The operation (or list of Operations) to register.
- kind: Rank to execute the operation ('all', 'not0', or integer).
- all: all Ranks execute this operation
- not0: all Ranks but the Root (rank 0) execute
- Or the integer of the rank that should execute
Note: if a str is passed as the operation, it will create a new Instruction from the given string
"""
if isinstance(op, str):
op = Instruction(op)
if kind == 'all':
if isinstance(op, list):
self.operations['all'].extend(op)
else:
self.operations['all'].append(op)
elif kind == 'not0':
if isinstance(op, list):
self.operations['not0'].extend(op)
else:
self.operations['not0'].append(op)
else:
as_int = int(kind) # will Raise ValueError if not integer
if as_int not in self.operations:
self.operations[as_int] = []
if isinstance(op, list):
self.operations[as_int].extend(op)
else:
self.operations[as_int].append(op)
def get_version(self) -> str:
"""
Retrieves the minimum required MPI version.
Returns:
str: The MPI version used.
"""
max_v = "0.0"
for k, v in self.operations.items():
for op in v:
if isinstance(op, MPI_Call):
max_v = max(op.get_version(), max_v)
return max_v
def __str__(self):
"""
Converts the InstructionBlock instance to a string, replacing placeholders.
Returns:
str: The string representation of the InstructionBlock.
"""
result_str = ""
for key, val in self.operations.items():
if key == 'all':
for op in val:
result_str += str(op) + "\n"
elif key == 'not0':
if len(val) > 0:
result_str += "if (rank != 0) {\n"
for op in val:
result_str += str(op) + "\n"
result_str += "}\n"
else:
assert len(val) > 0
result_str += "if (rank == %d) {\n" % int(key)
for op in val:
result_str += str(op) + "\n"
result_str += "}\n"
return result_str
def has_instruction(self, kind: int | str = 'all', index: int = 0) -> bool:
"""
Checks if the Block has an operation with the given index and kind
Parameters:
- kind ('all','not0' or integer): which ranks should execute the operation
- index (int ): the index of the operation within the given kind
Returns:
boolean
"""
try:
result = self.operations[kind][index]
return True
except (KeyError, IndexError) as e:
return False
def get_instruction(self, kind: int | str = 'all', index: str | int = 0) -> Instruction | typing.List[Instruction]:
"""
Retrieve the operation registered. will Raise IndexError if not present
Parameters:
- kind ('all','not0' or integer): which ranks should execute the operation
- index ('all' or int): the index of the operation within the given kind; 'all' means that the list of all operations for the kind is returned
Returns:
str: The operation specified by kind and index
"""
if index == 'all':
return self.operations[kind]
else:
as_int = int(index) # will Raise ValueError if not integer
return self.operations[kind][as_int]
def replace_instruction(self, op: str | Instruction | typing.List[Instruction], kind: str | int = 'all',
index: str | int = 0):
"""
Replace the operation registered. will Raise IndexError if not present
Parameters:
- op the new operation or list of operations
- kind ('all','not0' or integer): which ranks should execute the operation
- index ('all' or int): the index of the operation within the given kind; 'all' means all operations will be replaced with the given list
Notes : if one wants to replace all operations one needs to provide a list
if one only wants to replace one operation: no list of operations is allowed
if a string is passed as the operation, it will create a new Instruction
"""
if isinstance(op, str):
op = Instruction(op)
if index == 'all':
if not isinstance(op, list):
raise ValueError('Provide List for replacement')
self.operations[kind] = op
else:
as_int = int(index) # will Raise ValueError if not integer
print(op)
if not isinstance(op, Instruction):
raise ValueError('Provide Instruction')
if len(self.operations[kind]) < as_int:
raise IndexError("Operation Not Found")
self.operations[kind][as_int] = op
def insert_instruction(self, op: str | Instruction | typing.List[Instruction], kind: str | int = 'all',
before_index: int = 0):
"""
Inserts an operation before the specified one. will Raise IndexError if not present
Parameters:
- op the new operation or list of operations
- kind ('all','not0' or integer): which ranks should execute the operation
- index (int): the index of the operation within the given kind
note: if str is passed as the operation, it will Create a New Instruction
"""
if isinstance(op, str):
op = Instruction(op)
as_int = int(before_index) # will Raise ValueError if not integer
if len(self.operations[kind]) < before_index:
raise IndexError("Operation Not Found")
if isinstance(op, list):
self.operations[kind] = (
self.operations[kind][0:before_index - 1] + op + self.operations[kind][before_index:])
else:
self.operations[kind].insert(before_index, op)
def remove_instruction(self, kind: str | int = 'all', index: str | int = 0):
"""
Removes the operation registered. will Raise IndexError if not present
Parameters:
- kind ('all','not0' or integer): which ranks should execute the operation
- index ('all' or int): the index of the operation within the given kind
"""
if index == 'all':
self.operations[kind] = []
else:
as_int = int(index) # will Raise ValueError if not integer
if len(self.operations[kind]) < index:
raise IndexError("Operation Not Found")
del self.operations[kind][index]
......@@ -9,13 +9,13 @@ from scripts.Infrastructure.MPIAPIInfo.MPIAPIParameters import get_mpi_version_d
template = """
@staticmethod
def @{FUNC_KEY}@(*args):
return MPI_Call("@{FUNC_NAME}@", OrderedDict(@{PARAM_DICT}@), "@{VERSION}@")
return MPICall("@{FUNC_NAME}@", OrderedDict(@{PARAM_DICT}@), "@{VERSION}@")
"""
file_header="""#! /usr/bin/python3
from collections import OrderedDict
from scripts.Infrastructure.MPICall import MPI_Call
from scripts.Infrastructure.MPICall import MPICall
class MPICallFactory:
......
#! /usr/bin/python3
from __future__ import annotations
import typing
from typing_extensions import override
from scripts.Infrastructure.Instruction import Instruction
from scripts.Infrastructure.Variables import ERROR_MARKER_COMMENT
from scripts.Infrastructure.Variables import ERROR_MARKER_COMMENT_BEGIN, ERROR_MARKER_COMMENT_END
class MPI_Call(Instruction):
class MPICall(Instruction):
@override
def __init__(self, function: str, args: typing.OrderedDict[str, str], version: str):
super().__init__("")
def __init__(self, function: str, args: typing.OrderedDict[str, str], version: str, rank: str | int = 'all',
identifier: str = None):
super().__init__("", rank, identifier)
self._function = function
self._args = args
self._version = version
self._has_error = False
@override
def __str__(self):
......@@ -25,7 +27,7 @@ class MPI_Call(Instruction):
s = s[:-2] # remove last , and space
s += ");"
if self._has_error:
s += ERROR_MARKER_COMMENT
s = ERROR_MARKER_COMMENT_BEGIN + s + ERROR_MARKER_COMMENT_END
return s
......
This diff is collapsed.
#! /usr/bin/python3
from __future__ import annotations
import typing
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICall import MPI_Call
from scripts.Infrastructure.Instruction import Instruction
from scripts.Infrastructure.MPICall import MPICall
template = """// @{generatedby}@
/* ///////////////////////// The MPI Bug Bench ////////////////////////
......@@ -62,13 +68,27 @@ class TemplateManager:
Class Overview:
The `TemplateManager` class is responsible for managing MPI error case templates.
Attributes:
_descr_full (str): The full description of the template.
_descr_short (str): The short description of the template.
_instructions (list): List of instruction blocks.
_thread_level (str): The MPI thread level to use.
_min_ranks (int): The minimum number of MPI ranks.
_has_finalize (bool): Whether to include a call to MPI_Finalize.
_has_init (bool): Whether to include a call to MPI_Init.
Methods:
- `__init__(self)`: Initializes a new instance of the TemplateManager class.
- `__str__(self)`: Converts the TemplateManager instance to a string, replacing placeholders.
- `register_instruction_block(self, block)`: Registers an instruction block with the template.
- `get_version(self)`: Retrieves required MPI version.
- `set_description(self, descr_short, descr_full)`: Sets the short and full descriptions.
- `get_short_descr(self)`: Retrieves the short description .
- __init__(self, min_ranks: int = 2, thread_level: str = None, has_finalize: bool = True,
has_init: bool = True): Initializes a new instance of the TemplateManager class.
- __str__(self): Converts the TemplateManager instance to a string, replacing placeholders.
- register_instruction(self, inst: Instruction | typing.List[Instruction]): Registers an instruction with the template.
- get_version(self) -> str: Retrieves the minimum required MPI version.
- set_description(self, descr_short: str, descr_full: str): Sets the short and full descriptions for the template.
- get_short_descr(self) -> str: Retrieves the short description (to use as a filename).
- get_instruction(self, identifier: str = None, return_list=False, idx: int = None) -> Instruction | typing.List[Instruction]: Retrieves an instruction or list of them based on its identifier or index.
- insert_instruction(self, new_instruction: Instruction, after_instruction: str | int = None, before_instruction: str | int = None): Inserts a new instruction into the template.
- remove_instruction(self, identifier: str = None, idx: int | typing.List[int] = None): Removes an instruction from the template.
- replace_instruction(self, new_instruction=Instruction, identifier: str = None, idx: int | typing.List[int] = None): Replaces an instruction in the template with a new one.
"""
def __init__(self, min_ranks: int = 2, thread_level: str = None, has_finalize: bool = True, has_init: bool = True):
......@@ -83,7 +103,7 @@ class TemplateManager:
"""
self._descr_full = ""
self._descr_short = ""
self._blocks = []
self._instructions = []
self._thread_level = thread_level
self._min_ranks = min_ranks
self._has_finalize = has_finalize
......@@ -94,9 +114,23 @@ class TemplateManager:
Converts the TemplateManager to a string, replacing placeholders.
"""
version = self.get_version()
block_string = ""
for block in self._blocks:
block_string += str(block) + "\n"
code_string = ""
current_rank = 'all'
for inst in self._instructions:
if inst.get_ranks_executing != current_rank:
if current_rank != 'all':
code_string = code_string + "}\n" # end previous if
current_rank = inst.get_ranks_executing()
if current_rank == 'not0':
code_string = code_string + "if (rank!=0){\n"
elif current_rank != 'all':
code_string = code_string + "if (rank==%D){\n" % current_rank
code_string += str(inst) + "\n"
# end for inst
if current_rank != 'all':
code_string = code_string + "}\n" # end previous if
init_string = ""
if self._has_init:
......@@ -115,15 +149,18 @@ class TemplateManager:
.replace("@{mpi_finalize}@", finalize_string)
.replace("@{desc}@", self._descr_full)
.replace("@{version}@", version)
.replace("@{test_code}@", block_string))
.replace("@{test_code}@", code_string))
def register_instruction_block(self, block: InstructionBlock):
def register_instruction(self, inst: Instruction | typing.List[Instruction]):
"""
Registers an instruction block with the template. inserting it at the end, before the mpi finalize
Parameters:
- block: The instruction block to register.
"""
self._blocks.append(block)
if isinstance(inst, list):
self._instructions.extend(inst)
else:
self._instructions.append(inst)
def get_version(self) -> str:
"""
......@@ -157,118 +194,156 @@ class TemplateManager:
assert self._descr_short != ""
return self._descr_short
def get_block(self, block_name: str = None, idx: int = None) -> InstructionBlock:
def get_instruction(self, identifier: str = None, return_list=False, idx: int = None) -> Instruction | typing.List[
Instruction]:
"""
Retrieves the given Instruction Block Either by name or by index
Raises IndexError if the specified block is not found
Raises IndexError if multiple Blocks with the given name are found
Raises ValueError if Both a block name and index are given (or none is given)
Args:
block_name (str): The name of the InstructionBlock to receive
idx (int): index of the InstructionBlock to retrieve
Retrieves an instruction or list fh them based on its identifier or index.
Parameters:
identifier (str): The identifier of the instruction.
return_list (bool): Whether to return a list of instruction.
idx (int): The index of the instruction block.
Returns:
the specified Block
Instruction | List[Instruction]: The instruction block(s).
Raises:
ValueError: if both index and identifier are provided
IndexError: if return_list is False and not exactely one instruction is found by index
"""
if block_name is not None:
if idx is not None:
raise ValueError("Both block name and index are given")
to_return = [b for b in self._blocks if b.name == block_name]
if len(to_return) == 0:
raise IndexError("Block Not Found")
if len(to_return) > 1:
raise IndexError("Multiple Blocks Found")
# assert only one param is not None
parameters = [identifier, idx]
if parameters.count(None) != 1:
raise ValueError("Only one parameter is allowed to be specified")
if identifier is not None:
to_return = [i for i in self._instructions if i.get_identifier() == identifier]
if return_list:
return to_return
if len(to_return) == 1:
return to_return[0]
if len(to_return) == 0:
raise IndexError("Found no matching Instruction")
raise IndexError("Found too many elements")
if idx is not None:
if block_name is not None:
raise ValueError("Both block name and index are given")
return self._blocks[idx]
if return_list:
return [self._instructions[idx]]
return self._instructions[idx]
raise ValueError("Neither Both block name nor index is given")
def insert_block(self, new_block: InstructionBlock, after_block_name: str = None, after_idx: int = None):
def __get_instruction_index(self, identifier: str) -> typing.List[int]:
"""
inserts the given Instruction Block AFTER the one specified Either by name or by index
Raises IndexError if the specified block is not found
Raises IndexError if multiple Blocks with the given name are found
Raises ValueError if Both a block name and index are given (or none is given)
Args:
new_block (InstructionBlock): the block to insert
after_block_name (str): The name of the InstructionBlock to receive
after_idx (int): index of the InstructionBlock to retrieve
internal helper function to receive the indices of instructions with ghe given identifier
"""
if after_block_name is not None:
if after_idx is not None:
raise ValueError("Both block name and index are given")
to_return = [b for b in self._blocks if b.name == after_block_name]
if len(to_return) == 0:
raise IndexError("Block Not Found")
if len(to_return) > 1:
raise IndexError("Multiple Blocks Found")
self._blocks.insert(self._blocks.index(to_return[0]) + 1, new_block)
return
if after_idx is not None:
if after_block_name is not None:
raise ValueError("Both block name and index are given")
self._blocks.insert(after_idx + 1, new_block)
return
return [idx for inst, idx in enumerate(self._instructions) if inst.get_identifier() == identifier]
raise ValueError("Neither Both block name nor index is given")
def insert_instruction(self, new_instruction: Instruction, after_instruction: str | int = None,
before_instruction: str | int = None):
"""
Inserts a new instruction into the template.
def remove_block(self, block_name: str = None, idx: int = None):
Parameters:
new_instruction (Instruction): The new instruction to insert.
after_instruction (str | int): The instruction after which to insert the new one (identifier or index).
before_instruction (str | int): The instruction before which to insert the new one (identifier or index).
Raises:
ValueError: if both before and after are provided
IndexError: if it finds multiple places to insert by identifier
"""
Removes the given Instruction Block Either by name or by index
Raises IndexError if the specified block is not found
Raises IndexError if multiple Blocks with the given name are found
Raises ValueError if Both a block name and index are given (or none is given)
Args:
block_name (str): The name of the InstructionBlock to receive
idx (int): index of the InstructionBlock to retrieve
# assert only one param is not None
parameters = [after_instruction, before_instruction]
if parameters.count(None) != 1:
raise ValueError("Only one parameter is allowed to be specified")
idx_to_use = 0
if after_instruction is not None:
if isinstance(after_instruction, int):
idx_to_use = 1 + after_instruction
else:
assert isinstance(after_instruction, str)
inst_idx_list = self.__get_instruction_index(after_instruction)
if len(inst_idx_list) != 1:
raise IndexError("Did not find place to insert")
idx_to_use = 1 + inst_idx_list[0]
if before_instruction is not None:
if isinstance(before_instruction, int):
idx_to_use = before_instruction
else:
assert isinstance(before_instruction, str)
inst_idx_list = self.__get_instruction_index(before_instruction)
if len(inst_idx_list) != 1:
raise IndexError("Did not find place to insert")
idx_to_use = inst_idx_list[0]
self._instructions.insert(idx_to_use, new_instruction)
def remove_instruction(self, identifier: str = None, idx: int | typing.List[int] = None):
"""
if block_name is not None:
if idx is not None:
raise ValueError("Both block name and index are given")
to_return = [b for b in self._blocks if b.name == block_name]
if len(to_return) == 0:
raise IndexError("Block Not Found")
if len(to_return) > 1:
raise IndexError("Multiple Blocks Found")
self._blocks.remove(to_return[0])
Removes an instruction from the template.
Parameters:
identifier (str): The identifier of the instruction to remove.
idx (int | List[int]): The index or list of indices of the instruction(s) to remove.
"""
# assert only one param is not None
parameters = [identifier, idx]
if parameters.count(None) != 1:
raise ValueError("Only one parameter is allowed to be specified")
idxs_to_remove = []
if idx is not None:
if block_name is not None:
raise ValueError("Both block name and index are given")
del self._blocks[idx]
if isinstance(idx, int):
idxs_to_remove = [idx]
else:
idxs_to_remove = idx
if identifier is not None:
idxs_to_remove = self.__get_instruction_index(identifier)
raise ValueError("Neither Both block name nor index is given")
if len(idxs_to_remove) == 0:
# TODO
# may also be a silen No-Op?
raise ValueError("Nothing to remove")
self._instructions = [elem for idx, elem in enumerate(self._instructions) if idx not in idxs_to_remove]
def replace_block(self, new_block: InstructionBlock, block_name: str = None, idx: int = None):
def replace_instruction(self, new_instruction=Instruction, identifier: str = None,
idx: int | typing.List[int] = None):
"""
Removes the given Instruction Block Either by name or by index
Raises IndexError if the specified block is not found
Raises IndexError if multiple Blocks with the given name are found
Raises ValueError if Both a block name and index are given (or none is given)
Args:
new_block (InstructionBlock): The new Block to replace the old one (does not need to have the same name)
block_name (str): The name of the InstructionBlock to receive
idx (int): index of the InstructionBlock to retrieve
Replaces an instruction in the template with a new one.
Parameters:
new_instruction (Instruction | List[Instruction]): The new instruction(s) to replace with.
identifier (str): The identifier of the instruction to replace.
idx (int | List[int]): The index or list of indices of the instruction(s) to replace.
Raises
ValueError: if the number of instructions to replace does not match the number of instructions provided
or if both idx and identifier are provided
Notes: The instructions to be replaced must not be in contiguous order
"""
assert isinstance(new_block, InstructionBlock)
if block_name is not None:
if idx is not None:
raise ValueError("Both block name and index are given")
to_return = [b for b in self._blocks if b.name == block_name]
if len(to_return) == 0:
raise IndexError("Block Not Found")
if len(to_return) > 1:
raise IndexError("Multiple Blocks Found")
self._blocks[self._blocks.index(to_return[0])] = new_block
parameters = [identifier, idx]
if parameters.count(None) != 1:
raise ValueError("Only one parameter is allowed to be specified")
new_instruction_list = []
if isinstance(new_instruction, Instruction):
new_instruction_list = [new_instruction]
else:
new_instruction_list = new_instruction
idxs_to_replace = []
if idx is not None:
if block_name is not None:
raise ValueError("Both block name and index are given")
self._blocks[idx] = new_block
if isinstance(idx, int):
idxs_to_replace = [idx]
else:
idxs_to_replace = idx
if identifier is not None:
idxs_to_replace = self.__get_instruction_index(identifier)
raise ValueError("Neither Both block name nor index is given")
if len(idxs_to_replace) == len(new_instruction_list):
raise ValueError("Number of instructions to Replace does not match number of given instructions")
for (index, replacement) in zip(idxs_to_replace, new_instruction_list):
self._instructions[index] = replacement
......@@ -6,7 +6,7 @@ import typing
from scripts.Infrastructure.AllocCall import AllocCall
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICall import MPI_Call
from scripts.Infrastructure.MPICall import MPICall
from scripts.Infrastructure.MPICallFactory import CorrectMPICallFactory, MPICallFactory
from scripts.Infrastructure.Template import TemplateManager
......@@ -24,7 +24,7 @@ def get_default_template(mpi_func):
def get_send_recv_template(send_func: str = "mpi_isend", recv_func: str | typing.Tuple[str, str] = "mpi_irecv"):
"""
Contructs a default template for the given mpi send recv function pair
Contructs a default template for the given mpi send recv function pair it contains a send from rank 1 to rank 0
Returns:
TemplateManager Initialized with a default template
The function is contained in a block named MPICALL with seperate calls for rank 1 and 2)
......@@ -142,22 +142,20 @@ def get_send_recv_template(send_func: str = "mpi_isend", recv_func: str | typing
tm.register_instruction_block(b)
# wait for op to complete
if send_func in isend_funcs + persistent_send_funcs:
b = InstructionBlock("WAIT")
b.register_instruction(CorrectMPICallFactory().mpi_wait(), 1)
tm.register_instruction_block(b)
wait_block = InstructionBlock("WAIT")
if send_func in isend_funcs + persistent_send_funcs:
wait_block.register_instruction(CorrectMPICallFactory().mpi_wait(), 1)
if recv_func in irecv_funcs + persistent_recv_funcs + [["mpi_mprobe", "mpi_imrecv"],
["mpi_improbe", "mpi_imrecv"]]:
b = InstructionBlock("WAIT")
b.register_instruction(CorrectMPICallFactory().mpi_wait(), 0)
tm.register_instruction_block(b)
wait_block.register_instruction(CorrectMPICallFactory().mpi_wait(), 0)
tm.register_instruction_block(wait_block) # if no waits: block is empty
# end MPI operation
# cleanup
free_block = InstructionBlock("buf_free")
if send_func in ["mpi_bsend", "mpi_ibsend", "mpi_bsend_init"]:
free_block = InstructionBlock("buf_detach")
free_block.register_instruction("int freed_size;")
free_block.register_instruction(MPICallFactory().mpi_buffer_detach("mpi_buf", "&freed_size"))
free_block.register_instruction("free(mpi_buf);")
......
#! /usr/bin/python3
# The 'Magic' Comment denoting where the Error is in a testcase
ERROR_MARKER_COMMENT = "/*MBBERROR*/"
# The 'Magic' Comments denoting where the Error is in a testcase
ERROR_MARKER_COMMENT_BEGIN = "/*MBBERROR_BEGIN*/"
ERROR_MARKER_COMMENT_END = "/*MBBERROR_END*/"
# The List of Features considered
featurelist = ["P2P", "COLL", "RMA", "TOOL"]
......
#! /usr/bin/python3
from scripts.Infrastructure.AllocCall import AllocCall
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
from itertools import chain
class InvalidBufErrorP2P(ErrorGenerator):
invalid_bufs = [CorrectParameterFactory().buf_var_name, "NULL"]
send_funcs = ["mpi_send",
"mpi_isend", "mpi_ssend", "mpi_issend", "mpi_rsend", "mpi_irsend", "mpi_bsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init",
]
recv_funcs = ["mpi_recv", "mpi_irecv", "mpi_recv_init", "mpi_precv_init"]
sendrecv_funcs = ["mpi_sendrecv", "mpi_sendrecv_replace"]
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate_impl(self, send_func, recv_func, check_receive):
for buf_to_use in self.invalid_bufs:
tm = get_send_recv_template(send_func, recv_func)
if buf_to_use == CorrectParameterFactory().buf_var_name:
for i, inst in enumerate(tm.get_block("alloc").get_instruction(kind='all', index='all')):
if isinstance(inst, AllocCall):
name = inst.get_name()
if name == "recv_buf":
tm.get_block("alloc").replace_instruction(
CorrectParameterFactory.dtype[0] + "* " + name + ";", kind='all', index=i)
# only one such instruction present
break
else:
tm.get_block("alloc").replace_instruction(
CorrectParameterFactory.dtype[0] + "* " + CorrectParameterFactory.buf_var_name + ";")
# without allocation: usage is undefined behaviour
if check_receive:
tm.set_description("InvalidParam-Buffer-" + recv_func, "Invalid Buffer: %s" % buf_to_use)
else:
tm.set_description("InvalidParam-Buffer-" + send_func, "Invalid Buffer: %s" % buf_to_use)
if check_receive:
if tm.get_block("MPICALL").get_instruction(kind=0, index=0).has_arg("buf"):
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_arg("buf", buf_to_use)
else:
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_arg("recvbuf", buf_to_use)
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_has_error()
else:
if tm.get_block("MPICALL").get_instruction(kind=1, index=0).has_arg("buf"):
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_arg("buf", buf_to_use)
else:
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_arg("recvbuf", buf_to_use)
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_has_error()
yield tm
def generate(self, generate_full_set):
for func in self.send_funcs:
yield from self.generate_impl(func, 'mpi_irecv', False)
if not generate_full_set:
return
for func in self.recv_funcs:
yield from self.generate_impl("mpi_send", func, True)
yield from self.generate_impl("mpi_sendrecv", "mpi_sendrecv", True)
yield from self.generate_impl("mpi_sendrecv", "mpi_sendrecv", False)
yield from self.generate_impl("mpi_sendrecv_replace", "mpi_sendrecv_replace", True)
class MessageRaceErrorSendRecv(ErrorGenerator):
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate(self, generate_full_set):
for buf_to_use in ["buf", "MPI_IN_PLACE"]:
tm = get_send_recv_template("mpi_sendrecv", "mpi_sendrecv")
tm.set_description("InvalidParam-Buffer-mpi_sendrecv", "send and recv buffer must be disjoint in sendrecv")
for k in [0, 1]:
tm.get_block("MPICALL").get_instruction(kind=k, index=0).set_arg("recvbuf", buf_to_use)
tm.get_block("MPICALL").get_instruction(kind=k, index=0).set_has_error()
yield tm
#! /usr/bin/python3
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICall import MPICall
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template, get_communicator, get_intercomm
from itertools import chain
sendrecv_funcs = ["mpi_sendrecv", "mpi_sendrecv_replace"]
class InvalidCommErrorP2P(ErrorGenerator):
invalid_comm = ["MPI_COMM_NULL", "NULL"]
missmatching_comms = ["MPI_COMM_SELF", "mpi_comm_dup", "mpi_comm_dup_with_info", "mpi_comm_idup",
"mpi_comm_idup_with_info", "mpi_comm_create", "mpi_comm_create_group", "mpi_comm_split",
"mpi_comm_split_type", "mpi_comm_create_from_group"
]
intercomms = ["mpi_intercomm_create", "mpi_intercomm_merge", "mpi_intercomm_create_from_groups"]
# as extended testcases
comms_to_check = invalid_comm + missmatching_comms + intercomms
mprobe_funcs = ["mpi_mprobe", "mpi_improbe"]
probe_funcs = ["mpi_probe", "mpi_iprobe"]
functions_to_check = ["mpi_send",
"mpi_recv", "mpi_irecv",
"mpi_isend", "mpi_ssend", "mpi_issend", "mpi_rsend", "mpi_irsend", "mpi_bsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init",
"mpi_precv_init", "mpi_recv_init"
] + sendrecv_funcs + mprobe_funcs + probe_funcs
recv_funcs = ["mpi_recv", "mpi_irecv", "mpi_recv_init",
"mpi_precv_init"] + sendrecv_funcs + mprobe_funcs + probe_funcs
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate(self, generate_full_set):
# TODO one may want to refactor it for better readability
for send_func in self.functions_to_check:
send_func_to_use = send_func
for comm_to_use in self.comms_to_check:
check_receive = False
recv_func = "mpi_irecv"
if send_func in self.recv_funcs:
check_receive = True
recv_func = send_func
send_func_to_use = "mpi_send"
if recv_func in sendrecv_funcs:
send_func_to_use = recv_func
recv_func_to_use = recv_func
if recv_func in self.mprobe_funcs:
recv_func_to_use = [recv_func, "mpi_mrecv"]
if recv_func in self.probe_funcs:
recv_func_to_use = "mpi_recv"
tm = get_send_recv_template(send_func_to_use, recv_func_to_use)
if comm_to_use in self.missmatching_comms and comm_to_use != "MPI_COMM_SELF":
b = get_communicator(comm_to_use, comm_to_use)
tm.insert_block(b, after_block_name="alloc")
if comm_to_use in self.intercomms:
b = get_intercomm(comm_to_use, comm_to_use)
tm.insert_block(b, after_block_name="alloc")
error_string = "ParamMatching"
if comm_to_use in self.invalid_comm:
error_string = "InvalidParam"
if check_receive:
if comm_to_use in self.missmatching_comms + self.intercomms and recv_func == "mpi_irecv":
# combination repeated
continue
tm.set_description(error_string + "-Comm-" + recv_func, error_string + ": %s" % comm_to_use)
else:
tm.set_description(error_string + "-Comm-" + send_func, error_string + ": %s" % comm_to_use)
# add an additional probe call
if send_func == "mpi_probe":
tm.get_block("MPICALL").insert_instruction(CorrectMPICallFactory.get(send_func), kind=0,
before_index=0)
if send_func == "mpi_iprobe":
tm.get_block("MPICALL").insert_instruction("int flag=0;", kind=0,
before_index=0)
tm.get_block("MPICALL").insert_instruction("while (!flag){", kind=0,
before_index=1)
tm.get_block("MPICALL").insert_instruction(CorrectMPICallFactory.get(send_func), kind=0,
before_index=2)
tm.get_block("MPICALL").insert_instruction("}", kind=0,
before_index=3) # end while
kind = 1
if check_receive:
kind = 0
idx = 0
if recv_func == "mpi_improbe":
idx = 1
if recv_func == "mpi_iprobe":
idx = 2
tm.get_block("MPICALL").get_instruction(kind=kind, index=idx).set_arg("comm", comm_to_use)
tm.get_block("MPICALL").get_instruction(kind=kind, index=idx).set_has_error()
if comm_to_use in self.missmatching_comms + self.intercomms:
# missmatch is between both
tm.get_block("MPICALL").get_instruction(kind=((kind + 1) % 2), index=0).set_has_error()
# an intercomm has only one rank (the other group)
# so all rank values must be set to 0
if comm_to_use in self.intercomms and not comm_to_use == "mpi_intercomm_merge":
# intercomm merge results in an "equivalent" comm again
for inst in tm.get_block("MPICALL").get_instruction(kind=0, index='all'):
if isinstance(inst, MPICall):
if inst.has_arg("source"):
inst.set_arg("source", "0")
for inst in tm.get_block("MPICALL").get_instruction(kind=1, index='all'):
if isinstance(inst, MPICall):
if inst.has_arg("source"):
inst.set_arg("source", "0")
if comm_to_use in self.missmatching_comms + self.intercomms and comm_to_use != "MPI_COMM_SELF":
b = InstructionBlock("comm_free")
b.register_instruction(MPICallFactory().mpi_comm_free("&" + comm_to_use))
tm.register_instruction_block(b)
yield tm
# end for comm to check
if not generate_full_set:
return
# end for send_func in funcs_to_check
#! /usr/bin/python3
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
from itertools import chain
class InvalidRankErrorP2P(ErrorGenerator):
invalid_ranks = ["-1", "nprocs", "MPI_PROC_NULL"]
send_funcs = ["mpi_send",
"mpi_isend", "mpi_ssend", "mpi_issend", "mpi_rsend", "mpi_irsend", "mpi_bsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init"
]
recv_funcs = ["mpi_recv", "mpi_irecv", "mpi_recv_init", "mpi_precv_init"]
probe_recv_funcs = ["mpi_mprobe", "mpi_improbe"]
sendrecv_funcs = ["mpi_sendrecv", "mpi_sendrecv_replace"]
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate_impl(self, send_func, recv_func, check_receive):
for rank_to_use in self.invalid_ranks:
tm = get_send_recv_template(send_func, recv_func)
if check_receive:
tm.set_description("InvalidParam-Rank-" + recv_func, "Invalid Rank: %s" % rank_to_use)
else:
tm.set_description("InvalidParam-Rank-" + send_func, "Invalid Rank: %s" % rank_to_use)
if check_receive:
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_arg("source", rank_to_use)
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_has_error()
else:
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_arg("dest", rank_to_use)
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_has_error()
yield tm
def generate_mprobe(self, send_func, recv_func):
for rank_to_use in self.invalid_ranks:
tm = get_send_recv_template(send_func, [recv_func, "mpi_mrecv"])
tm.set_description("InvalidParam-Rank-" + recv_func, "Invalid Rank: %s" % rank_to_use)
index = 0
if recv_func == "mpi_improbe":
index = 1
tm.get_block("MPICALL").get_instruction(kind=0, index=index).set_arg("source", rank_to_use)
tm.get_block("MPICALL").get_instruction(kind=0, index=index).set_has_error()
yield tm
def generate_probe(self, probe_to_use):
for rank_to_use in self.invalid_ranks:
tm = get_send_recv_template("mpi_send", "mpi_recv")
tm.set_description("InvalidParam-Rank-" + probe_to_use, "Invalid Rank: %s" % rank_to_use)
probe_call = CorrectMPICallFactory.get(probe_to_use)
probe_call.set_arg("source", rank_to_use)
probe_call.set_has_error()
if probe_to_use == "mpi_iprobe":
tm.get_block("MPICALL").insert_instruction("int flag=0;", kind=0, before_index=0)
tm.get_block("MPICALL").insert_instruction("while (!flag){", kind=0, before_index=1)
tm.get_block("MPICALL").insert_instruction(probe_call, kind=0, before_index=2)
tm.get_block("MPICALL").insert_instruction("}", kind=0, before_index=3) # end while
else:
tm.get_block("MPICALL").insert_instruction(probe_call, kind=0, before_index=0)
yield tm
def generate(self, generate_full_set):
for send_func in self.send_funcs:
yield from self.generate_impl(send_func, "mpi_irecv", False)
if not generate_full_set:
return
for func in self.recv_funcs:
yield from self.generate_impl("mpi_send", func, True)
for func in self.sendrecv_funcs:
yield from self.generate_impl(func, func, True)
yield from self.generate_impl(func, func, False)
for func in self.probe_recv_funcs:
yield from self.generate_mprobe("mpi_send", func)
for func in ["mpi_probe", "mpi_iprobe"]:
yield from self.generate_probe(func)
#! /usr/bin/python3
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
from itertools import chain
class InvalidRequestErrorP2P(ErrorGenerator):
invalid_requests = ["MPI_REQUEST_NULL", # probably triggers compiler warning
# "&MPI_REQUEST_NULL" #TODO test this as well?? depending on MPI implementation: may be compiler error
"NULL"]
functions_to_check = ["mpi_irecv", "mpi_isend",
"mpi_issend", "mpi_irsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init",
"mpi_precv_init", "mpi_recv_init", "mpi_imrecv"
]
recv_funcs = ["mpi_irecv", "mpi_recv_init", "mpi_precv_init", "mpi_imrecv"]
def __init__(self):
pass
def get_num_errors(self):
# send + receive = only check the first two functions
return len(self.invalid_requests) * 2
# the number of errors to produce in the extended mode (all possible combinations)
def get_num_errors_extended(self):
return len(self.invalid_requests) * len(self.functions_to_check)
def get_feature(self):
return ["P2P"]
def generate(self, generate_full_set):
for send_func in self.functions_to_check:
for req_to_use in self.invalid_requests:
check_receive = False
recv_func = "mpi_irecv"
if send_func in self.recv_funcs:
check_receive = True
recv_func = send_func
send_func = "mpi_isend"
recv_func_to_use = recv_func
if recv_func == "mpi_imrecv":
recv_func_to_use = ["mpi_mprobe", "mpi_imrecv"]
tm = get_send_recv_template(send_func, recv_func_to_use)
if check_receive:
tm.set_description("InvalidParam-Request-" + recv_func, "Invalid Request: %s" % req_to_use)
else:
tm.set_description("InvalidParam-Request-" + send_func, "Invalid Request: %s" % req_to_use)
kind = 1
if check_receive:
kind = 0
idx = 0
if recv_func == "mpi_imrecv":
idx = 1
tm.get_block("MPICALL").get_instruction(kind=kind, index=idx).set_arg("request", req_to_use)
tm.get_block("MPICALL").get_instruction(kind=kind, index=idx).set_has_error()
yield tm
if not generate_full_set:
return
#! /usr/bin/python3
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
from itertools import chain
class InvalidTagErrorP2P(ErrorGenerator):
invalid_tags = ["-1", "MPI_TAG_UB+1", CorrectParameterFactory.tag * 2, "MPI_ANY_TAG"]
send_funcs = ["mpi_send",
"mpi_isend", "mpi_ssend", "mpi_issend", "mpi_rsend", "mpi_irsend", "mpi_bsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init",
]
recv_funcs = ["mpi_recv", "mpi_irecv", "mpi_recv_init", "mpi_precv_init"]
sendrecv_funcs = ["mpi_sendrecv", "mpi_sendrecv_replace"]
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate_impl(self, send_func, recv_func, check_receive):
for tag_to_use in self.invalid_tags:
tm = get_send_recv_template(send_func, recv_func)
error_string = "InvalidParam"
if tag_to_use == CorrectParameterFactory.tag * 2:
error_string = "ParamMatching"
if check_receive:
if tag_to_use == "MPI_ANY_TAG":
# correct case
continue
if tag_to_use == CorrectParameterFactory().tag * 2 and recv_func == "mpi_irecv":
# combination repeated
continue
tm.set_description(error_string + "-Tag-" + recv_func, "Invalid Rank: %s" % tag_to_use)
else:
tm.set_description(error_string + "-Tag-" + send_func, "Invalid Rank: %s" % tag_to_use)
if check_receive:
if tm.get_block("MPICALL").get_instruction(kind=0, index=0).has_arg("tag"):
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_arg("tag", tag_to_use)
else:
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_arg("recvtag", tag_to_use)
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_has_error()
if tag_to_use == CorrectParameterFactory.tag * 2:
# missmatch is between both
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_has_error()
else:
if tm.get_block("MPICALL").get_instruction(kind=1, index=0).has_arg("tag"):
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_arg("tag", tag_to_use)
else:
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_arg("sendtag", tag_to_use)
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_has_error()
if tag_to_use == CorrectParameterFactory.tag * 2:
# missmatch is between both
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_has_error()
yield tm
def generate_mprobe(self, probe_to_use):
for tag_to_use in self.invalid_tags:
if tag_to_use == "MPI_ANY_TAG":
# correct case
continue
error_string = "InvalidParam"
if tag_to_use == CorrectParameterFactory.tag * 2:
error_string = "ParamMatching"
tm = get_send_recv_template("mpi_send", [probe_to_use, "mpi_mrecv"])
tm.set_description(error_string + "-Tag-" + probe_to_use, "Invalid Rank: %s" % tag_to_use)
index = 0
if probe_to_use == "mpi_improbe":
index = 1
tm.get_block("MPICALL").get_instruction(kind=0, index=index).set_arg("tag", tag_to_use)
tm.get_block("MPICALL").get_instruction(kind=0, index=index).set_has_error()
yield tm
def generate_probe(self, probe_to_use):
for tag_to_use in self.invalid_tags:
if tag_to_use == "MPI_ANY_TAG":
# correct case
continue
error_string = "InvalidParam"
if tag_to_use == CorrectParameterFactory.tag * 2:
error_string = "ParamMatching"
tm = get_send_recv_template("mpi_send", "mpi_recv")
tm.set_description(error_string + "-Tag-" + probe_to_use, "Invalid Rank: %s" % tag_to_use)
probe_call = CorrectMPICallFactory.get(probe_to_use)
probe_call.set_arg("tag", tag_to_use)
probe_call.set_has_error()
if probe_to_use == "mpi_iprobe":
tm.get_block("MPICALL").insert_instruction("int flag=0;", kind=0, before_index=0)
tm.get_block("MPICALL").insert_instruction("while (!flag){", kind=0, before_index=1)
tm.get_block("MPICALL").insert_instruction(probe_call, kind=0, before_index=2)
tm.get_block("MPICALL").insert_instruction("}", kind=0, before_index=3) # end while
else:
tm.get_block("MPICALL").insert_instruction(probe_call, kind=0, before_index=0)
yield tm
def generate(self, generate_full_set):
for send_func in self.send_funcs:
yield from self.generate_impl(send_func, "mpi_irecv", False)
if not generate_full_set:
return
for recv_func in self.recv_funcs:
yield from self.generate_impl("mpi_send", recv_func, True)
for func in self.sendrecv_funcs:
yield from self.generate_impl(func, func, True)
yield from self.generate_impl(func, func, False)
yield from self.generate_mprobe("mpi_mprobe")
yield from self.generate_mprobe("mpi_improbe")
yield from self.generate_probe("mpi_probe")
yield from self.generate_probe("mpi_iprobe")
#! /usr/bin/python3
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.Instruction import Instruction
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
sendrecv_funcs = ["mpi_sendrecv", "mpi_sendrecv_replace"]
class LocalConcurrencyErrorP2P(ErrorGenerator):
functions_to_check = ["mpi_irecv",
"mpi_isend", "mpi_issend", "mpi_irsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init",
"mpi_precv_init", "mpi_recv_init", "mpi_imrecv"
]
recv_funcs = ["mpi_irecv", "mpi_recv_init", "mpi_precv_init", "mpi_imrecv"]
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate(self, generate_full_set):
for send_func in self.functions_to_check:
check_receive = False
recv_func = "mpi_irecv"
if send_func in self.recv_funcs:
check_receive = True
recv_func = send_func
send_func = "mpi_isend"
recv_func_to_use = recv_func
if recv_func == "mpi_imrecv":
recv_func_to_use = ["mpi_mprobe", "mpi_imrecv"]
tm = get_send_recv_template(send_func, recv_func_to_use)
if check_receive:
tm.set_description("LocalConcurrency-receive-" + recv_func,
"usage of receive buffer before operation is completed")
else:
tm.set_description("LocalConcurrency-send-" + send_func,
"usage of send buffer before operation is completed")
conflicting_inst = Instruction("buf[2]=1;")
conflicting_inst.set_has_error()
kind = 1
if check_receive:
kind = 0
idx = 0
if recv_func == "mpi_imrecv":
idx = 1
tm.get_block("MPICALL").get_instruction(kind=kind, index=idx).set_has_error()
tm.get_block("MPICALL").register_instruction(conflicting_inst, kind=kind)
yield tm
if not generate_full_set:
return
#! /usr/bin/python3
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
from itertools import chain
class MessageRaceErrorAnyTag(ErrorGenerator):
# TODO do we need to generate it for all combinations of send and recv?
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate(self, generate_full_set):
tm = TemplateManager()
tm.set_description("MsgRace-ANY_TAG", "order of messages is indeterministic, may lead to a deadlock")
b = InstructionBlock("alloc")
b.register_instruction(CorrectParameterFactory().get_buffer_alloc())
tm.register_instruction_block(b)
b = InstructionBlock("MPICALL")
# send part
b.register_instruction("for(int i =0; i < 10; ++i) {", kind=1)
b.register_instruction("buf[0]=i;", kind=1)
send_call = CorrectMPICallFactory().mpi_send()
send_call.set_arg("tag", "i")
b.register_instruction(send_call, kind=1)
b.register_instruction("}", kind=1)
# recv part
b.register_instruction("for(int i =0; i < 10; ++i) {", kind=0)
recv_call = CorrectMPICallFactory().mpi_recv()
recv_call.set_arg("tag", "MPI_ANY_TAG")
b.register_instruction(recv_call, kind=0)
b.register_instruction("if(buf[0]!=i){", kind=0)
additional_recv = CorrectMPICallFactory().mpi_recv()
additional_recv.set_has_error() # additional recv leads to deadlock
b.register_instruction(additional_recv, kind=0)
b.register_instruction(" }", kind=0) # end if
b.register_instruction("}", kind=0) # end for
tm.register_instruction_block(b)
b = InstructionBlock("free")
b.register_instruction(CorrectParameterFactory().get_buffer_free())
tm.register_instruction_block(b)
yield tm
class MessageRaceErrorAnysource(ErrorGenerator):
# TODO do we need to generate it for all combinations of send and recv?
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate(self, generate_full_set):
tm = TemplateManager(min_ranks=3)
tm.set_description("MsgRace-ANY_SOURCE", "order of messages is indeterministic, may lead to a deadlock")
b = InstructionBlock("alloc")
b.register_instruction(CorrectParameterFactory().get_buffer_alloc())
tm.register_instruction_block(b)
b = InstructionBlock("MPICALL")
# send part
b.register_instruction("buf[0]=rank;", kind='not0')
send_call = CorrectMPICallFactory().mpi_send()
b.register_instruction(send_call, kind='not0')
# recv part
b.register_instruction("for(int i =1; i < nprocs; ++i) {", kind=0)
recv_call = CorrectMPICallFactory().mpi_recv()
recv_call.set_arg("source", "MPI_ANY_SOURCE")
b.register_instruction(recv_call, kind=0)
b.register_instruction("if(buf[0]!=i){", kind=0)
additional_recv = CorrectMPICallFactory().mpi_recv()
additional_recv.set_has_error() # additional recv leads to deadlock
b.register_instruction(additional_recv, kind=0)
b.register_instruction(" }", kind=0) # end if
b.register_instruction("}", kind=0) # end for
tm.register_instruction_block(b)
b = InstructionBlock("free")
b.register_instruction(CorrectParameterFactory().get_buffer_free())
tm.register_instruction_block(b)
yield tm
#! /usr/bin/python3
import itertools
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
from itertools import chain
class RecvBeforeSend(ErrorGenerator):
send_funcs = ["mpi_send",
"mpi_isend", "mpi_ssend", "mpi_issend", "mpi_rsend", "mpi_irsend", "mpi_bsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init"
]
recv_funcs = ["mpi_recv", "mpi_irecv", "mpi_recv_init", "mpi_precv_init"]
probe_recv_funcs = ["mpi_mprobe", "mpi_improbe"]
probe_funcs = ["mpi_probe", "mpi_iprobe"]
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def get_reverse_msg_block(self):
reverse_msg_block = InstructionBlock("second_msg")
send = CorrectMPICallFactory.mpi_send()
send.set_arg("dest", "1")
reverse_msg_block.register_instruction(send, 0)
recv = CorrectMPICallFactory.mpi_recv()
recv.set_arg("source", "0")
reverse_msg_block.register_instruction(recv, 1)
return reverse_msg_block
def generate_impl(self, send_func, recv_func):
tm = get_send_recv_template(send_func, recv_func)
tm.set_description("CallOrdering-" + send_func + "-" + recv_func,
"Call Ordering: both ranks try to receive befroe sending")
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_has_error()
reverse_msg_block = self.get_reverse_msg_block()
# this is a correct case with no deadlock: tm.insert_block(reverse_msg_block, after_block_name="MPICALL")
# introduce deadlock by moving rank 1s send after the recv
rank_1_send_instr = tm.get_block("MPICALL").get_instruction(kind=1, index='all')
tm.get_block("MPICALL").remove_instruction(kind=1, index='all')
wait_calls = tm.get_block("WAIT").get_instruction(kind=1, index='all')
tm.get_block("WAIT").remove_instruction(kind=1, index='all')
reverse_msg_block.register_instruction(rank_1_send_instr, kind=1)
reverse_msg_block.register_instruction(wait_calls, kind=1)
tm.insert_block(reverse_msg_block, before_block_name="buf_free")
yield tm
def generate_probe(self, probe_to_use):
tm = get_send_recv_template("mpi_send", "mpi_recv")
tm.set_description("CallOrdering-" + probe_to_use + "-" + "mpi_send",
"Call Ordering: probe for message before it is going to be send")
reverse_msg_block = self.get_reverse_msg_block()
tm.insert_block(reverse_msg_block, before_block_name="buf_free")
# add the probe to rank 1 before the send
probecall = CorrectMPICallFactory.get(probe_to_use)
probecall.set_arg("source", "0") # recv from 1 is the default
probecall.set_has_error()
if probe_to_use == "mpi_iprobe":
tm.get_block("MPICALL").insert_instruction("int flag=0;", kind=1, before_index=0)
tm.get_block("MPICALL").insert_instruction("while (!flag){", kind=1, before_index=1)
tm.get_block("MPICALL").insert_instruction(probecall, kind=1, before_index=2)
tm.get_block("MPICALL").insert_instruction("}", kind=1, before_index=3)
else:
tm.get_block("MPICALL").insert_instruction(probecall, kind=1, before_index=0)
yield tm
def generate_mprobe(self, probe_to_use):
tm = get_send_recv_template("mpi_send", "mpi_recv")
tm.get_block("alloc").register_instruction("MPI_Message msg;")
tm.set_description("CallOrdering-" + probe_to_use + "-" + "mpi_send",
"Call Ordering: probe for message before it is going to be send")
reverse_msg_block = self.get_reverse_msg_block()
tm.insert_block(reverse_msg_block, before_block_name="buf_free")
# use mrecv instead of recv
reverse_msg_block.replace_instruction(CorrectMPICallFactory.mpi_mrecv(), kind=1, index=0)
# add the probe to rank 1 before the send
probecall = CorrectMPICallFactory.get(probe_to_use)
probecall.set_arg("source", "0") # recv from 1 is the default
probecall.set_has_error()
if probe_to_use == "mpi_improbe":
tm.get_block("MPICALL").insert_instruction("int flag=0;", kind=1, before_index=0)
tm.get_block("MPICALL").insert_instruction("while (!flag){", kind=1, before_index=1)
tm.get_block("MPICALL").insert_instruction(probecall, kind=1, before_index=2)
tm.get_block("MPICALL").insert_instruction("}", kind=1, before_index=3)
else:
tm.get_block("MPICALL").insert_instruction(probecall, kind=1, before_index=0)
yield tm
def generate(self, generate_full_set):
for send_func, recv_func in itertools.product(self.send_funcs, self.recv_funcs):
yield from self.generate_impl(send_func, "mpi_irecv")
if not generate_full_set:
return
yield from self.generate_probe("mpi_probe")
yield from self.generate_probe("mpi_iprobe")
yield from self.generate_mprobe("mpi_mprobe")
yield from self.generate_mprobe("mpi_improbe")
#! /usr/bin/python3
import itertools
from scripts.Infrastructure.ErrorGenerator import ErrorGenerator
from scripts.Infrastructure.InstructionBlock import InstructionBlock
from scripts.Infrastructure.MPICallFactory import MPICallFactory, CorrectMPICallFactory
from scripts.Infrastructure.CorrectParameter import CorrectParameterFactory, get_matching_recv
from scripts.Infrastructure.Template import TemplateManager
from scripts.Infrastructure.TemplateFactory import get_send_recv_template
from itertools import chain
class UnmatchedP2Pcall(ErrorGenerator):
send_funcs = ["mpi_send",
"mpi_isend", "mpi_ssend", "mpi_issend", "mpi_rsend", "mpi_irsend", "mpi_bsend", "mpi_ibsend",
"mpi_send_init", "mpi_ssend_init", "mpi_bsend_init", "mpi_rsend_init", "mpi_psend_init"
]
recv_funcs = ["mpi_recv", "mpi_irecv", "mpi_recv_init", "mpi_precv_init"]
probe_recv_funcs = ["mpi_mprobe", "mpi_improbe"]
def __init__(self):
pass
def get_feature(self):
return ["P2P"]
def generate_impl(self, func_to_check, check_receive):
send_func = "mpi_send"
recv_func = "mpi_irecv"
if check_receive:
recv_func = func_to_check
if func_to_check in self.probe_recv_funcs:
recv_func = [func_to_check, "mpi_mrecv"]
else:
send_func = func_to_check
tm = get_send_recv_template(send_func, recv_func)
tm.set_description("CallOrdering-unmatched-" + func_to_check,
"The %s does not have a counterpart" % ("receive" if check_receive else "send"))
if func_to_check == "mpi_improbe":
tm.get_block("MPICALL").get_instruction(kind=0, index=1).set_has_error()
else:
tm.get_block("MPICALL").get_instruction(kind=0, index=0).set_has_error()
tm.get_block("MPICALL").get_instruction(kind=1, index=0).set_has_error()
# remove send/recv COUNTER-part
kind = 0
if check_receive:
kind = 1
tm.get_block("MPICALL").remove_instruction(kind, 'all')
tm.get_block("WAIT").remove_instruction(kind, 'all')
yield tm
def generate(self, generate_full_set):
for func in self.recv_funcs + self.probe_recv_funcs:
yield from self.generate_impl(func, True)
if not generate_full_set:
return
for func in self.send_funcs:
yield from self.generate_impl(func, False)