diff --git a/openmp/README.rst b/openmp/README.rst index 2dfc8630858b8..c34d3e8a40d7d 100644 --- a/openmp/README.rst +++ b/openmp/README.rst @@ -369,6 +369,7 @@ There are following check-* make targets for tests. - ``check-ompt`` (ompt tests under runtime/test/ompt) - ``check-ompt-multiplex`` (ompt multiplex tests under tools/multiplex/tests) +- ``check-ompt-omptest`` (ompt omptest tests under tools/omptest/tests) - ``check-libarcher`` (libarcher tests under tools/archer/tests) - ``check-libomp`` (libomp tests under runtime/test. This includes check-ompt tests too) - ``check-libomptarget-*`` (libomptarget tests for specific target under libomptarget/test) diff --git a/openmp/tools/omptest/CMakeLists.txt b/openmp/tools/omptest/CMakeLists.txt new file mode 100644 index 0000000000000..19f9f898f4300 --- /dev/null +++ b/openmp/tools/omptest/CMakeLists.txt @@ -0,0 +1,116 @@ +##===----------------------------------------------------------------------===## +# +# Build OMPT unit testing library: ompTest +# +##===----------------------------------------------------------------------===## + +cmake_minimum_required(VERSION 3.22) +project(omptest LANGUAGES CXX) + +option(LIBOMPTEST_BUILD_STANDALONE + "Build ompTest 'standalone', i.e. w/o GoogleTest." OFF) +option(LIBOMPTEST_BUILD_UNITTESTS + "Build ompTest's unit tests , requires GoogleTest." OFF) + +# In absence of corresponding OMPT support: exit early +if(NOT ${LIBOMPTARGET_OMPT_SUPPORT}) + return() +endif() + +set(OMPTEST_HEADERS + ./include/AssertMacros.h + ./include/InternalEvent.h + ./include/InternalEventCommon.h + ./include/Logging.h + ./include/OmptAliases.h + ./include/OmptAsserter.h + ./include/OmptAssertEvent.h + ./include/OmptCallbackHandler.h + ./include/OmptTester.h + ./include/OmptTesterGlobals.h +) + +add_library(omptest + SHARED + + ${OMPTEST_HEADERS} + ./src/InternalEvent.cpp + ./src/InternalEventOperators.cpp + ./src/Logging.cpp + ./src/OmptAsserter.cpp + ./src/OmptAssertEvent.cpp + ./src/OmptCallbackHandler.cpp + ./src/OmptTester.cpp +) + +# Target: ompTest library +# On (implicit) request of GoogleTest, link against the one provided with LLVM. +if ((NOT LIBOMPTEST_BUILD_STANDALONE) OR LIBOMPTEST_BUILD_UNITTESTS) + # Check if standalone build was requested together with unittests + if (LIBOMPTEST_BUILD_STANDALONE) + # Emit warning: this build actually depends on LLVM's GoogleTest + message(WARNING "LIBOMPTEST_BUILD_STANDALONE and LIBOMPTEST_BUILD_UNITTESTS" + " requested simultaneously.\n" + "Linking against LLVM's GoogleTest library archives.\n" + "Disable LIBOMPTEST_BUILD_UNITTESTS to perform an actual" + " standalone build.") + # Explicitly disable LIBOMPTEST_BUILD_STANDALONE + set(LIBOMPTEST_BUILD_STANDALONE OFF) + endif() + + # Use LLVM's gtest library archive + set(GTEST_LIB "${LLVM_BINARY_DIR}/lib/libllvm_gtest.a") + # Link gtest as whole-archive to expose required symbols + set(GTEST_LINK_CMD "-Wl,--whole-archive" ${GTEST_LIB} + "-Wl,--no-whole-archive" LLVMSupport) + + # Add GoogleTest-based header + target_sources(omptest PRIVATE ./include/OmptTesterGoogleTest.h) + + # Add LLVM-provided GoogleTest include directories. + target_include_directories(omptest PRIVATE + ${LLVM_THIRD_PARTY_DIR}/unittest/googletest/include) + + # TODO: Re-visit ABI breaking checks, disable for now. + target_compile_definitions(omptest PUBLIC + -DLLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING) + + # Link against gtest and gtest_main + target_link_libraries(omptest PRIVATE ${GTEST_LINK_CMD}) +else() + # Add 'standalone' compile definitions + target_compile_definitions(omptest PRIVATE + -DOPENMP_LIBOMPTEST_BUILD_STANDALONE) + + # Add 'standalone' source files + target_sources(omptest PRIVATE + ./include/OmptTesterStandalone.h + ./src/OmptTesterStandalone.cpp) +endif() + +# Add common include directories. +target_include_directories(omptest PRIVATE + ./include + ${LIBOMPTARGET_INCLUDE_DIR}) +target_compile_features(omptest PRIVATE cxx_std_17) + +# Create and install package configuration files. +configure_file( + ${omptest_SOURCE_DIR}/cmake/omptest-config.cmake.in + ${omptest_BINARY_DIR}/cmake/omptest-config.cmake @ONLY) + +install(FILES ${omptest_BINARY_DIR}/cmake/omptest-config.cmake + DESTINATION "${OPENMP_INSTALL_LIBDIR}/cmake/openmp/omptest") + +# Install libomptest header files: Copy header-files from include dir +install(DIRECTORY ./include + DESTINATION "${LIBOMP_HEADERS_INSTALL_PATH}/omptest" + FILES_MATCHING PATTERN "*.h") + +install(TARGETS omptest LIBRARY COMPONENT omptest + DESTINATION "${OPENMP_INSTALL_LIBDIR}") + +# Discover unit tests (added to check-openmp) +if(LIBOMPTEST_BUILD_UNITTESTS) + add_subdirectory(test) +endif() diff --git a/openmp/tools/omptest/README.md b/openmp/tools/omptest/README.md new file mode 100644 index 0000000000000..bfed871b59bdb --- /dev/null +++ b/openmp/tools/omptest/README.md @@ -0,0 +1,279 @@ + +README for the OpenMP Tooling Interface Testing Library (ompTest) +================================================================= + +# Introduction +OpenMP Tooling Interface Testing Library (ompTest) +ompTest is a unit testing framework for testing OpenMP implementations. +It offers a simple-to-use framework that allows a tester to check for OMPT +events in addition to regular unit testing code, supported by linking against +GoogleTest by default. It also facilitates writing concise tests while bridging +the semantic gap between the unit under test and the OMPT-event testing. + +# Testing macros + +Corresponding macro definitions are located in: `./include/AssertMacros.h` + +## OMPT_GENERATE_EVENTS(NumberOfCopies, EventMacro) +`TODO` + +## OMPT_ASSERT_SET_EVENT(Name, Group, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SET(EventTy, ...) +`TODO` + +## OMPT_ASSERT_SET_GROUPED(Group, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SET_NAMED(Name, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SET_EVENT_NOT(Name, Group, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SET_NOT(EventTy, ...) +`TODO` + +## OMPT_ASSERT_SET_GROUPED_NOT(Group, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SET_NAMED_NOT(Name, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SEQUENCE_EVENT(Name, Group, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SEQUENCE(EventTy, ...) +This macro checks for the occurrence of the provided event, which also +entails the exact sequence of events. When only using this assertion macro one +has to provide every single event in the exact order of occurrence. + +## OMPT_ASSERT_SEQUENCE_GROUPED(Group, EventTy, ...) +This macro acts like `OMPT_ASSERT_SEQUENCE` with the addition of grouping. + +## OMPT_ASSERT_SEQUENCE_NAMED(Name, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SEQUENCE_EVENT_NOT(Name, Group, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SEQUENCE_NOT(EventTy, ...) +`TODO` + +## OMPT_ASSERT_SEQUENCE_GROUPED_NOT(Group, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SEQUENCE_NAMED_NOT(Name, EventTy, ...) +`TODO` + +## OMPT_ASSERT_SEQUENCE_SUSPEND() +`TODO` + +## OMPT_ASSERT_SEQUENCE_ONLY(EventTy, ...) +This macro acts like `OMPT_ASSERT_SEQUENCE`, while actually being preceded +-AND- succeeded by commands to suspend sequenced assertion until the next match. +As a result, one may omit all other "unneccessary" events from the sequence. + +## OMPT_ASSERT_SEQUENCE_GROUPED_ONLY(Group, EventTy, ...) +This macro acts like `OMPT_ASSERT_SEQUENCE_ONLY`, plus grouping. + +## OMPT_ASSERT_SEQUENCE_NAMED_ONLY(Name, EventTy, ...) +`TODO` + +## OMPT_ASSERTER_MODE_STRICT(Asserter) +`TODO` + +## OMPT_ASSERTER_MODE_RELAXED(Asserter) +`TODO` + +## OMPT_ASSERT_SEQUENCE_MODE_STRICT() +`TODO` + +## OMPT_ASSERT_SEQUENCE_MODE_RELAXED() +`TODO` + +## OMPT_ASSERT_SET_MODE_STRICT() +`TODO` + +## OMPT_ASSERT_SET_MODE_RELAXED() +`TODO` + +## OMPT_ASSERTER_DISABLE(Asserter) +`TODO` + +## OMPT_ASSERTER_ENABLE(Asserter) +`TODO` + +## OMPT_ASSERT_SET_DISABLE() +`TODO` + +## OMPT_ASSERT_SET_ENABLE() +`TODO` + +## OMPT_ASSERT_SEQUENCE_DISABLE() +`TODO` + +## OMPT_ASSERT_SEQUENCE_ENABLE() +`TODO` + +## OMPT_REPORT_EVENT_DISABLE() +`TODO` + +## OMPT_REPORT_EVENT_ENABLE() +`TODO` + +## OMPT_ASSERTER_PERMIT_EVENT(Asserter, EventTy) +`TODO` + +## OMPT_ASSERTER_SUPPRESS_EVENT(Asserter, EventTy) +`TODO` + +## OMPT_PERMIT_EVENT(EventTy) +`TODO` + +## OMPT_SUPPRESS_EVENT(EventTy) +`TODO` + +## OMPT_ASSERTER_LOG_LEVEL(Asserter, LogLevel) +`TODO` + +## OMPT_ASSERTER_LOG_FORMATTED(Asserter, FormatLog) +`TODO` + +## OMPT_ASSERT_SYNC_POINT(SyncPointName) +`TODO` + +### Grouping Asserts + +This allows to generate and verify data during runtime of a test. +Currently, we only use target region information which manifests into groups. +This allows to correlate multiple events to a certain target region without +manual interaction just by specifying a groupname for these events. + +When a target region is encountered and we are about to enter it, we gather the +`target_id` (non-EMI) -OR- `target_data->value` (EMI). This value is stored +along the groupname for future reference. Upon target region end, the +corresponding group is erased. (Note: The groupname is available again.) + +Other asserted callbacks which may occur within target regions query their +groupname: retrieving and comparing the value of the group against the observed +event's value. + +### Suspending Sequenced Asserts + +When a sequence of events is not of interest while testing, these additional +events may be ignored by suspending the assertion until the next match. This +can be done by using `OMPT_ASSERT_SEQUENCE_SUSPEND` manually or the `_ONLY` +macro variants, like `OMPT_ASSERT_GROUPED_SEQUENCE_ONLY`. + +The former adds a special event to the queue of expected events and signal +that any non-matching event should be ignored rather than failing the test. +`_ONLY` macros embed their corresponding macro between two calls to +`OMPT_ASSERT_SEQUENCE_SUSPEND`. As a consequence, we enter passive assertion +until a match occurs, then enter passive assertion again. This enables us to +"only" assert a certain, single event in arbitrary circumstances. + +### Asserter Modes +`TODO` + +## Aliases (shorthands) +To allow for easier writing of tests and enhanced readability, the following set +of aliases is introduced. The left hand side represents the original value, +while the right hand side depicts the shorthand version. + +| Type | Enum Value | Shorthand | +|---------------------------|---------------------------------------------|---------------------------| +| **ompt_scope_endpoint_t** | | | +| | ompt_scope_begin | BEGIN | +| | ompt_scope_end | END | +| | ompt_scope_beginend | BEGINEND | +| **ompt_target_t** | | | +| | ompt_target | TARGET | +| | ompt_target_enter_data | ENTER_DATA | +| | ompt_target_exit_data | EXIT_DATA | +| | ompt_target_update | UPDATE | +| | ompt_target_nowait | TARGET_NOWAIT | +| | ompt_target_enter_data_nowait | ENTER_DATA_NOWAIT | +| | ompt_target_exit_data_nowait | EXIT_DATA_NOWAIT | +| | ompt_target_update_nowait | UPDATE_NOWAIT | +| **ompt_target_data_op_t** | | | +| | ompt_target_data_alloc | ALLOC | +| | ompt_target_data_transfer_to_device | H2D | +| | ompt_target_data_transfer_from_device | D2H | +| | ompt_target_data_delete | DELETE | +| | ompt_target_data_associate | ASSOCIATE | +| | ompt_target_data_disassociate | DISASSOCIATE | +| | ompt_target_data_alloc_async | ALLOC_ASYNC | +| | ompt_target_data_transfer_to_device_async | H2D_ASYNC | +| | ompt_target_data_transfer_from_device_async | D2H_ASYNC | +| | ompt_target_data_delete_async | DELETE_ASYNC | +| **ompt_callbacks_t** | | | +| | ompt_callback_target | CB_TARGET | +| | ompt_callback_target_data_op | CB_DATAOP | +| | ompt_callback_target_submit | CB_KERNEL | +| **ompt_work_t** | | | +| | ompt_work_loop | WORK_LOOP | +| | ompt_work_sections | WORK_SECT | +| | ompt_work_single_executor | WORK_EXEC | +| | ompt_work_single_other | WORK_SINGLE | +| | ompt_work_workshare | WORK_SHARE | +| | ompt_work_distribute | WORK_DIST | +| | ompt_work_taskloop | WORK_TASK | +| | ompt_work_scope | WORK_SCOPE | +| | ompt_work_loop_static | WORK_LOOP_STA | +| | ompt_work_loop_dynamic | WORK_LOOP_DYN | +| | ompt_work_loop_guided | WORK_LOOP_GUI | +| | ompt_work_loop_other | WORK_LOOP_OTH | +| **ompt_sync_region_t** | | | +| | ompt_sync_region_barrier | SR_BARRIER | +| | ompt_sync_region_barrier_implicit | SR_BARRIER_IMPL | +| | ompt_sync_region_barrier_explicit | SR_BARRIER_EXPL | +| | ompt_sync_region_barrier_implementation | SR_BARRIER_IMPLEMENTATION | +| | ompt_sync_region_taskwait | SR_TASKWAIT | +| | ompt_sync_region_taskgroup | SR_TASKGROUP | +| | ompt_sync_region_reduction | SR_REDUCTION | +| | ompt_sync_region_barrier_implicit_workshare | SR_BARRIER_IMPL_WORKSHARE | +| | ompt_sync_region_barrier_implicit_parallel | SR_BARRIER_IMPL_PARALLEL | +| | ompt_sync_region_barrier_teams | SR_BARRIER_TEAMS | + + +Limitations +=========== +Currently, there are some peculiarities which have to be kept in mind when using +this library: + +## Callbacks + * It is not possible to e.g. test non-EMI -AND- EMI callbacks within the same + test file. Reason: all testsuites share the initialization and therefore the + registered callbacks. + * It is not possible to check for device initialization and/or load callbacks + more than once per test file. The first testcase being run, triggers these + callbacks and is therefore the only testcase that is able to check for them. + This is because, after that, the device remains initialized. + * It is not possible to check for device finalization callbacks, as libomptest + is un-loaded before this callback occurs. Same holds true for the final + ThreadEnd event(s). + +Miscellaneous +============= + +## Default values + +To allow for easier writing of tests, many OMPT events may be created using less +parameters than actually requested by the spec -- by using default values. These +defaults are currently set to the corresponding data type's minimum as follows, +for example integers use: `std::numeric_limits::min()`. + +When an expected / user-specified event has certain values set to the +corresponding default, these values are ignored. That is, when compared to an +observed event, this property is considered as 'equal' regardless of their +actual equality relation. + +References +========== +[0]: ompTest – Unit Testing with OMPT + https://doi.org/10.1109/SCW63240.2024.00031 + +[1]: OMPTBench – OpenMP Tool Interface Conformance Testing + https://doi.org/10.1109/SCW63240.2024.00036 diff --git a/openmp/tools/omptest/cmake/omptest-config.cmake.in b/openmp/tools/omptest/cmake/omptest-config.cmake.in new file mode 100644 index 0000000000000..dca02505539b0 --- /dev/null +++ b/openmp/tools/omptest/cmake/omptest-config.cmake.in @@ -0,0 +1,29 @@ +################################################################################ +## +## omptest cmake configuration file. +## Enable support for find_package. +## +################################################################################ + +# Compute installation prefix relative to this file. +get_filename_component(LLVM_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_FILE}" REALPATH) +get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) +get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) +get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) +get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) +get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) +get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) + +# Provide header and library paths. +set(LIBOMP_HEADERS_INSTALL_PATH "${LLVM_INSTALL_PREFIX}/@LIBOMP_HEADERS_INSTALL_PATH@") +set(LIBOMP_LIBRARY_INSTALL_PATH "${LLVM_INSTALL_PREFIX}/@OPENMP_INSTALL_LIBDIR@") +set(omptest_INCLUDE_DIR "${LIBOMP_HEADERS_INSTALL_PATH}/omptest/include") +set(omptest_LIBRARY_DIR "${LIBOMP_LIBRARY_INSTALL_PATH}") + +# Provide compiler default values. +set(LLVM_BIN_INSTALL_DIR "${LLVM_INSTALL_PREFIX}/bin") +set(omptest_C_COMPILER "${LLVM_BIN_INSTALL_DIR}/clang") +set(omptest_CXX_COMPILER "${LLVM_BIN_INSTALL_DIR}/clang++") + +# Provide information, if ompTest has been built 'standalone'. +set(LIBOMPTEST_BUILD_STANDALONE "@LIBOMPTEST_BUILD_STANDALONE@") diff --git a/openmp/tools/omptest/include/AssertMacros.h b/openmp/tools/omptest/include/AssertMacros.h new file mode 100644 index 0000000000000..d5d191c10dabb --- /dev/null +++ b/openmp/tools/omptest/include/AssertMacros.h @@ -0,0 +1,138 @@ +//===- AssertMacros.h - Macro aliases for ease-of-use -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Provides macros to be used in unit tests for OMPT events. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_ASSERTMACROS_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_ASSERTMACROS_H + +#define OMPTEST_EXCLUDED_EVENT omptest::ObserveState::never +#define OMPTEST_REQUIRED_EVENT omptest::ObserveState::always + +/// ASSERT MACROS TO BE USED BY THE USER + +#define OMPT_GENERATE_EVENTS(NumberOfCopies, EventMacro) \ + for (size_t i = 0; i < NumberOfCopies; ++i) { \ + EventMacro \ + } + +// Handle a minimum unordered set of events +// Required events +#define OMPT_ASSERT_SET_EVENT(Name, Group, EventTy, ...) \ + SetAsserter->insert(OmptAssertEvent::EventTy( \ + Name, Group, OMPTEST_REQUIRED_EVENT, __VA_ARGS__)); +#define OMPT_ASSERT_SET(EventTy, ...) \ + OMPT_ASSERT_SET_EVENT("", "", EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SET_GROUPED(Group, EventTy, ...) \ + OMPT_ASSERT_SET_EVENT("", Group, EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SET_NAMED(Name, EventTy, ...) \ + OMPT_ASSERT_SET_EVENT(Name, "", EventTy, __VA_ARGS__) +// Excluded ("NOT") events +#define OMPT_ASSERT_SET_EVENT_NOT(Name, Group, EventTy, ...) \ + SetAsserter->insert(OmptAssertEvent::EventTy( \ + Name, Group, OMPTEST_EXCLUDED_EVENT, __VA_ARGS__)); +#define OMPT_ASSERT_SET_NOT(EventTy, ...) \ + OMPT_ASSERT_SET_EVENT_NOT("", "", EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SET_GROUPED_NOT(Group, EventTy, ...) \ + OMPT_ASSERT_SET_EVENT_NOT("", Group, EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SET_NAMED_NOT(Name, EventTy, ...) \ + OMPT_ASSERT_SET_EVENT_NOT(Name, "", EventTy, __VA_ARGS__) + +// Handle an exact sequence of events +// Required events +#define OMPT_ASSERT_SEQUENCE_EVENT(Name, Group, EventTy, ...) \ + SequenceAsserter->insert(OmptAssertEvent::EventTy( \ + Name, Group, OMPTEST_REQUIRED_EVENT, __VA_ARGS__)); +#define OMPT_ASSERT_SEQUENCE(EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_EVENT("", "", EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SEQUENCE_GROUPED(Group, EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_EVENT("", Group, EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SEQUENCE_NAMED(Name, EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_EVENT(Name, "", EventTy, __VA_ARGS__) +// Excluded ("NOT") events +#define OMPT_ASSERT_SEQUENCE_EVENT_NOT(Name, Group, EventTy, ...) \ + SequenceAsserter->insert(OmptAssertEvent::EventTy( \ + Name, Group, OMPTEST_EXCLUDED_EVENT, __VA_ARGS__)); +#define OMPT_ASSERT_SEQUENCE_NOT(EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_EVENT_NOT("", "", EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SEQUENCE_GROUPED_NOT(Group, EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_EVENT_NOT("", Group, EventTy, __VA_ARGS__) +#define OMPT_ASSERT_SEQUENCE_NAMED_NOT(Name, EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_EVENT_NOT(Name, "", EventTy, __VA_ARGS__) +// Special command: suspend active assertion +// The created event is not correlated to any observed event +#define OMPT_ASSERT_SEQUENCE_SUSPEND() \ + SequenceAsserter->insert( \ + OmptAssertEvent::AssertionSuspend("", "", OMPTEST_EXCLUDED_EVENT)); +#define OMPT_ASSERT_SEQUENCE_ONLY(EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_SUSPEND() \ + OMPT_ASSERT_SEQUENCE_EVENT("", "", EventTy, __VA_ARGS__) \ + OMPT_ASSERT_SEQUENCE_SUSPEND() +#define OMPT_ASSERT_SEQUENCE_GROUPED_ONLY(Group, EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_SUSPEND() \ + OMPT_ASSERT_SEQUENCE_EVENT("", Group, EventTy, __VA_ARGS__) \ + OMPT_ASSERT_SEQUENCE_SUSPEND() +#define OMPT_ASSERT_SEQUENCE_NAMED_ONLY(Name, EventTy, ...) \ + OMPT_ASSERT_SEQUENCE_SUSPEND() \ + OMPT_ASSERT_SEQUENCE_EVENT(Name, "", EventTy, __VA_ARGS__) \ + OMPT_ASSERT_SEQUENCE_SUSPEND() + +#define OMPT_ASSERTER_MODE_STRICT(Asserter) \ + Asserter->setOperationMode(AssertMode::strict); +#define OMPT_ASSERTER_MODE_RELAXED(Asserter) \ + Asserter->setOperationMode(AssertMode::relaxed); +#define OMPT_ASSERT_SEQUENCE_MODE_STRICT() \ + OMPT_ASSERTER_MODE_STRICT(SequenceAsserter) +#define OMPT_ASSERT_SEQUENCE_MODE_RELAXED() \ + OMPT_ASSERTER_MODE_RELAXED(SequenceAsserter) +#define OMPT_ASSERT_SET_MODE_STRICT() OMPT_ASSERTER_MODE_STRICT(SetAsserter) +#define OMPT_ASSERT_SET_MODE_RELAXED() OMPT_ASSERTER_MODE_RELAXED(SetAsserter) + +// Enable / disable asserters entirely +#define OMPT_ASSERTER_DISABLE(Asserter) Asserter->setActive(false); +#define OMPT_ASSERTER_ENABLE(Asserter) Asserter->setActive(true); +#define OMPT_ASSERT_SET_DISABLE() OMPT_ASSERTER_DISABLE(SetAsserter) +#define OMPT_ASSERT_SET_ENABLE() OMPT_ASSERTER_ENABLE(SetAsserter) +#define OMPT_ASSERT_SEQUENCE_DISABLE() OMPT_ASSERTER_DISABLE(SequenceAsserter) +#define OMPT_ASSERT_SEQUENCE_ENABLE() OMPT_ASSERTER_ENABLE(SequenceAsserter) +#define OMPT_REPORT_EVENT_DISABLE() OMPT_ASSERTER_DISABLE(EventReporter) +#define OMPT_REPORT_EVENT_ENABLE() OMPT_ASSERTER_ENABLE(EventReporter) + +// Enable / disable certain event types for asserters +#define OMPT_ASSERTER_PERMIT_EVENT(Asserter, EventTy) \ + Asserter->permitEvent(EventTy); +#define OMPT_ASSERTER_SUPPRESS_EVENT(Asserter, EventTy) \ + Asserter->suppressEvent(EventTy); +#define OMPT_PERMIT_EVENT(EventTy) \ + OMPT_ASSERTER_PERMIT_EVENT(SetAsserter, EventTy); \ + OMPT_ASSERTER_PERMIT_EVENT(EventReporter, EventTy); \ + OMPT_ASSERTER_PERMIT_EVENT(SequenceAsserter, EventTy); +#define OMPT_SUPPRESS_EVENT(EventTy) \ + OMPT_ASSERTER_SUPPRESS_EVENT(SetAsserter, EventTy); \ + OMPT_ASSERTER_SUPPRESS_EVENT(EventReporter, EventTy); \ + OMPT_ASSERTER_SUPPRESS_EVENT(SequenceAsserter, EventTy); + +// Set logging level for asserters +// Note: Logger is a singleton, hence this will affect all asserter instances +#define OMPT_ASSERTER_LOG_LEVEL(Asserter, LogLevel) \ + Asserter->getLog()->setLoggingLevel(LogLevel); + +// Set log formatting (esp. coloring) for asserters +// Note: Logger is a singleton, hence this will affect all asserter instances +#define OMPT_ASSERTER_LOG_FORMATTED(Asserter, FormatLog) \ + Asserter->getLog()->setFormatOutput(FormatLog); + +// SyncPoint handling +#define OMPT_ASSERT_SYNC_POINT(SyncPointName) \ + flush_traced_devices(); \ + OmptCallbackHandler::get().handleAssertionSyncPoint(SyncPointName); + +#endif diff --git a/openmp/tools/omptest/include/InternalEvent.h b/openmp/tools/omptest/include/InternalEvent.h new file mode 100644 index 0000000000000..455d8d996e5f4 --- /dev/null +++ b/openmp/tools/omptest/include/InternalEvent.h @@ -0,0 +1,331 @@ +//===- InternalEvent.h - Internal event representation ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Declares internal event representations along the default CTOR definition. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_INTERNALEVENT_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_INTERNALEVENT_H + +#include "InternalEventCommon.h" + +#include +#include +#include + +#define expectedDefault(TypeName) std::numeric_limits::min() + +namespace omptest { + +namespace util { + +/// String manipulation helper function. Takes up to 8 bytes of data and returns +/// their hexadecimal representation as string. The data can be expanded to the +/// given size in bytes and will by default be prefixed with '0x'. +std::string makeHexString(uint64_t Data, bool IsPointer = true, + size_t DataBytes = 0, bool ShowHexBase = true); + +} // namespace util + +namespace internal { +// clang-format off +event_class_w_custom_body(AssertionSyncPoint, \ + AssertionSyncPoint(const std::string &Name) \ + : InternalEvent(EventTy::AssertionSyncPoint), Name(Name) {} \ + \ + const std::string Name; \ +) +event_class_stub(AssertionSuspend) +event_class_w_custom_body(ThreadBegin, \ + ThreadBegin(ompt_thread_t ThreadType) \ + : InternalEvent(EventTy::ThreadBegin), ThreadType(ThreadType) {} \ + \ + ompt_thread_t ThreadType; \ +) +event_class_w_custom_body(ThreadEnd, \ + ThreadEnd() : InternalEvent(EventTy::ThreadEnd) {} \ +) +event_class_w_custom_body(ParallelBegin, \ + ParallelBegin(int NumThreads) \ + : InternalEvent(EventTy::ParallelBegin), NumThreads(NumThreads) {} \ + \ + unsigned int NumThreads; \ +) +event_class_w_custom_body(ParallelEnd, \ + ParallelEnd(ompt_data_t *ParallelData, ompt_data_t *EncounteringTaskData, \ + int Flags, const void *CodeptrRA) \ + : InternalEvent(EventTy::ParallelEnd), ParallelData(ParallelData), \ + EncounteringTaskData(EncounteringTaskData), Flags(Flags), \ + CodeptrRA(CodeptrRA) {} \ + \ +ompt_data_t *ParallelData; \ +ompt_data_t *EncounteringTaskData; \ +int Flags; \ +const void *CodeptrRA; \ +) +event_class_w_custom_body(Work, \ + Work(ompt_work_t WorkType, ompt_scope_endpoint_t Endpoint, \ + ompt_data_t *ParallelData, ompt_data_t *TaskData, uint64_t Count, \ + const void *CodeptrRA) \ + : InternalEvent(EventTy::Work), WorkType(WorkType), Endpoint(Endpoint), \ + ParallelData(ParallelData), TaskData(TaskData), Count(Count), \ + CodeptrRA(CodeptrRA) {} \ + \ +ompt_work_t WorkType; \ +ompt_scope_endpoint_t Endpoint; \ +ompt_data_t *ParallelData; \ +ompt_data_t *TaskData; \ +uint64_t Count; \ +const void *CodeptrRA; \ +) +event_class_w_custom_body(Dispatch, \ + Dispatch(ompt_data_t *ParallelData, ompt_data_t *TaskData, \ + ompt_dispatch_t Kind, ompt_data_t Instance) \ + : InternalEvent(EventTy::Dispatch), ParallelData(ParallelData), \ + TaskData(TaskData), Kind(Kind), Instance(Instance) {} \ + \ +ompt_data_t *ParallelData; \ +ompt_data_t *TaskData; \ +ompt_dispatch_t Kind; \ +ompt_data_t Instance; \ +) +event_class_w_custom_body(TaskCreate, \ + TaskCreate(ompt_data_t *EncounteringTaskData, \ + const ompt_frame_t *EncounteringTaskFrame, \ + ompt_data_t *NewTaskData, int Flags, int HasDependences, \ + const void *CodeptrRA) \ + : InternalEvent(EventTy::TaskCreate), \ + EncounteringTaskData(EncounteringTaskData), \ + EncounteringTaskFrame(EncounteringTaskFrame), NewTaskData(NewTaskData), \ + Flags(Flags), HasDependences(HasDependences), CodeptrRA(CodeptrRA) {} \ + \ +ompt_data_t *EncounteringTaskData; \ +const ompt_frame_t *EncounteringTaskFrame; \ +ompt_data_t *NewTaskData; \ +int Flags; \ +int HasDependences; \ +const void *CodeptrRA; \ +) +event_class_stub(Dependences) +event_class_stub(TaskDependence) +event_class_stub(TaskSchedule) +event_class_w_custom_body(ImplicitTask, \ + ImplicitTask(ompt_scope_endpoint_t Endpoint, ompt_data_t *ParallelData, \ + ompt_data_t *TaskData, unsigned int ActualParallelism, \ + unsigned int Index, int Flags) \ + : InternalEvent(EventTy::ImplicitTask), Endpoint(Endpoint), \ + ParallelData(ParallelData), TaskData(TaskData), \ + ActualParallelism(ActualParallelism), Index(Index), Flags(Flags) {} \ + \ +ompt_scope_endpoint_t Endpoint; \ +ompt_data_t *ParallelData; \ +ompt_data_t *TaskData; \ +unsigned int ActualParallelism; \ +unsigned int Index; \ +int Flags; \ +) +event_class_stub(Masked) +event_class_w_custom_body(SyncRegion, \ + SyncRegion(ompt_sync_region_t Kind, ompt_scope_endpoint_t Endpoint, \ + ompt_data_t *ParallelData, ompt_data_t *TaskData, \ + const void *CodeptrRA) \ + : InternalEvent(EventTy::SyncRegion), Kind(Kind), Endpoint(Endpoint), \ + ParallelData(ParallelData), TaskData(TaskData), CodeptrRA(CodeptrRA) {} \ + \ +ompt_sync_region_t Kind; \ +ompt_scope_endpoint_t Endpoint; \ +ompt_data_t *ParallelData; \ +ompt_data_t *TaskData; \ +const void *CodeptrRA; \ +) +event_class_stub(MutexAcquire) +event_class_stub(Mutex) +event_class_stub(NestLock) +event_class_stub(Flush) +event_class_stub(Cancel) +event_class_w_custom_body(Target, \ + Target(ompt_target_t Kind, ompt_scope_endpoint_t Endpoint, int DeviceNum, \ + ompt_data_t *TaskData, ompt_id_t TargetId, const void *CodeptrRA) \ + : InternalEvent(EventTy::Target), Kind(Kind), Endpoint(Endpoint), \ + DeviceNum(DeviceNum), TaskData(TaskData), TargetId(TargetId), \ + CodeptrRA(CodeptrRA) {} \ + \ + ompt_target_t Kind; \ + ompt_scope_endpoint_t Endpoint; \ + int DeviceNum; \ + ompt_data_t *TaskData; \ + ompt_id_t TargetId; \ + const void *CodeptrRA; \ +) +event_class_w_custom_body(TargetEmi, \ + TargetEmi(ompt_target_t Kind, ompt_scope_endpoint_t Endpoint, int DeviceNum, \ + ompt_data_t *TaskData, ompt_data_t *TargetTaskData, \ + ompt_data_t *TargetData, const void *CodeptrRA) \ + : InternalEvent(EventTy::TargetEmi), Kind(Kind), Endpoint(Endpoint), \ + DeviceNum(DeviceNum), TaskData(TaskData), \ + TargetTaskData(TargetTaskData), TargetData(TargetData), \ + CodeptrRA(CodeptrRA) {} \ + \ + ompt_target_t Kind; \ + ompt_scope_endpoint_t Endpoint; \ + int DeviceNum; \ + ompt_data_t *TaskData; \ + ompt_data_t *TargetTaskData; \ + ompt_data_t *TargetData; \ + const void *CodeptrRA; \ +) +event_class_w_custom_body(TargetDataOp, \ + TargetDataOp(ompt_id_t TargetId, ompt_id_t HostOpId, \ + ompt_target_data_op_t OpType, void *SrcAddr, int SrcDeviceNum, \ + void *DstAddr, int DstDeviceNum, size_t Bytes, \ + const void *CodeptrRA) \ + : InternalEvent(EventTy::TargetDataOp), TargetId(TargetId), \ + HostOpId(HostOpId), OpType(OpType), SrcAddr(SrcAddr), \ + SrcDeviceNum(SrcDeviceNum), DstAddr(DstAddr), \ + DstDeviceNum(DstDeviceNum), Bytes(Bytes), CodeptrRA(CodeptrRA) {} \ + \ + ompt_id_t TargetId; \ + ompt_id_t HostOpId; \ + ompt_target_data_op_t OpType; \ + void *SrcAddr; \ + int SrcDeviceNum; \ + void *DstAddr; \ + int DstDeviceNum; \ + size_t Bytes; \ + const void *CodeptrRA; \ +) +event_class_w_custom_body(TargetDataOpEmi, \ + TargetDataOpEmi(ompt_scope_endpoint_t Endpoint, ompt_data_t *TargetTaskData, \ + ompt_data_t *TargetData, ompt_id_t *HostOpId, \ + ompt_target_data_op_t OpType, void *SrcAddr, \ + int SrcDeviceNum, void *DstAddr, int DstDeviceNum, \ + size_t Bytes, const void *CodeptrRA) \ + : InternalEvent(EventTy::TargetDataOpEmi), Endpoint(Endpoint), \ + TargetTaskData(TargetTaskData), TargetData(TargetData), \ + HostOpId(HostOpId), OpType(OpType), SrcAddr(SrcAddr), \ + SrcDeviceNum(SrcDeviceNum), DstAddr(DstAddr), \ + DstDeviceNum(DstDeviceNum), Bytes(Bytes), CodeptrRA(CodeptrRA) {} \ + \ + ompt_scope_endpoint_t Endpoint; \ + ompt_data_t *TargetTaskData; \ + ompt_data_t *TargetData; \ + ompt_id_t *HostOpId; \ + ompt_target_data_op_t OpType; \ + void *SrcAddr; \ + int SrcDeviceNum; \ + void *DstAddr; \ + int DstDeviceNum; \ + size_t Bytes; \ + const void *CodeptrRA; \ +) +event_class_w_custom_body(TargetSubmit, \ + TargetSubmit(ompt_id_t TargetId, ompt_id_t HostOpId, \ + unsigned int RequestedNumTeams) \ + : InternalEvent(EventTy::TargetSubmit), TargetId(TargetId), \ + HostOpId(HostOpId), RequestedNumTeams(RequestedNumTeams) {} \ + \ + ompt_id_t TargetId; \ + ompt_id_t HostOpId; \ + unsigned int RequestedNumTeams; \ +) +event_class_w_custom_body(TargetSubmitEmi, \ + TargetSubmitEmi(ompt_scope_endpoint_t Endpoint, ompt_data_t *TargetData, \ + ompt_id_t *HostOpId, unsigned int RequestedNumTeams) \ + : InternalEvent(EventTy::TargetSubmitEmi), Endpoint(Endpoint), \ + TargetData(TargetData), HostOpId(HostOpId), \ + RequestedNumTeams(RequestedNumTeams) {} \ + \ + ompt_scope_endpoint_t Endpoint; \ + ompt_data_t *TargetData; \ + ompt_id_t *HostOpId; \ + unsigned int RequestedNumTeams; \ +) +event_class_stub(ControlTool) +event_class_w_custom_body(DeviceInitialize, \ + DeviceInitialize(int DeviceNum, const char *Type, ompt_device_t *Device, \ + ompt_function_lookup_t LookupFn, const char *DocStr) \ + : InternalEvent(EventTy::DeviceInitialize), DeviceNum(DeviceNum), \ + Type(Type), Device(Device), LookupFn(LookupFn), DocStr(DocStr) {} \ + \ + int DeviceNum; \ + const char *Type; \ + ompt_device_t *Device; \ + ompt_function_lookup_t LookupFn; \ + const char *DocStr; \ +) +event_class_w_custom_body(DeviceFinalize, \ + DeviceFinalize(int DeviceNum) \ + : InternalEvent(EventTy::DeviceFinalize), DeviceNum(DeviceNum) {} \ + \ + int DeviceNum; \ +) +event_class_w_custom_body(DeviceLoad, \ + DeviceLoad(int DeviceNum, const char *Filename, int64_t OffsetInFile, \ + void *VmaInFile, size_t Bytes, void *HostAddr, void *DeviceAddr, \ + uint64_t ModuleId) \ + : InternalEvent(EventTy::DeviceLoad), DeviceNum(DeviceNum), \ + Filename(Filename), OffsetInFile(OffsetInFile), VmaInFile(VmaInFile), \ + Bytes(Bytes), HostAddr(HostAddr), DeviceAddr(DeviceAddr), \ + ModuleId(ModuleId) {} \ + \ + int DeviceNum; \ + const char *Filename; \ + int64_t OffsetInFile; \ + void *VmaInFile; \ + size_t Bytes; \ + void *HostAddr; \ + void *DeviceAddr; \ + uint64_t ModuleId; \ +) +event_class_stub(DeviceUnload) +event_class_w_custom_body(BufferRequest, \ + BufferRequest(int DeviceNum, ompt_buffer_t **Buffer, size_t *Bytes) \ + : InternalEvent(EventTy::BufferRequest), DeviceNum(DeviceNum), \ + Buffer(Buffer), Bytes(Bytes) {} \ + \ + int DeviceNum; \ + ompt_buffer_t **Buffer; \ + size_t *Bytes; \ +) +event_class_w_custom_body(BufferComplete, \ + BufferComplete(int DeviceNum, ompt_buffer_t *Buffer, size_t Bytes, \ + ompt_buffer_cursor_t Begin, int BufferOwned) \ + : InternalEvent(EventTy::BufferComplete), DeviceNum(DeviceNum), \ + Buffer(Buffer), Bytes(Bytes), Begin(Begin), BufferOwned(BufferOwned) {} \ + \ + int DeviceNum; \ + ompt_buffer_t *Buffer; \ + size_t Bytes; \ + ompt_buffer_cursor_t Begin; \ + int BufferOwned; \ +) +event_class_w_custom_body(BufferRecord, \ + BufferRecord(ompt_record_ompt_t *RecordPtr) \ + : InternalEvent(EventTy::BufferRecord), RecordPtr(RecordPtr) { \ + if (RecordPtr != nullptr) Record = *RecordPtr; \ + else memset(&Record, 0, sizeof(ompt_record_ompt_t)); \ + } \ + \ + ompt_record_ompt_t Record; \ + ompt_record_ompt_t *RecordPtr; \ +) +event_class_w_custom_body(BufferRecordDeallocation, \ + BufferRecordDeallocation(ompt_buffer_t *Buffer) \ + : InternalEvent(EventTy::BufferRecordDeallocation), Buffer(Buffer) {} \ + \ + ompt_buffer_t *Buffer; \ +) +// clang-format on + +} // namespace internal + +} // namespace omptest + +#endif diff --git a/openmp/tools/omptest/include/InternalEventCommon.h b/openmp/tools/omptest/include/InternalEventCommon.h new file mode 100644 index 0000000000000..e48eeddd975ed --- /dev/null +++ b/openmp/tools/omptest/include/InternalEventCommon.h @@ -0,0 +1,133 @@ +//===- InternalEventCommon.h - Common internal event basics -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Provides event types, and class/operator declaration macros. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_INTERNALEVENTCOMMON_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_INTERNALEVENTCOMMON_H + +#include "omp-tools.h" + +#include +#include + +namespace omptest { + +namespace internal { +/// Enum values are used for comparison of observed and asserted events +/// List is based on OpenMP 5.2 specification, table 19.2 (page 447) +enum class EventTy { + None, // not part of OpenMP spec, used for implementation + AssertionSyncPoint, // not part of OpenMP spec, used for implementation + AssertionSuspend, // not part of OpenMP spec, used for implementation + BufferRecord, // not part of OpenMP spec, used for implementation + BufferRecordDeallocation, // not part of OpenMP spec, used for implementation + ThreadBegin, + ThreadEnd, + ParallelBegin, + ParallelEnd, + Work, + Dispatch, + TaskCreate, // TODO: Implement + Dependences, // TODO: Implement + TaskDependence, // TODO: Implement + TaskSchedule, // TODO: Implement + ImplicitTask, // TODO: Implement + Masked, // TODO: Implement + SyncRegion, + MutexAcquire, // TODO: Implement + Mutex, // TODO: Implement + NestLock, // TODO: Implement + Flush, // TODO: Implement + Cancel, // TODO: Implement + DeviceInitialize, + DeviceFinalize, + DeviceLoad, + DeviceUnload, + BufferRequest, + BufferComplete, + TargetDataOp, + TargetDataOpEmi, + Target, + TargetEmi, + TargetSubmit, + TargetSubmitEmi, + ControlTool +}; + +struct InternalEvent { + EventTy Type; + EventTy getType() const { return Type; } + + InternalEvent() : Type(EventTy::None) {} + InternalEvent(EventTy T) : Type(T) {} + virtual ~InternalEvent() = default; + + virtual bool equals(const InternalEvent *o) const { + assert(false && "Base class implementation"); + return false; + }; + + virtual std::string toString() const { + std::string S{"InternalEvent: Type="}; + S.append(std::to_string((uint32_t)Type)); + return S; + } +}; + +#define event_class_stub(EvTy) \ + struct EvTy : public InternalEvent { \ + virtual bool equals(const InternalEvent *o) const override; \ + EvTy() : InternalEvent(EventTy::EvTy) {} \ + }; + +#define event_class_w_custom_body(EvTy, ...) \ + struct EvTy : public InternalEvent { \ + virtual bool equals(const InternalEvent *o) const override; \ + std::string toString() const override; \ + __VA_ARGS__ \ + }; + +#define event_class_operator_stub(EvTy) \ + bool operator==(const EvTy &Expected, const EvTy &Observed) { return true; } + +#define event_class_operator_w_body(EvTy, ...) \ + bool operator==(const EvTy &Expected, const EvTy &Observed) { __VA_ARGS__ } + +/// Template "base" for the cast functions generated in the define_cast_func +/// macro +template const To *cast(const InternalEvent *From) { + return nullptr; +} + +/// Generates template specialization of the cast operation for the specified +/// EvTy as the template parameter +#define define_cast_func(EvTy) \ + template <> const EvTy *cast(const InternalEvent *From) { \ + if (From->getType() == EventTy::EvTy) \ + return static_cast(From); \ + return nullptr; \ + } + +/// Auto generate the equals override to cast and dispatch to the specific class +/// operator== +#define class_equals_op(EvTy) \ + bool EvTy::equals(const InternalEvent *o) const { \ + if (const auto O = cast(o)) \ + return *this == *O; \ + return false; \ + } + +} // namespace internal + +} // namespace omptest + +#endif diff --git a/openmp/tools/omptest/include/Logging.h b/openmp/tools/omptest/include/Logging.h new file mode 100644 index 0000000000000..0104191b1d15f --- /dev/null +++ b/openmp/tools/omptest/include/Logging.h @@ -0,0 +1,155 @@ +//===- Logging.h - General logging class ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Provides ompTest-tailored logging, with log-levels and formatting/coloring. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_LOGGING_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_LOGGING_H + +#include "OmptAssertEvent.h" + +#include +#include +#include +#include +#include +#include + +namespace omptest { +namespace logging { + +enum class Level : uint32_t { + // Levels (Note: DEBUG may already be reserved) + DIAGNOSTIC = 10, + INFO = 20, + WARNING = 30, + ERROR = 40, + CRITICAL = 50, + + // Types used for formatting options + Default, + ExpectedEvent, + ObservedEvent, + OffendingEvent, + + // Suppress all prints + SILENT = 0xFFFFFFFF +}; + +enum class FormatOption : uint32_t { + // General options + // Note: BOLD is actually "BRIGHT" -- But it will be perceived as 'bold' font + // It is implicitly switching colors to the 'Light' variant + // Thus, it has -NO EFFECT- when already using a Light* color + NONE = 0, + BOLD = 1, + DIM = 2, + UNDERLINED = 4, + BLINK = 5, + INVERTED = 7, + HIDDEN = 8, + // Foreground colors + COLOR_Default = 39, + COLOR_Black = 30, + COLOR_Red = 31, + COLOR_Green = 32, + COLOR_Yellow = 33, + COLOR_Blue = 34, + COLOR_Magenta = 35, + COLOR_Cyan = 36, + COLOR_LightGray = 37, + COLOR_DarkGray = 90, + COLOR_LightRed = 91, + COLOR_LightGreen = 92, + COLOR_LightYellow = 93, + COLOR_LightBlue = 94, + COLOR_LightMagenta = 95, + COLOR_LightCyan = 96, + COLOR_White = 97, + // Background colors + COLOR_BG_Default = 49, + COLOR_BG_Black = 40, + COLOR_BG_Red = 41, + COLOR_BG_Green = 42, + COLOR_BG_Yellow = 43, + COLOR_BG_Blue = 44, + COLOR_BG_Magenta = 45, + COLOR_BG_Cyan = 46, + COLOR_BG_LightGray = 47, + COLOR_BG_DarkGray = 100, + COLOR_BG_LightRed = 101, + COLOR_BG_LightGreen = 102, + COLOR_BG_LightYellow = 103, + COLOR_BG_LightBlue = 104, + COLOR_BG_LightMagenta = 105, + COLOR_BG_LightCyan = 106, + COLOR_BG_White = 107 +}; + +/// Returns a string representation of the given logging level. +const char *to_string(Level LogLevel); + +/// Returns the format options as escaped sequence, for the given logging level +std::string getFormatSequence(Level LogLevel = Level::Default); + +/// Format the given message with the provided option(s) and return it. +/// Here formatting is only concerning control sequences using character +/// which can be obtained using '\e' (on console), '\033' or '\x1B'. +std::string format(const std::string &Message, FormatOption Option); +std::string format(const std::string &Message, std::set Options); + +class Logger { +public: + Logger(Level LogLevel = Level::WARNING, std::ostream &OutStream = std::cerr, + bool FormatOutput = true); + ~Logger(); + + /// Log the given message to the output. + void log(Level LogLevel, const std::string &Message) const; + + /// Log a single event mismatch. + void eventMismatch(const omptest::OmptAssertEvent &OffendingEvent, + const std::string &Message, + Level LogLevel = Level::ERROR) const; + + /// Log an event-pair mismatch. + void eventMismatch(const omptest::OmptAssertEvent &ExpectedEvent, + const omptest::OmptAssertEvent &ObservedEvent, + const std::string &Message, + Level LogLevel = Level::ERROR) const; + + /// Set if output is being formatted (e.g. colored). + void setFormatOutput(bool Enabled); + + /// Return the current (minimum) Logging Level. + Level getLoggingLevel() const; + + /// Set the (minimum) Logging Level. + void setLoggingLevel(Level LogLevel); + +private: + /// The minimum logging level that is considered by the logger instance. + Level LoggingLevel; + + /// The output stream used by the logger instance. + std::ostream &OutStream; + + /// Determine if log messages are formatted using control sequences. + bool FormatOutput; + + /// Mutex to ensure serialized logging + mutable std::mutex LogMutex; +}; + +} // namespace logging +} // namespace omptest + +#endif \ No newline at end of file diff --git a/openmp/tools/omptest/include/OmptAliases.h b/openmp/tools/omptest/include/OmptAliases.h new file mode 100644 index 0000000000000..500be5ef9f749 --- /dev/null +++ b/openmp/tools/omptest/include/OmptAliases.h @@ -0,0 +1,85 @@ +//===- OmptAliases.h - Shorthand aliases for OMPT enum values ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines shorthand aliases for OMPT enum values, providing improved +/// ease-of-use and readability. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTALIASES_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTALIASES_H + +#include + +/// Aliases for enum: ompt_scope_endpoint_t +constexpr ompt_scope_endpoint_t BEGIN = ompt_scope_begin; +constexpr ompt_scope_endpoint_t END = ompt_scope_end; +constexpr ompt_scope_endpoint_t BEGINEND = ompt_scope_beginend; + +/// Aliases for enum: ompt_target_t +constexpr ompt_target_t TARGET = ompt_target; +constexpr ompt_target_t ENTER_DATA = ompt_target_enter_data; +constexpr ompt_target_t EXIT_DATA = ompt_target_exit_data; +constexpr ompt_target_t UPDATE = ompt_target_update; +constexpr ompt_target_t TARGET_NOWAIT = ompt_target_nowait; +constexpr ompt_target_t ENTER_DATA_NOWAIT = ompt_target_enter_data_nowait; +constexpr ompt_target_t EXIT_DATA_NOWAIT = ompt_target_exit_data_nowait; +constexpr ompt_target_t UPDATE_NOWAIT = ompt_target_update_nowait; + +/// Aliases for enum: ompt_target_data_op_t +constexpr ompt_target_data_op_t ALLOC = ompt_target_data_alloc; +constexpr ompt_target_data_op_t H2D = ompt_target_data_transfer_to_device; +constexpr ompt_target_data_op_t D2H = ompt_target_data_transfer_from_device; +constexpr ompt_target_data_op_t DELETE = ompt_target_data_delete; +constexpr ompt_target_data_op_t ASSOCIATE = ompt_target_data_associate; +constexpr ompt_target_data_op_t DISASSOCIATE = ompt_target_data_disassociate; +constexpr ompt_target_data_op_t ALLOC_ASYNC = ompt_target_data_alloc_async; +constexpr ompt_target_data_op_t H2D_ASYNC = + ompt_target_data_transfer_to_device_async; +constexpr ompt_target_data_op_t D2H_ASYNC = + ompt_target_data_transfer_from_device_async; +constexpr ompt_target_data_op_t DELETE_ASYNC = ompt_target_data_delete_async; + +/// Aliases for enum: ompt_callbacks_t (partial) +constexpr ompt_callbacks_t CB_TARGET = ompt_callback_target; +constexpr ompt_callbacks_t CB_DATAOP = ompt_callback_target_data_op; +constexpr ompt_callbacks_t CB_KERNEL = ompt_callback_target_submit; + +/// Aliases for enum: ompt_work_t +constexpr ompt_work_t WORK_LOOP = ompt_work_loop; +constexpr ompt_work_t WORK_SECT = ompt_work_sections; +constexpr ompt_work_t WORK_EXEC = ompt_work_single_executor; +constexpr ompt_work_t WORK_SINGLE = ompt_work_single_other; +constexpr ompt_work_t WORK_SHARE = ompt_work_workshare; +constexpr ompt_work_t WORK_DIST = ompt_work_distribute; +constexpr ompt_work_t WORK_TASK = ompt_work_taskloop; +constexpr ompt_work_t WORK_SCOPE = ompt_work_scope; +constexpr ompt_work_t WORK_LOOP_STA = ompt_work_loop_static; +constexpr ompt_work_t WORK_LOOP_DYN = ompt_work_loop_dynamic; +constexpr ompt_work_t WORK_LOOP_GUI = ompt_work_loop_guided; +constexpr ompt_work_t WORK_LOOP_OTH = ompt_work_loop_other; + +/// Aliases for enum: ompt_sync_region_t +constexpr ompt_sync_region_t SR_BARRIER = ompt_sync_region_barrier; +constexpr ompt_sync_region_t SR_BARRIER_IMPL = + ompt_sync_region_barrier_implicit; +constexpr ompt_sync_region_t SR_BARRIER_EXPL = + ompt_sync_region_barrier_explicit; +constexpr ompt_sync_region_t SR_BARRIER_IMPLEMENTATION = + ompt_sync_region_barrier_implementation; +constexpr ompt_sync_region_t SR_TASKWAIT = ompt_sync_region_taskwait; +constexpr ompt_sync_region_t SR_TASKGROUP = ompt_sync_region_taskgroup; +constexpr ompt_sync_region_t SR_REDUCTION = ompt_sync_region_reduction; +constexpr ompt_sync_region_t SR_BARRIER_IMPL_WORKSHARE = + ompt_sync_region_barrier_implicit_workshare; +constexpr ompt_sync_region_t SR_BARRIER_IMPL_PARALLEL = + ompt_sync_region_barrier_implicit_parallel; +constexpr ompt_sync_region_t SR_BARRIER_TEAMS = ompt_sync_region_barrier_teams; + +#endif diff --git a/openmp/tools/omptest/include/OmptAssertEvent.h b/openmp/tools/omptest/include/OmptAssertEvent.h new file mode 100644 index 0000000000000..87d187c823796 --- /dev/null +++ b/openmp/tools/omptest/include/OmptAssertEvent.h @@ -0,0 +1,377 @@ +//===- OmptAssertEvent.h - Assertion event declarations ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Contains assertion event constructors, for generally all observable events. +/// This includes user-generated events, like synchronization. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTASSERTEVENT_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTASSERTEVENT_H + +#include "InternalEvent.h" +#include "omp-tools.h" + +#include +#include +#include +#include + +namespace omptest { + +enum class ObserveState { generated, always, never }; + +/// Helper function, returning an ObserveState string representation +const char *to_string(ObserveState State); + +/// Assertion event struct, provides statically callable CTORs. +struct OmptAssertEvent { + static OmptAssertEvent AssertionSyncPoint(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + const std::string &SyncPointName); + + static OmptAssertEvent AssertionSuspend(const std::string &Name, + const std::string &Group, + const ObserveState &Expected); + + static OmptAssertEvent ThreadBegin(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_thread_t ThreadType); + + static OmptAssertEvent ThreadEnd(const std::string &Name, + const std::string &Group, + const ObserveState &Expected); + + static OmptAssertEvent ParallelBegin(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + int NumThreads); + + static OmptAssertEvent ParallelEnd( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, + ompt_data_t *ParallelData = expectedDefault(ompt_data_t *), + ompt_data_t *EncounteringTaskData = expectedDefault(ompt_data_t *), + int Flags = expectedDefault(int), + const void *CodeptrRA = expectedDefault(const void *)); + + static OmptAssertEvent + Work(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_work_t WorkType, + ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData = expectedDefault(ompt_data_t *), + ompt_data_t *TaskData = expectedDefault(ompt_data_t *), + uint64_t Count = expectedDefault(uint64_t), + const void *CodeptrRA = expectedDefault(const void *)); + + static OmptAssertEvent + Dispatch(const std::string &Name, const std::string &Group, + const ObserveState &Expected, + ompt_data_t *ParallelData = expectedDefault(ompt_data_t *), + ompt_data_t *TaskData = expectedDefault(ompt_data_t *), + ompt_dispatch_t Kind = expectedDefault(ompt_dispatch_t), + ompt_data_t Instance = expectedDefault(ompt_data_t)); + + static OmptAssertEvent + TaskCreate(const std::string &Name, const std::string &Group, + const ObserveState &Expected, + ompt_data_t *EncounteringTaskData = expectedDefault(ompt_data_t *), + const ompt_frame_t *EncounteringTaskFrame = + expectedDefault(ompt_frame_t *), + ompt_data_t *NewTaskData = expectedDefault(ompt_data_t *), + int Flags = expectedDefault(int), + int HasDependences = expectedDefault(int), + const void *CodeptrRA = expectedDefault(const void *)); + + static OmptAssertEvent TaskSchedule(const std::string &Name, + const std::string &Group, + const ObserveState &Expected); + + static OmptAssertEvent + ImplicitTask(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData = expectedDefault(ompt_data_t *), + ompt_data_t *TaskData = expectedDefault(ompt_data_t *), + unsigned int ActualParallelism = expectedDefault(unsigned int), + unsigned int Index = expectedDefault(unsigned int), + int Flags = expectedDefault(int)); + + static OmptAssertEvent + SyncRegion(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_sync_region_t Kind, + ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData = expectedDefault(ompt_data_t *), + ompt_data_t *TaskData = expectedDefault(ompt_data_t *), + const void *CodeptrRA = expectedDefault(const void *)); + + static OmptAssertEvent + Target(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_t Kind, + ompt_scope_endpoint_t Endpoint, int DeviceNum = expectedDefault(int), + ompt_data_t *TaskData = expectedDefault(ompt_data_t *), + ompt_id_t TargetId = expectedDefault(ompt_id_t), + const void *CodeptrRA = expectedDefault(void *)); + + static OmptAssertEvent + TargetEmi(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_t Kind, + ompt_scope_endpoint_t Endpoint, + int DeviceNum = expectedDefault(int), + ompt_data_t *TaskData = expectedDefault(ompt_data_t *), + ompt_data_t *TargetTaskData = expectedDefault(ompt_data_t *), + ompt_data_t *TargetData = expectedDefault(ompt_data_t *), + const void *CodeptrRA = expectedDefault(void *)); + + static OmptAssertEvent + TargetDataOp(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_id_t TargetId, + ompt_id_t HostOpId, ompt_target_data_op_t OpType, void *SrcAddr, + int SrcDeviceNum, void *DstAddr, int DstDeviceNum, size_t Bytes, + const void *CodeptrRA); + + static OmptAssertEvent + TargetDataOp(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_data_op_t OpType, + size_t Bytes = expectedDefault(size_t), + void *SrcAddr = expectedDefault(void *), + void *DstAddr = expectedDefault(void *), + int SrcDeviceNum = expectedDefault(int), + int DstDeviceNum = expectedDefault(int), + ompt_id_t TargetId = expectedDefault(ompt_id_t), + ompt_id_t HostOpId = expectedDefault(ompt_id_t), + const void *CodeptrRA = expectedDefault(void *)); + + static OmptAssertEvent + TargetDataOpEmi(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetTaskData, ompt_data_t *TargetData, + ompt_id_t *HostOpId, ompt_target_data_op_t OpType, + void *SrcAddr, int SrcDeviceNum, void *DstAddr, + int DstDeviceNum, size_t Bytes, const void *CodeptrRA); + + static OmptAssertEvent + TargetDataOpEmi(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_data_op_t OpType, + ompt_scope_endpoint_t Endpoint, + size_t Bytes = expectedDefault(size_t), + void *SrcAddr = expectedDefault(void *), + void *DstAddr = expectedDefault(void *), + int SrcDeviceNum = expectedDefault(int), + int DstDeviceNum = expectedDefault(int), + ompt_data_t *TargetTaskData = expectedDefault(ompt_data_t *), + ompt_data_t *TargetData = expectedDefault(ompt_data_t *), + ompt_id_t *HostOpId = expectedDefault(ompt_id_t *), + const void *CodeptrRA = expectedDefault(void *)); + + static OmptAssertEvent TargetSubmit(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_id_t TargetId, ompt_id_t HostOpId, + unsigned int RequestedNumTeams); + + static OmptAssertEvent + TargetSubmit(const std::string &Name, const std::string &Group, + const ObserveState &Expected, unsigned int RequestedNumTeams, + ompt_id_t TargetId = expectedDefault(ompt_id_t), + ompt_id_t HostOpId = expectedDefault(ompt_id_t)); + + static OmptAssertEvent + TargetSubmitEmi(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetData, ompt_id_t *HostOpId, + unsigned int RequestedNumTeams); + + static OmptAssertEvent + TargetSubmitEmi(const std::string &Name, const std::string &Group, + const ObserveState &Expected, unsigned int RequestedNumTeams, + ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetData = expectedDefault(ompt_data_t *), + ompt_id_t *HostOpId = expectedDefault(ompt_id_t *)); + + static OmptAssertEvent ControlTool(const std::string &Name, + const std::string &Group, + const ObserveState &Expected); + + static OmptAssertEvent DeviceInitialize( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, int DeviceNum, + const char *Type = expectedDefault(const char *), + ompt_device_t *Device = expectedDefault(ompt_device_t *), + ompt_function_lookup_t LookupFn = expectedDefault(ompt_function_lookup_t), + const char *DocumentationStr = expectedDefault(const char *)); + + static OmptAssertEvent DeviceFinalize(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + int DeviceNum); + + static OmptAssertEvent + DeviceLoad(const std::string &Name, const std::string &Group, + const ObserveState &Expected, int DeviceNum, + const char *Filename = expectedDefault(const char *), + int64_t OffsetInFile = expectedDefault(int64_t), + void *VmaInFile = expectedDefault(void *), + size_t Bytes = expectedDefault(size_t), + void *HostAddr = expectedDefault(void *), + void *DeviceAddr = expectedDefault(void *), + uint64_t ModuleId = expectedDefault(int64_t)); + + static OmptAssertEvent DeviceUnload(const std::string &Name, + const std::string &Group, + const ObserveState &Expected); + + static OmptAssertEvent BufferRequest(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + int DeviceNum, ompt_buffer_t **Buffer, + size_t *Bytes); + + static OmptAssertEvent + BufferComplete(const std::string &Name, const std::string &Group, + const ObserveState &Expected, int DeviceNum, + ompt_buffer_t *Buffer, size_t Bytes, + ompt_buffer_cursor_t Begin, int BufferOwned); + + static OmptAssertEvent BufferRecord(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_record_ompt_t *Record); + + /// Handle type = ompt_record_target_t + static OmptAssertEvent + BufferRecord(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + ompt_target_t Kind, ompt_scope_endpoint_t Endpoint, + int DeviceNum = expectedDefault(int), + ompt_id_t TaskId = expectedDefault(ompt_id_t), + ompt_id_t TargetId = expectedDefault(ompt_id_t), + const void *CodeptrRA = expectedDefault(void *)); + + /// Handle type = ompt_callback_target_data_op + static OmptAssertEvent + BufferRecord(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + ompt_target_data_op_t OpType, size_t Bytes, + std::pair Timeframe, + void *SrcAddr = expectedDefault(void *), + void *DstAddr = expectedDefault(void *), + int SrcDeviceNum = expectedDefault(int), + int DstDeviceNum = expectedDefault(int), + ompt_id_t TargetId = expectedDefault(ompt_id_t), + ompt_id_t HostOpId = expectedDefault(ompt_id_t), + const void *CodeptrRA = expectedDefault(void *)); + + /// Handle type = ompt_callback_target_data_op + static OmptAssertEvent BufferRecord( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + ompt_target_data_op_t OpType, size_t Bytes = expectedDefault(size_t), + ompt_device_time_t MinimumTimeDelta = expectedDefault(ompt_device_time_t), + void *SrcAddr = expectedDefault(void *), + void *DstAddr = expectedDefault(void *), + int SrcDeviceNum = expectedDefault(int), + int DstDeviceNum = expectedDefault(int), + ompt_id_t TargetId = expectedDefault(ompt_id_t), + ompt_id_t HostOpId = expectedDefault(ompt_id_t), + const void *CodeptrRA = expectedDefault(void *)); + + /// Handle type = ompt_callback_target_submit + static OmptAssertEvent + BufferRecord(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + std::pair Timeframe, + unsigned int RequestedNumTeams = expectedDefault(unsigned int), + unsigned int GrantedNumTeams = expectedDefault(unsigned int), + ompt_id_t TargetId = expectedDefault(ompt_id_t), + ompt_id_t HostOpId = expectedDefault(ompt_id_t)); + + /// Handle type = ompt_callback_target_submit + /// Note: This will also act as the simplest default CTOR + static OmptAssertEvent BufferRecord( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + ompt_device_time_t MinimumTimeDelta = expectedDefault(ompt_device_time_t), + unsigned int RequestedNumTeams = expectedDefault(unsigned int), + unsigned int GrantedNumTeams = expectedDefault(unsigned int), + ompt_id_t TargetId = expectedDefault(ompt_id_t), + ompt_id_t HostOpId = expectedDefault(ompt_id_t)); + + static OmptAssertEvent BufferRecordDeallocation(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_buffer_t *Buffer); + + /// Allow move construction (due to std::unique_ptr) + OmptAssertEvent(OmptAssertEvent &&o) = default; + OmptAssertEvent &operator=(OmptAssertEvent &&o) = default; + + /// Get the event's name + std::string getEventName() const; + + /// Get the event's group name + std::string getEventGroup() const; + + /// Get the event's expected observation state + ObserveState getEventExpectedState() const; + + /// Return the actual event type enum value + internal::EventTy getEventType() const; + + /// Get a pointer to the internal event + internal::InternalEvent *getEvent() const; + + /// Make events comparable + friend bool operator==(const OmptAssertEvent &A, const OmptAssertEvent &B); + + /// Returns the string representation of the event + std::string toString(bool PrefixEventName = false) const; + +private: + OmptAssertEvent(const std::string &Name, const std::string &Group, + const ObserveState &Expected, internal::InternalEvent *IE); + OmptAssertEvent(const OmptAssertEvent &o) = delete; + + /// Determine the event name. Either it is provided directly or determined + /// from the calling function's name. + static std::string getName(const std::string &Name, + const char *Caller = __builtin_FUNCTION()) { + std::string EName = Name; + if (EName.empty()) + EName.append(Caller).append(" (auto generated)"); + + return EName; + } + + /// Determine the event name. Either it is provided directly or "default". + static std::string getGroup(const std::string &Group) { + if (Group.empty()) + return "default"; + + return Group; + } + + std::string Name; + std::string Group; + ObserveState ExpectedState; + std::unique_ptr TheEvent; +}; + +/// POD type, which holds the target region id, corresponding to an event group. +struct AssertEventGroup { + AssertEventGroup(uint64_t TargetRegion) : TargetRegion(TargetRegion) {} + uint64_t TargetRegion; +}; + +bool operator==(const OmptAssertEvent &A, const OmptAssertEvent &B); + +} // namespace omptest + +#endif diff --git a/openmp/tools/omptest/include/OmptAsserter.h b/openmp/tools/omptest/include/OmptAsserter.h new file mode 100644 index 0000000000000..64cbb5f3642f9 --- /dev/null +++ b/openmp/tools/omptest/include/OmptAsserter.h @@ -0,0 +1,291 @@ +//===- OmptAsserter.h - Asserter-related classes, enums, etc. ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Contains all asserter-related class declarations and important enums. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTASSERTER_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTASSERTER_H + +#include "Logging.h" +#include "OmptAssertEvent.h" + +#include +#include +#include +#include +#include +#include + +namespace omptest { + +// Forward declaration. +class OmptEventGroupInterface; + +enum class AssertMode { strict, relaxed }; +enum class AssertState { pass, fail }; + +/// General base class for the subscriber/notification pattern in +/// OmptCallbachHandler. Derived classes need to implement the notify method. +class OmptListener { +public: + virtual ~OmptListener() = default; + + /// Called for each registered OMPT event of the OmptCallbackHandler + virtual void notify(omptest::OmptAssertEvent &&AE) = 0; + + /// Control whether this asserter should be considered 'active'. + void setActive(bool Enabled); + + /// Check if this asserter is considered 'active'. + bool isActive(); + + /// Check if the given event type is in the set of suppressed event types. + bool isSuppressedEventType(omptest::internal::EventTy EvTy); + + /// Remove the given event type to the set of suppressed events. + void permitEvent(omptest::internal::EventTy EvTy); + + /// Add the given event type to the set of suppressed events. + void suppressEvent(omptest::internal::EventTy EvTy); + +private: + bool Active{true}; + + // Add event types to the set of suppressed events by default. + std::set SuppressedEvents{ + omptest::internal::EventTy::ThreadBegin, + omptest::internal::EventTy::ThreadEnd, + omptest::internal::EventTy::ParallelBegin, + omptest::internal::EventTy::ParallelEnd, + omptest::internal::EventTy::Work, + omptest::internal::EventTy::Dispatch, + omptest::internal::EventTy::TaskCreate, + omptest::internal::EventTy::Dependences, + omptest::internal::EventTy::TaskDependence, + omptest::internal::EventTy::TaskSchedule, + omptest::internal::EventTy::ImplicitTask, + omptest::internal::EventTy::Masked, + omptest::internal::EventTy::SyncRegion, + omptest::internal::EventTy::MutexAcquire, + omptest::internal::EventTy::Mutex, + omptest::internal::EventTy::NestLock, + omptest::internal::EventTy::Flush, + omptest::internal::EventTy::Cancel}; +}; + +/// Base class for asserting on OMPT events +class OmptAsserter : public OmptListener { +public: + OmptAsserter(); + virtual ~OmptAsserter() = default; + + /// Add an event to the asserter's internal data structure. + virtual void insert(omptest::OmptAssertEvent &&AE); + + /// Called from the CallbackHandler with a corresponding AssertEvent to which + /// callback was handled. + void notify(omptest::OmptAssertEvent &&AE) override; + + /// Implemented in subclasses to implement what should actually be done with + /// the notification. + virtual void notifyImpl(omptest::OmptAssertEvent &&AE) = 0; + + /// Get the number of currently remaining events, with: ObserveState::always. + virtual size_t getRemainingEventCount() = 0; + + /// Get the total number of received, effective notifications. + int getNotificationCount() { return NumNotifications; } + + /// Get the total number of successful assertion checks. + int getSuccessfulAssertionCount() { return NumSuccessfulAsserts; } + + /// Get the asserter's current operationmode: e.g.: strict or relaxed. + AssertMode getOperationMode() { return OperationMode; } + + /// Return the asserter's current state. + omptest::AssertState getState() { return State; } + + /// Determine and return the asserter's state. + virtual omptest::AssertState checkState(); + + /// Accessor for the event group interface. + std::shared_ptr getEventGroups() const { + return EventGroups; + } + + /// Accessor for the event group interface. + std::shared_ptr getLog() const { return Log; } + + /// Check the observed events' group association. If the event indicates the + /// begin/end of an OpenMP target region, we will create/deprecate the + /// expected event's group. Return true if the expected event group exists + /// (and is active), otherwise: false. Note: BufferRecords may also match with + /// deprecated groups as they may be delivered asynchronously. + bool verifyEventGroups(const omptest::OmptAssertEvent &ExpectedEvent, + const omptest::OmptAssertEvent &ObservedEvent); + + /// Set the asserter's mode of operation w.r.t. assertion. + void setOperationMode(AssertMode Mode); + + /// The total number of effective notifications. For example, if specific + /// notifications are to be ignored, they will not count towards this total. + int NumNotifications{0}; + + /// The number of successful assertion checks. + int NumSuccessfulAsserts{0}; + +protected: + /// The asserter's current state. + omptest::AssertState State{omptest::AssertState::pass}; + + /// Mutex to avoid data races w.r.t. event notifications and/or insertions. + std::mutex AssertMutex; + + /// Pointer to the OmptEventGroupInterface. + std::shared_ptr EventGroups{nullptr}; + + /// Pointer to the logging instance. + std::shared_ptr Log{nullptr}; + + /// Operation mode during assertion / notification. + AssertMode OperationMode{AssertMode::strict}; + +private: + /// Mutex for creating/accessing the singleton members + static std::mutex StaticMemberAccessMutex; + + /// Static member to manage the singleton event group interface instance + static std::weak_ptr EventGroupInterfaceInstance; + + /// Static member to manage the singleton logging instance + static std::weak_ptr LoggingInstance; +}; + +/// Class that can assert in a sequenced fashion, i.e., events have to occur in +/// the order they were registered +class OmptSequencedAsserter : public OmptAsserter { +public: + OmptSequencedAsserter() : OmptAsserter(), NextEvent(0) {} + + /// Add the event to the in-sequence set of events that the asserter should + /// check for. + void insert(omptest::OmptAssertEvent &&AE) override; + + /// Implements the asserter's actual logic + virtual void notifyImpl(omptest::OmptAssertEvent &&AE) override; + + size_t getRemainingEventCount() override; + + omptest::AssertState checkState() override; + + bool AssertionSuspended{false}; + +protected: + /// Notification helper function, implementing SyncPoint logic. Returns true + /// in case of consumed event, indicating early exit of notification. + bool consumeSyncPoint(const omptest::OmptAssertEvent &AE); + + /// Notification helper function, implementing excess event notification + /// logic. Returns true when no more events were expected, indicating early + /// exit of notification. + bool checkExcessNotify(const omptest::OmptAssertEvent &AE); + + /// Notification helper function, implementing Suspend logic. Returns true + /// in case of consumed event, indicating early exit of notification. + bool consumeSuspend(); + + /// Notification helper function, implementing regular event notification + /// logic. Returns true when a matching event was encountered, indicating + /// early exit of notification. + bool consumeRegularEvent(const omptest::OmptAssertEvent &AE); + +public: + /// Index of the next, expected event. + size_t NextEvent{0}; + std::vector Events{}; +}; + +/// Class that asserts with set semantics, i.e., unordered +struct OmptEventAsserter : public OmptAsserter { + OmptEventAsserter() : OmptAsserter(), NumEvents(0), Events() {} + + /// Add the event to the set of events that the asserter should check for. + void insert(omptest::OmptAssertEvent &&AE) override; + + /// Implements the asserter's logic + virtual void notifyImpl(omptest::OmptAssertEvent &&AE) override; + + size_t getRemainingEventCount() override; + + omptest::AssertState checkState() override; + + size_t NumEvents{0}; + + /// For now use vector (but do set semantics) + // TODO std::unordered_set? + std::vector Events{}; +}; + +/// Class that reports the occurred events +class OmptEventReporter : public OmptListener { +public: + OmptEventReporter(std::ostream &OutStream = std::cout) + : OutStream(OutStream) {} + + /// Called from the CallbackHandler with a corresponding AssertEvent to which + /// callback was handled. + void notify(omptest::OmptAssertEvent &&AE) override; + +private: + std::ostream &OutStream; +}; + +/// This class provides the members and methods to manage event groups and +/// SyncPoints in conjunction with asserters. Most importantly it maintains a +/// coherent view of active and past events or SyncPoints. +class OmptEventGroupInterface { +public: + OmptEventGroupInterface() = default; + ~OmptEventGroupInterface() = default; + + /// Non-copyable and non-movable + OmptEventGroupInterface(const OmptEventGroupInterface &) = delete; + OmptEventGroupInterface &operator=(const OmptEventGroupInterface &) = delete; + OmptEventGroupInterface(OmptEventGroupInterface &&) = delete; + OmptEventGroupInterface &operator=(OmptEventGroupInterface &&) = delete; + + /// Add given group to the set of active event groups. Effectively connecting + /// the given groupname (expected) with a target region id (observed). + bool addActiveEventGroup(const std::string &GroupName, + omptest::AssertEventGroup Group); + + /// Move given group from the set of active event groups to the set of + /// previously active event groups. + bool deprecateActiveEventGroup(const std::string &GroupName); + + /// Check if given group is currently part of the active event groups. + bool checkActiveEventGroups(const std::string &GroupName, + omptest::AssertEventGroup Group); + + /// Check if given group is currently part of the deprecated event groups. + bool checkDeprecatedEventGroups(const std::string &GroupName, + omptest::AssertEventGroup Group); + +private: + mutable std::mutex GroupMutex; + std::map ActiveEventGroups{}; + std::map DeprecatedEventGroups{}; + std::set EncounteredSyncPoints{}; +}; + +} // namespace omptest + +#endif diff --git a/openmp/tools/omptest/include/OmptCallbackHandler.h b/openmp/tools/omptest/include/OmptCallbackHandler.h new file mode 100644 index 0000000000000..40076c386107e --- /dev/null +++ b/openmp/tools/omptest/include/OmptCallbackHandler.h @@ -0,0 +1,165 @@ +//===- OmptCallbackHandler.h - Callback reception and handling --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides the OMPT callback handling declarations. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTCALLBACKHANDLER_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTCALLBACKHANDLER_H + +#include "OmptAssertEvent.h" +#include "OmptAsserter.h" + +#include "omp-tools.h" + +#include + +namespace omptest { + +/// Handler class to do whatever is needed to be done when a callback is invoked +/// by the OMP runtime +/// Supports a RecordAndReplay mechanism in which all OMPT events are recorded +/// and then replayed. This is so that a test can assert on, e.g., a device +/// initialize event, even though this would occur before a unit test is +/// actually executed. +class OmptCallbackHandler { +public: + ~OmptCallbackHandler() = default; + + /// Singleton handler + static OmptCallbackHandler &get(); + + /// Subscribe a listener to be notified for OMPT events + void subscribe(OmptListener *Listener); + + /// Remove all subscribers + void clearSubscribers(); + + /// When the record and replay mechanism is enabled this replays all OMPT + /// events + void replay(); + + /// Special asserter callback which checks that upon encountering the + /// synchronization point, all expected events have been processed. That is: + /// there are currently no remaining expected events for any asserter. + void handleAssertionSyncPoint(const std::string &SyncPointName); + + void handleThreadBegin(ompt_thread_t ThreadType, ompt_data_t *ThreadData); + + void handleThreadEnd(ompt_data_t *ThreadData); + + void handleTaskCreate(ompt_data_t *EncounteringTaskData, + const ompt_frame_t *EncounteringTaskFrame, + ompt_data_t *NewTaskData, int Flags, int HasDependences, + const void *CodeptrRA); + + void handleTaskSchedule(ompt_data_t *PriorTaskData, + ompt_task_status_t PriorTaskStatus, + ompt_data_t *NextTaskData); + + void handleImplicitTask(ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData, ompt_data_t *TaskData, + unsigned int ActualParallelism, unsigned int Index, + int Flags); + + void handleParallelBegin(ompt_data_t *EncounteringTaskData, + const ompt_frame_t *EncounteringTaskFrame, + ompt_data_t *ParallelData, + unsigned int RequestedParallelism, int Flags, + const void *CodeptrRA); + + void handleParallelEnd(ompt_data_t *ParallelData, + ompt_data_t *EncounteringTaskData, int Flags, + const void *CodeptrRA); + + void handleDeviceInitialize(int DeviceNum, const char *Type, + ompt_device_t *Device, + ompt_function_lookup_t LookupFn, + const char *DocumentationStr); + + void handleDeviceFinalize(int DeviceNum); + + void handleTarget(ompt_target_t Kind, ompt_scope_endpoint_t Endpoint, + int DeviceNum, ompt_data_t *TaskData, ompt_id_t TargetId, + const void *CodeptrRA); + + void handleTargetEmi(ompt_target_t Kind, ompt_scope_endpoint_t Endpoint, + int DeviceNum, ompt_data_t *TaskData, + ompt_data_t *TargetTaskData, ompt_data_t *TargetData, + const void *CodeptrRA); + + void handleTargetSubmit(ompt_id_t TargetId, ompt_id_t HostOpId, + unsigned int RequestedNumTeams); + + void handleTargetSubmitEmi(ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetData, ompt_id_t *HostOpId, + unsigned int RequestedNumTeams); + + void handleTargetDataOp(ompt_id_t TargetId, ompt_id_t HostOpId, + ompt_target_data_op_t OpType, void *SrcAddr, + int SrcDeviceNum, void *DstAddr, int DstDeviceNum, + size_t Bytes, const void *CodeptrRA); + + void handleTargetDataOpEmi(ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetTaskData, + ompt_data_t *TargetData, ompt_id_t *HostOpId, + ompt_target_data_op_t OpType, void *SrcAddr, + int SrcDeviceNum, void *DstAddr, int DstDeviceNum, + size_t Bytes, const void *CodeptrRA); + + void handleDeviceLoad(int DeviceNum, const char *Filename, + int64_t OffsetInFile, void *VmaInFile, size_t Bytes, + void *HostAddr, void *DeviceAddr, uint64_t ModuleId); + + void handleDeviceUnload(int DeviceNum, uint64_t ModuleId); + + void handleBufferRequest(int DeviceNum, ompt_buffer_t **Buffer, + size_t *Bytes); + + void handleBufferComplete(int DeviceNum, ompt_buffer_t *Buffer, size_t Bytes, + ompt_buffer_cursor_t Begin, int BufferOwned); + + void handleBufferRecord(ompt_record_ompt_t *Record); + + void handleBufferRecordDeallocation(ompt_buffer_t *Buffer); + + /// Not needed for a conforming minimal OMPT implementation + void handleWork(ompt_work_t WorkType, ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData, ompt_data_t *TaskData, + uint64_t Count, const void *CodeptrRA); + + void handleDispatch(ompt_data_t *ParallelData, ompt_data_t *TaskData, + ompt_dispatch_t Kind, ompt_data_t Instance); + + void handleSyncRegion(ompt_sync_region_t Kind, ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData, ompt_data_t *TaskData, + const void *CodeptrRA); + +private: + /// Wrapper around emplace_back for potential additional logging / checking or + /// so + void recordEvent(OmptAssertEvent &&Event); + + /// Listeners to be notified + std::vector Subscribers; + + /// Toggle if OMPT events should notify subscribers immediately or not + bool RecordAndReplay{false}; + + /// Recorded events in Record and Replay mode + std::vector RecordedEvents; +}; + +} // namespace omptest + +// Pointer to global callback handler +extern omptest::OmptCallbackHandler *Handler; + +#endif diff --git a/openmp/tools/omptest/include/OmptTester.h b/openmp/tools/omptest/include/OmptTester.h new file mode 100644 index 0000000000000..155e61d5f7482 --- /dev/null +++ b/openmp/tools/omptest/include/OmptTester.h @@ -0,0 +1,60 @@ +//===- OmptTester.h - Main header for ompTest usage -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file represents the main header file for usage of the ompTest library. +/// Depending on the build either 'standalone' or GoogleTest headers are +/// included and corresponding main-function macros are defined. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTER_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTER_H + +#include "AssertMacros.h" +#include "Logging.h" +#include "OmptAliases.h" +#include "OmptAssertEvent.h" +#include "OmptAsserter.h" +#include "OmptCallbackHandler.h" + +#include +#include +#include +#include +#include +#include +#include + +// Standalone header section +#ifdef OPENMP_LIBOMPTEST_BUILD_STANDALONE + +#include "OmptTesterStandalone.h" + +// Define standalone main function (place once at the bottom of a testsuite) +#define OMPTEST_TESTSUITE_MAIN() \ + int main(int argc, char **argv) { \ + Runner R; \ + return R.run(); \ + } + +// GoogleTest header section +#else + +#include "OmptTesterGoogleTest.h" + +// Define GoogleTest main function (place once at the bottom of a testsuite) +#define OMPTEST_TESTSUITE_MAIN() \ + int main(int argc, char **argv) { \ + testing::InitGoogleTest(&argc, argv); \ + return RUN_ALL_TESTS(); \ + } + +#endif + +#endif diff --git a/openmp/tools/omptest/include/OmptTesterGlobals.h b/openmp/tools/omptest/include/OmptTesterGlobals.h new file mode 100644 index 0000000000000..62f443aed80e0 --- /dev/null +++ b/openmp/tools/omptest/include/OmptTesterGlobals.h @@ -0,0 +1,36 @@ +//===- OmptTesterGlobals.h - Global function declarations -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Contains global function declarations, esp. for OMPT symbols. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTERGLOBALS_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTERGLOBALS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif +ompt_start_tool_result_t *ompt_start_tool(unsigned int omp_version, + const char *runtime_version); +int start_trace(ompt_device_t *Device); +int flush_trace(ompt_device_t *Device); +// Function which calls flush_trace(ompt_device_t *) on all traced devices. +int flush_traced_devices(); +int stop_trace(ompt_device_t *Device); +// Function which calls stop_trace(ompt_device_t *) on all traced devices. +int stop_trace_devices(); +void libomptest_global_eventreporter_set_active(bool State); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/openmp/tools/omptest/include/OmptTesterGoogleTest.h b/openmp/tools/omptest/include/OmptTesterGoogleTest.h new file mode 100644 index 0000000000000..51b94bc678f50 --- /dev/null +++ b/openmp/tools/omptest/include/OmptTesterGoogleTest.h @@ -0,0 +1,86 @@ +//===- OmptTesterGoogleTest.h - GoogleTest header variant -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file represents the GoogleTest-based header variant, defining the +/// actual test classes and their behavior. +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTERGOOGLETEST_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTERGOOGLETEST_H + +#include "AssertMacros.h" +#include "OmptAliases.h" +#include "OmptAssertEvent.h" +#include "OmptAsserter.h" +#include "OmptCallbackHandler.h" +#include "OmptTesterGlobals.h" + +// This will allow us to override the "TEST" macro of gtest +#define GTEST_DONT_DEFINE_TEST 1 +#include "gtest/gtest.h" + +namespace testing { +class GTEST_API_ OmptTestCase : public testing::Test, + public omptest::OmptEventGroupInterface { +public: + std::unique_ptr SequenceAsserter = + std::make_unique(); + std::unique_ptr SetAsserter = + std::make_unique(); + std::unique_ptr EventReporter = + std::make_unique(); + +protected: + void SetUp() override { + omptest::OmptCallbackHandler::get().subscribe(SequenceAsserter.get()); + omptest::OmptCallbackHandler::get().subscribe(SetAsserter.get()); + omptest::OmptCallbackHandler::get().subscribe(EventReporter.get()); + } + + void TearDown() override { + // Actively flush potential in-flight trace records + flush_traced_devices(); + + // Remove subscribers to not be notified of events after test execution. + omptest::OmptCallbackHandler::get().clearSubscribers(); + + // This common testcase must not encounter any failures. + if (SequenceAsserter->checkState() == omptest::AssertState::fail || + SetAsserter->checkState() == omptest::AssertState::fail) + ADD_FAILURE(); + } +}; + +class GTEST_API_ OmptTestCaseXFail : public testing::OmptTestCase { +protected: + void TearDown() override { + // Actively flush potential in-flight trace records + flush_traced_devices(); + + // Remove subscribers to not be notified of events after test execution. + omptest::OmptCallbackHandler::get().clearSubscribers(); + + // This eXpectedly failing testcase has to encounter at least one failure. + if (SequenceAsserter->checkState() == omptest::AssertState::pass && + SetAsserter->checkState() == omptest::AssertState::pass) + ADD_FAILURE(); + } +}; +} // namespace testing + +#define TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::OmptTestCase, \ + ::testing::internal::GetTypeId<::testing::OmptTestCase>()) + +#define TEST_XFAIL(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::OmptTestCaseXFail, \ + ::testing::internal::GetTypeId<::testing::OmptTestCaseXFail>()) + +#endif // include guard diff --git a/openmp/tools/omptest/include/OmptTesterStandalone.h b/openmp/tools/omptest/include/OmptTesterStandalone.h new file mode 100644 index 0000000000000..06649031c5d1c --- /dev/null +++ b/openmp/tools/omptest/include/OmptTesterStandalone.h @@ -0,0 +1,123 @@ +//===- OmptTesterStandalone.h - Standalone header variant -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file represents the 'standalone' header variant, defining the actual +/// test classes and their behavior (it does not have external dependencies). +/// +//===----------------------------------------------------------------------===// + +#ifndef OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTERSTANDALONE_H +#define OPENMP_TOOLS_OMPTEST_INCLUDE_OMPTTESTERSTANDALONE_H + +#include "OmptAssertEvent.h" +#include "OmptAsserter.h" +#include "OmptTesterGlobals.h" + +#include +#include + +// Forward declarations. +namespace omptest { +struct OmptEventAsserter; +class OmptEventReporter; +class OmptSequencedAsserter; +} // namespace omptest + +struct Error { + operator bool() { return Fail; } + bool Fail; +}; + +/// A pretty crude test case abstraction +struct TestCase { + TestCase(const std::string &name) + : IsDisabled(name.rfind("DISABLED_", 0) == 0), Name(name) {} + TestCase(const std::string &name, const omptest::AssertState &expected) + : IsDisabled(name.rfind("DISABLED_", 0) == 0), Name(name), + ExpectedState(expected) {} + virtual ~TestCase() = default; + Error exec(); + virtual void execImpl() { assert(false && "Allocating base class"); } + + bool IsDisabled{false}; + std::string Name; + omptest::AssertState ExpectedState{omptest::AssertState::pass}; + omptest::AssertState ResultState{omptest::AssertState::pass}; + + std::unique_ptr SequenceAsserter = + std::make_unique(); + std::unique_ptr SetAsserter = + std::make_unique(); + std::unique_ptr EventReporter = + std::make_unique(); +}; +/// A pretty crude test suite abstraction +struct TestSuite { + using TestCaseVec = std::vector>; + std::string Name; + TestSuite() = default; + TestSuite(const TestSuite &O) = delete; + TestSuite(TestSuite &&O); + void setup(); + void teardown(); + TestCaseVec::iterator begin(); + TestCaseVec::iterator end(); + TestCaseVec TestCases; +}; +/// Static class used to register all test cases and provide them to the driver +class TestRegistrar { +public: + static TestRegistrar &get(); + static std::vector getTestSuites(); + static void addCaseToSuite(TestCase *TC, std::string TSName); + +private: + TestRegistrar() = default; + TestRegistrar(const TestRegistrar &o) = delete; + TestRegistrar operator=(const TestRegistrar &o) = delete; + // Keep tests in order 'of appearance' (top -> bottom), avoid unordered_map + static std::map Tests; +}; +/// Hack to register test cases +struct Registerer { + Registerer(TestCase *TC, const std::string SuiteName); +}; +/// Eventually executes all test suites and cases, should contain logic to skip +/// stuff if needed +struct Runner { + Runner() : TestSuites(TestRegistrar::get().getTestSuites()) {} + int run(); + void reportError(const Error &Err); + void abortOrKeepGoing(); + // Print an execution summary of all testsuites and their corresponding + // testcases. + void printSummary(); + std::vector TestSuites; +}; + +/// MACROS TO DEFINE A TESTSUITE + TESTCASE (like GoogleTest does) +#define XQUOTE(str) QUOTE(str) +#define QUOTE(str) #str + +#define TEST_TEMPLATE(SuiteName, CaseName, ExpectedState) \ + struct SuiteName##_##CaseName : public TestCase { \ + SuiteName##_##CaseName() \ + : TestCase(XQUOTE(CaseName), omptest::AssertState::ExpectedState) {} \ + virtual void execImpl() override; \ + }; \ + static Registerer R_##SuiteName##CaseName(new SuiteName##_##CaseName(), \ + #SuiteName); \ + void SuiteName##_##CaseName::execImpl() + +#define TEST(SuiteName, CaseName) \ + TEST_TEMPLATE(SuiteName, CaseName, /*ExpectedState=*/pass) +#define TEST_XFAIL(SuiteName, CaseName) \ + TEST_TEMPLATE(SuiteName, CaseName, /*ExpectedState=*/fail) + +#endif diff --git a/openmp/tools/omptest/src/InternalEvent.cpp b/openmp/tools/omptest/src/InternalEvent.cpp new file mode 100644 index 0000000000000..87daf5a6a31ba --- /dev/null +++ b/openmp/tools/omptest/src/InternalEvent.cpp @@ -0,0 +1,367 @@ +//===- InternalEvent.cpp - Internal event implementation --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implements internal event representation methods and helper functions. +/// +//===----------------------------------------------------------------------===// + +#include "InternalEvent.h" + +#include +#include + +using namespace omptest; +using namespace util; + +std::string util::makeHexString(uint64_t Data, bool IsPointer, size_t MinBytes, + bool ShowHexBase) { + if (Data == 0 && IsPointer) + return "(nil)"; + + thread_local std::ostringstream os; + // Clear the content of the stream + os.str(std::string()); + + // Manually prefixing "0x" will make the use of std::setfill more easy + if (ShowHexBase) + os << "0x"; + + // Default to 32bit (8 hex digits) width, if exceeding 64bit or zero value + size_t NumDigits = (MinBytes > 0 && MinBytes < 9) ? (MinBytes << 1) : 8; + + if (MinBytes > 0) + os << std::setfill('0') << std::setw(NumDigits); + + os << std::hex << Data; + return os.str(); +} + +std::string internal::AssertionSyncPoint::toString() const { + std::string S{"Assertion SyncPoint: '"}; + S.append(Name).append(1, '\''); + return S; +} + +std::string internal::ThreadBegin::toString() const { + std::string S{"OMPT Callback ThreadBegin: "}; + S.append("ThreadType=").append(std::to_string(ThreadType)); + return S; +} + +std::string internal::ThreadEnd::toString() const { + std::string S{"OMPT Callback ThreadEnd"}; + return S; +} + +std::string internal::ParallelBegin::toString() const { + std::string S{"OMPT Callback ParallelBegin: "}; + S.append("NumThreads=").append(std::to_string(NumThreads)); + return S; +} + +std::string internal::ParallelEnd::toString() const { + // TODO: Should we expose more detailed info here? + std::string S{"OMPT Callback ParallelEnd"}; + return S; +} + +std::string internal::Work::toString() const { + std::string S{"OMPT Callback Work: "}; + S.append("work_type=").append(std::to_string(WorkType)); + S.append(" endpoint=").append(std::to_string(Endpoint)); + S.append(" parallel_data=").append(makeHexString((uint64_t)ParallelData)); + S.append(" task_data=").append(makeHexString((uint64_t)TaskData)); + S.append(" count=").append(std::to_string(Count)); + S.append(" codeptr=").append(makeHexString((uint64_t)CodeptrRA)); + return S; +} + +std::string internal::Dispatch::toString() const { + std::string S{"OMPT Callback Dispatch: "}; + S.append("parallel_data=").append(makeHexString((uint64_t)ParallelData)); + S.append(" task_data=").append(makeHexString((uint64_t)TaskData)); + S.append(" kind=").append(std::to_string(Kind)); + // TODO Check what to print for instance in all different cases + if (Kind == ompt_dispatch_iteration) { + S.append(" instance=[it=") + .append(std::to_string(Instance.value)) + .append(1, ']'); + } else if (Kind == ompt_dispatch_section) { + S.append(" instance=[ptr=") + .append(makeHexString((uint64_t)Instance.ptr)) + .append(1, ']'); + } else if ((Kind == ompt_dispatch_ws_loop_chunk || + Kind == ompt_dispatch_taskloop_chunk || + Kind == ompt_dispatch_distribute_chunk) && + Instance.ptr != nullptr) { + auto Chunk = static_cast(Instance.ptr); + S.append(" instance=[chunk=(start=") + .append(std::to_string(Chunk->start)) + .append(", iterations=") + .append(std::to_string(Chunk->iterations)) + .append(")]"); + } + return S; +} + +std::string internal::TaskCreate::toString() const { + std::string S{"OMPT Callback TaskCreate: "}; + S.append("encountering_task_data=") + .append(makeHexString((uint64_t)EncounteringTaskData)); + S.append(" encountering_task_frame=") + .append(makeHexString((uint64_t)EncounteringTaskFrame)); + S.append(" new_task_data=").append(makeHexString((uint64_t)NewTaskData)); + S.append(" flags=").append(std::to_string(Flags)); + S.append(" has_dependences=").append(std::to_string(HasDependences)); + S.append(" codeptr=").append(makeHexString((uint64_t)CodeptrRA)); + return S; +} + +std::string internal::ImplicitTask::toString() const { + std::string S{"OMPT Callback ImplicitTask: "}; + S.append("endpoint=").append(std::to_string(Endpoint)); + S.append(" parallel_data=").append(makeHexString((uint64_t)ParallelData)); + S.append(" task_data=").append(makeHexString((uint64_t)TaskData)); + S.append(" actual_parallelism=").append(std::to_string(ActualParallelism)); + S.append(" index=").append(std::to_string(Index)); + S.append(" flags=").append(std::to_string(Flags)); + return S; +} + +std::string internal::SyncRegion::toString() const { + std::string S{"OMPT Callback SyncRegion: "}; + S.append("kind=").append(std::to_string(Kind)); + S.append(" endpoint=").append(std::to_string(Endpoint)); + S.append(" parallel_data=").append(makeHexString((uint64_t)ParallelData)); + S.append(" task_data=").append(makeHexString((uint64_t)TaskData)); + S.append(" codeptr=").append(makeHexString((uint64_t)CodeptrRA)); + return S; +} + +std::string internal::Target::toString() const { + // TODO Should we canonicalize the string prefix (use "OMPT ..." everywhere)? + std::string S{"Callback Target: target_id="}; + S.append(std::to_string(TargetId)); + S.append(" kind=").append(std::to_string(Kind)); + S.append(" endpoint=").append(std::to_string(Endpoint)); + S.append(" device_num=").append(std::to_string(DeviceNum)); + S.append(" code=").append(makeHexString((uint64_t)CodeptrRA)); + return S; +} + +std::string internal::TargetEmi::toString() const { + // TODO Should we canonicalize the string prefix (use "OMPT ..." everywhere)? + std::string S{"Callback Target EMI: kind="}; + S.append(std::to_string(Kind)); + S.append(" endpoint=").append(std::to_string(Endpoint)); + S.append(" device_num=").append(std::to_string(DeviceNum)); + S.append(" task_data=").append(makeHexString((uint64_t)TaskData)); + S.append(" (") + .append(makeHexString((uint64_t)(TaskData) ? TaskData->value : 0, + /*IsPointer=*/false)) + .append(1, ')'); + S.append(" target_task_data=") + .append(makeHexString((uint64_t)TargetTaskData)); + S.append(" (") + .append( + makeHexString((uint64_t)(TargetTaskData) ? TargetTaskData->value : 0, + /*IsPointer=*/false)) + .append(1, ')'); + S.append(" target_data=").append(makeHexString((uint64_t)TargetData)); + S.append(" (") + .append(makeHexString((uint64_t)(TargetData) ? TargetData->value : 0, + /*IsPointer=*/false)) + .append(1, ')'); + S.append(" code=").append(makeHexString((uint64_t)CodeptrRA)); + return S; +} + +std::string internal::TargetDataOp::toString() const { + std::string S{" Callback DataOp: target_id="}; + S.append(std::to_string(TargetId)); + S.append(" host_op_id=").append(std::to_string(HostOpId)); + S.append(" optype=").append(std::to_string(OpType)); + S.append(" src=").append(makeHexString((uint64_t)SrcAddr)); + S.append(" src_device_num=").append(std::to_string(SrcDeviceNum)); + S.append(" dest=").append(makeHexString((uint64_t)DstAddr)); + S.append(" dest_device_num=").append(std::to_string(DstDeviceNum)); + S.append(" bytes=").append(std::to_string(Bytes)); + S.append(" code=").append(makeHexString((uint64_t)CodeptrRA)); + return S; +} + +std::string internal::TargetDataOpEmi::toString() const { + std::string S{" Callback DataOp EMI: endpoint="}; + S.append(std::to_string(Endpoint)); + S.append(" optype=").append(std::to_string(OpType)); + S.append(" target_task_data=") + .append(makeHexString((uint64_t)TargetTaskData)); + S.append(" (") + .append( + makeHexString((uint64_t)(TargetTaskData) ? TargetTaskData->value : 0, + /*IsPointer=*/false)) + .append(1, ')'); + S.append(" target_data=").append(makeHexString((uint64_t)TargetData)); + S.append(" (") + .append(makeHexString((uint64_t)(TargetData) ? TargetData->value : 0, + /*IsPointer=*/false)) + .append(1, ')'); + S.append(" host_op_id=").append(makeHexString((uint64_t)HostOpId)); + S.append(" (") + .append(makeHexString((uint64_t)(HostOpId) ? (*HostOpId) : 0, + /*IsPointer=*/false)) + .append(1, ')'); + S.append(" src=").append(makeHexString((uint64_t)SrcAddr)); + S.append(" src_device_num=").append(std::to_string(SrcDeviceNum)); + S.append(" dest=").append(makeHexString((uint64_t)DstAddr)); + S.append(" dest_device_num=").append(std::to_string(DstDeviceNum)); + S.append(" bytes=").append(std::to_string(Bytes)); + S.append(" code=").append(makeHexString((uint64_t)CodeptrRA)); + return S; +} + +std::string internal::TargetSubmit::toString() const { + std::string S{" Callback Submit: target_id="}; + S.append(std::to_string(TargetId)); + S.append(" host_op_id=").append(std::to_string(HostOpId)); + S.append(" req_num_teams=").append(std::to_string(RequestedNumTeams)); + return S; +} + +std::string internal::TargetSubmitEmi::toString() const { + std::string S{" Callback Submit EMI: endpoint="}; + S.append(std::to_string(Endpoint)); + S.append(" req_num_teams=").append(std::to_string(RequestedNumTeams)); + S.append(" target_data=").append(makeHexString((uint64_t)TargetData)); + S.append(" (") + .append(makeHexString((uint64_t)(TargetData) ? TargetData->value : 0, + /*IsPointer=*/false)) + .append(1, ')'); + S.append(" host_op_id=").append(makeHexString((uint64_t)HostOpId)); + S.append(" (") + .append(makeHexString((uint64_t)(HostOpId) ? (*HostOpId) : 0, + /*IsPointer=*/false)) + .append(1, ')'); + return S; +} + +std::string internal::DeviceInitialize::toString() const { + std::string S{"Callback Init: device_num="}; + S.append(std::to_string(DeviceNum)); + S.append(" type=").append((Type) ? Type : "(null)"); + S.append(" device=").append(makeHexString((uint64_t)Device)); + S.append(" lookup=").append(makeHexString((uint64_t)LookupFn)); + S.append(" doc=").append(makeHexString((uint64_t)DocStr)); + return S; +} + +std::string internal::DeviceFinalize::toString() const { + std::string S{"Callback Fini: device_num="}; + S.append(std::to_string(DeviceNum)); + return S; +} + +std::string internal::DeviceLoad::toString() const { + std::string S{"Callback Load: device_num:"}; + S.append(std::to_string(DeviceNum)); + S.append(" module_id:").append(std::to_string(ModuleId)); + S.append(" filename:").append((Filename == nullptr) ? "(null)" : Filename); + S.append(" host_adddr:").append(makeHexString((uint64_t)HostAddr)); + S.append(" device_addr:").append(makeHexString((uint64_t)DeviceAddr)); + S.append(" bytes:").append(std::to_string(Bytes)); + return S; +} + +std::string internal::BufferRequest::toString() const { + std::string S{"Allocated "}; + S.append(std::to_string((Bytes != nullptr) ? *Bytes : 0)) + .append(" bytes at "); + S.append(makeHexString((Buffer != nullptr) ? (uint64_t)*Buffer : 0)); + S.append(" in buffer request callback"); + return S; +} + +std::string internal::BufferComplete::toString() const { + std::string S{"Executing buffer complete callback: "}; + S.append(std::to_string(DeviceNum)).append(1, ' '); + S.append(makeHexString((uint64_t)Buffer)).append(1, ' '); + S.append(std::to_string(Bytes)).append(1, ' '); + S.append(makeHexString((uint64_t)Begin)).append(1, ' '); + S.append(std::to_string(BufferOwned)); + return S; +} + +std::string internal::BufferRecord::toString() const { + std::string S{""}; + std::string T{""}; + S.append("rec=").append(makeHexString((uint64_t)RecordPtr)); + S.append(" type=").append(std::to_string(Record.type)); + + T.append("time=").append(std::to_string(Record.time)); + T.append(" thread_id=").append(std::to_string(Record.thread_id)); + T.append(" target_id=").append(std::to_string(Record.target_id)); + + switch (Record.type) { + case ompt_callback_target: + case ompt_callback_target_emi: { + // Handle Target Record + ompt_record_target_t TR = Record.record.target; + S.append(" (Target task) ").append(T); + S.append(" kind=").append(std::to_string(TR.kind)); + S.append(" endpoint=").append(std::to_string(TR.endpoint)); + S.append(" device=").append(std::to_string(TR.device_num)); + S.append(" task_id=").append(std::to_string(TR.task_id)); + S.append(" codeptr=").append(makeHexString((uint64_t)TR.codeptr_ra)); + break; + } + case ompt_callback_target_data_op: + case ompt_callback_target_data_op_emi: { + // Handle Target DataOp Record + ompt_record_target_data_op_t TDR = Record.record.target_data_op; + S.append(" (Target data op) ").append(T); + S.append(" host_op_id=").append(std::to_string(TDR.host_op_id)); + S.append(" optype=").append(std::to_string(TDR.optype)); + S.append(" src_addr=").append(makeHexString((uint64_t)TDR.src_addr)); + S.append(" src_device=").append(std::to_string(TDR.src_device_num)); + S.append(" dest_addr=").append(makeHexString((uint64_t)TDR.dest_addr)); + S.append(" dest_device=").append(std::to_string(TDR.dest_device_num)); + S.append(" bytes=").append(std::to_string(TDR.bytes)); + S.append(" end_time=").append(std::to_string(TDR.end_time)); + S.append(" duration=").append(std::to_string(TDR.end_time - Record.time)); + S.append(" ns codeptr=").append(makeHexString((uint64_t)TDR.codeptr_ra)); + break; + } + case ompt_callback_target_submit: + case ompt_callback_target_submit_emi: { + // Handle Target Kernel Record + ompt_record_target_kernel_t TKR = Record.record.target_kernel; + S.append(" (Target kernel) ").append(T); + S.append(" host_op_id=").append(std::to_string(TKR.host_op_id)); + S.append(" requested_num_teams=") + .append(std::to_string(TKR.requested_num_teams)); + S.append(" granted_num_teams=") + .append(std::to_string(TKR.granted_num_teams)); + S.append(" end_time=").append(std::to_string(TKR.end_time)); + S.append(" duration=").append(std::to_string(TKR.end_time - Record.time)); + S.append(" ns"); + break; + } + default: + S.append(" (unsupported record type)"); + break; + } + + return S; +} + +std::string internal::BufferRecordDeallocation::toString() const { + std::string S{"Deallocated "}; + S.append(makeHexString((uint64_t)Buffer)); + return S; +} diff --git a/openmp/tools/omptest/src/InternalEventOperators.cpp b/openmp/tools/omptest/src/InternalEventOperators.cpp new file mode 100644 index 0000000000000..49c61a44a7aba --- /dev/null +++ b/openmp/tools/omptest/src/InternalEventOperators.cpp @@ -0,0 +1,366 @@ +//===- InternalEventOperators.cpp - Operator implementations ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the internal event operators, like comparators. +/// +//===----------------------------------------------------------------------===// + +#include "InternalEvent.h" + +namespace omptest { + +namespace internal { +// clang-format off +event_class_operator_stub(AssertionSyncPoint) +event_class_operator_stub(AssertionSuspend) +event_class_operator_stub(ThreadBegin) +event_class_operator_stub(ThreadEnd) +event_class_operator_w_body(ParallelBegin, \ + return Expected.NumThreads == Observed.NumThreads; \ +) +event_class_operator_stub(ParallelEnd) +event_class_operator_w_body(Work, \ + bool isSameWorkType = (Expected.WorkType == Observed.WorkType); \ + bool isSameEndpoint = (Expected.Endpoint == Observed.Endpoint); \ + bool isSameParallelData = \ + (Expected.ParallelData == expectedDefault(ompt_data_t *)) ? \ + true : (Expected.ParallelData == Observed.ParallelData); \ + bool isSameTaskData = \ + (Expected.TaskData == expectedDefault(ompt_data_t *)) ? \ + true : (Expected.TaskData == Observed.TaskData); \ + bool isSameCount = (Expected.Count == expectedDefault(uint64_t)) ? \ + true : (Expected.Count == Observed.Count); \ + return isSameWorkType && isSameEndpoint && isSameParallelData && \ + isSameTaskData && isSameCount; \ +) +event_class_operator_stub(Dispatch) +event_class_operator_stub(TaskCreate) +event_class_operator_stub(Dependences) +event_class_operator_stub(TaskDependence) +event_class_operator_stub(TaskSchedule) +event_class_operator_w_body(ImplicitTask, \ + bool isSameEndpoint = (Expected.Endpoint == Observed.Endpoint); \ + bool isSameActualParallelism = \ + (Expected.ActualParallelism == expectedDefault(unsigned int)) ? \ + true : (Expected.ActualParallelism == Observed.ActualParallelism); \ + bool isSameIndex = (Expected.Index == expectedDefault(unsigned int)) ? \ + true : ( Expected.Index == Observed.Index); \ + return isSameEndpoint && isSameActualParallelism && isSameIndex; \ +) +event_class_operator_stub(Masked) +event_class_operator_w_body(SyncRegion, \ + bool isSameKind = (Expected.Kind == Observed.Kind); \ + bool isSameEndpoint = (Expected.Endpoint == Observed.Endpoint); \ + bool isSameParallelData = \ + (Expected.ParallelData == expectedDefault(ompt_data_t *)) ? \ + true : (Expected.ParallelData == Observed.ParallelData); \ + bool isSameTaskData = \ + (Expected.TaskData == expectedDefault(ompt_data_t *)) ? \ + true : (Expected.TaskData == Observed.TaskData); \ + return isSameKind && isSameEndpoint && isSameParallelData && isSameTaskData; \ +) +event_class_operator_stub(MutexAcquire) +event_class_operator_stub(Mutex) +event_class_operator_stub(NestLock) +event_class_operator_stub(Flush) +event_class_operator_stub(Cancel) +event_class_operator_w_body(Target, \ + bool isSameKind = (Expected.Kind == Observed.Kind); \ + bool isSameEndpoint = (Expected.Endpoint == Observed.Endpoint); \ + bool isSameDeviceNum = (Expected.DeviceNum == expectedDefault(int)) ? \ + true : (Expected.DeviceNum == Observed.DeviceNum); \ + return isSameKind && isSameEndpoint && isSameDeviceNum; \ +) +event_class_operator_w_body(TargetEmi, \ + bool isSameKind = (Expected.Kind == Observed.Kind); \ + bool isSameEndpoint = (Expected.Endpoint == Observed.Endpoint); \ + bool isSameDeviceNum = (Expected.DeviceNum == expectedDefault(int)) ? \ + true : (Expected.DeviceNum == Observed.DeviceNum); \ + return isSameKind && isSameEndpoint && isSameDeviceNum; \ +) +event_class_operator_w_body(TargetDataOp, \ + bool isSameOpType = (Expected.OpType == Observed.OpType); \ + bool isSameSize = (Expected.Bytes == expectedDefault(size_t)) ? \ + true : (Expected.Bytes == Observed.Bytes); \ + bool isSameSrcAddr = (Expected.SrcAddr == expectedDefault(void *)) ? \ + true : (Expected.SrcAddr == Observed.SrcAddr); \ + bool isSameDstAddr = (Expected.DstAddr == expectedDefault(void *)) ? \ + true : (Expected.DstAddr == Observed.DstAddr); \ + bool isSameSrcDeviceNum = \ + (Expected.SrcDeviceNum == expectedDefault(int)) ? \ + true : (Expected.SrcDeviceNum == Observed.SrcDeviceNum); \ + bool isSameDstDeviceNum = \ + (Expected.DstDeviceNum == expectedDefault(int)) ? \ + true : (Expected.DstDeviceNum == Observed.DstDeviceNum); \ + return isSameOpType && isSameSize && isSameSrcAddr && isSameDstAddr && \ + isSameSrcDeviceNum && isSameDstDeviceNum; \ +) +event_class_operator_w_body(TargetDataOpEmi, \ + bool isSameOpType = (Expected.OpType == Observed.OpType); \ + bool isSameEndpoint = (Expected.Endpoint == Observed.Endpoint); \ + bool isSameSize = (Expected.Bytes == expectedDefault(size_t)) ? \ + true : (Expected.Bytes == Observed.Bytes); \ + bool isSameSrcAddr = (Expected.SrcAddr == expectedDefault(void *)) ? \ + true : (Expected.SrcAddr == Observed.SrcAddr); \ + bool isSameDstAddr = (Expected.DstAddr == expectedDefault(void *)) ? \ + true : (Expected.DstAddr == Observed.DstAddr); \ + bool isSameSrcDeviceNum = \ + (Expected.SrcDeviceNum == expectedDefault(int)) ? \ + true : (Expected.SrcDeviceNum == Observed.SrcDeviceNum); \ + bool isSameDstDeviceNum = \ + (Expected.DstDeviceNum == expectedDefault(int)) ? \ + true : (Expected.DstDeviceNum == Observed.DstDeviceNum); \ + return isSameOpType && isSameEndpoint && isSameSize && isSameSrcAddr && \ + isSameDstAddr && isSameSrcDeviceNum && isSameDstDeviceNum; \ +) +event_class_operator_w_body(TargetSubmit, \ + bool isSameReqNumTeams = \ + (Expected.RequestedNumTeams == Observed.RequestedNumTeams); \ + return isSameReqNumTeams; \ +) +event_class_operator_w_body(TargetSubmitEmi, \ + bool isSameReqNumTeams = \ + (Expected.RequestedNumTeams == Observed.RequestedNumTeams); \ + bool isSameEndpoint = (Expected.Endpoint == Observed.Endpoint); \ + return isSameReqNumTeams && isSameEndpoint; \ +) +event_class_operator_stub(ControlTool) +event_class_operator_w_body(DeviceInitialize, \ + bool isSameDeviceNum = (Expected.DeviceNum == Observed.DeviceNum); \ + bool isSameType = (Expected.Type == expectedDefault(const char *)) ? \ + true : \ + ((Expected.Type == Observed.Type) || \ + (strcmp(Expected.Type, Observed.Type) == 0)); \ + bool isSameDevice = \ + (Expected.Device == expectedDefault(ompt_device_t *)) ? \ + true : (Expected.Device == Observed.Device); \ + return isSameDeviceNum && isSameType && isSameDevice; \ +) +event_class_operator_w_body(DeviceFinalize, \ + bool isSameDeviceNum = (Expected.DeviceNum == expectedDefault(int)) ? \ + true : (Expected.DeviceNum == Observed.DeviceNum); \ + return isSameDeviceNum; +) +event_class_operator_w_body(DeviceLoad, \ + bool isSameDeviceNum = (Expected.DeviceNum == expectedDefault(int)) ? \ + true : (Expected.DeviceNum == Observed.DeviceNum); \ + bool isSameSize = (Expected.Bytes == expectedDefault(size_t)) ? \ + true : (Expected.Bytes == Observed.Bytes); \ + return isSameDeviceNum && isSameSize; \ +) +event_class_operator_stub(DeviceUnload) +event_class_operator_w_body(BufferRequest, \ + bool isSameDeviceNum = (Expected.DeviceNum == expectedDefault(int)) ? \ + true : (Expected.DeviceNum == Observed.DeviceNum); \ + bool isSameSize = (Expected.Bytes == expectedDefault(size_t *)) ? \ + true : (Expected.Bytes == Observed.Bytes); \ + return isSameDeviceNum && isSameSize; \ +) +event_class_operator_w_body(BufferComplete, \ + bool isSameDeviceNum = (Expected.DeviceNum == expectedDefault(int)) ? \ + true : (Expected.DeviceNum == Observed.DeviceNum); \ + bool isSameSize = (Expected.Bytes == expectedDefault(size_t)) ? \ + true : (Expected.Bytes == Observed.Bytes); \ + return isSameDeviceNum && isSameSize; \ +) +event_class_operator_w_body(BufferRecord, \ + bool isSameType = (Expected.Record.type == Observed.Record.type); \ + bool isSameTargetId = \ + (Expected.Record.target_id == expectedDefault(ompt_id_t)) \ + ? true \ + : (Expected.Record.target_id == Observed.Record.target_id); \ + if (!(isSameType && isSameTargetId)) return false; \ + bool isEqual = true; \ + ompt_device_time_t ObservedDurationNs = \ + Observed.Record.record.target_data_op.end_time - Observed.Record.time; \ + switch(Expected.Record.type) { \ + case ompt_callback_target: \ + isEqual &= \ + (Expected.Record.record.target.kind == expectedDefault(ompt_target_t)) \ + ? true \ + : (Expected.Record.record.target.kind == \ + Observed.Record.record.target.kind); \ + isEqual &= \ + (Expected.Record.record.target.endpoint == \ + expectedDefault(ompt_scope_endpoint_t)) \ + ? true \ + : (Expected.Record.record.target.endpoint == \ + Observed.Record.record.target.endpoint); \ + isEqual &= \ + (Expected.Record.record.target.device_num == expectedDefault(int)) \ + ? true \ + : (Expected.Record.record.target.device_num == \ + Observed.Record.record.target.device_num); \ + break; \ + case ompt_callback_target_data_op: \ + isEqual &= \ + (Expected.Record.record.target_data_op.optype == \ + expectedDefault(ompt_target_data_op_t)) \ + ? true \ + : (Expected.Record.record.target_data_op.optype == \ + Observed.Record.record.target_data_op.optype); \ + isEqual &= \ + (Expected.Record.record.target_data_op.bytes == expectedDefault(size_t)) \ + ? true \ + : (Expected.Record.record.target_data_op.bytes == \ + Observed.Record.record.target_data_op.bytes); \ + isEqual &= \ + (Expected.Record.record.target_data_op.src_addr == \ + expectedDefault(void *)) \ + ? true \ + : (Expected.Record.record.target_data_op.src_addr == \ + Observed.Record.record.target_data_op.src_addr); \ + isEqual &= \ + (Expected.Record.record.target_data_op.dest_addr == \ + expectedDefault(void *)) \ + ? true \ + : (Expected.Record.record.target_data_op.dest_addr == \ + Observed.Record.record.target_data_op.dest_addr); \ + isEqual &= \ + (Expected.Record.record.target_data_op.src_device_num == \ + expectedDefault(int)) \ + ? true \ + : (Expected.Record.record.target_data_op.src_device_num == \ + Observed.Record.record.target_data_op.src_device_num); \ + isEqual &= \ + (Expected.Record.record.target_data_op.dest_device_num == \ + expectedDefault(int)) \ + ? true \ + : (Expected.Record.record.target_data_op.dest_device_num == \ + Observed.Record.record.target_data_op.dest_device_num); \ + isEqual &= \ + (Expected.Record.record.target_data_op.host_op_id == \ + expectedDefault(ompt_id_t)) \ + ? true \ + : (Expected.Record.record.target_data_op.host_op_id == \ + Observed.Record.record.target_data_op.host_op_id); \ + isEqual &= \ + (Expected.Record.record.target_data_op.codeptr_ra == \ + expectedDefault(void *)) \ + ? true \ + : (Expected.Record.record.target_data_op.codeptr_ra == \ + Observed.Record.record.target_data_op.codeptr_ra); \ + if (Expected.Record.record.target_data_op.end_time != \ + expectedDefault(ompt_device_time_t)) { \ + isEqual &= \ + ObservedDurationNs <= Expected.Record.record.target_data_op.end_time; \ + } \ + isEqual &= ObservedDurationNs >= Expected.Record.time; \ + break; \ + case ompt_callback_target_submit: \ + ObservedDurationNs = \ + Observed.Record.record.target_kernel.end_time - Observed.Record.time; \ + isEqual &= \ + (Expected.Record.record.target_kernel.requested_num_teams == \ + expectedDefault(unsigned int)) \ + ? true \ + : (Expected.Record.record.target_kernel.requested_num_teams == \ + Observed.Record.record.target_kernel.requested_num_teams); \ + isEqual &= \ + (Expected.Record.record.target_kernel.granted_num_teams == \ + expectedDefault(unsigned int)) \ + ? true \ + : (Expected.Record.record.target_kernel.granted_num_teams == \ + Observed.Record.record.target_kernel.granted_num_teams); \ + isEqual &= \ + (Expected.Record.record.target_kernel.host_op_id == \ + expectedDefault(ompt_id_t)) \ + ? true \ + : (Expected.Record.record.target_kernel.host_op_id == \ + Observed.Record.record.target_kernel.host_op_id); \ + if (Expected.Record.record.target_kernel.end_time != \ + expectedDefault(ompt_device_time_t)) { \ + isEqual &= \ + ObservedDurationNs <= Expected.Record.record.target_kernel.end_time; \ + } \ + isEqual &= ObservedDurationNs >= Expected.Record.time; \ + break; \ + default: \ + assert(false && "Encountered invalid record type"); \ + } \ + return isEqual; \ +) +event_class_operator_stub(BufferRecordDeallocation) + +define_cast_func(AssertionSyncPoint) +define_cast_func(AssertionSuspend) +define_cast_func(ThreadBegin) +define_cast_func(ThreadEnd) +define_cast_func(ParallelBegin) +define_cast_func(ParallelEnd) +define_cast_func(Work) +define_cast_func(Dispatch) +define_cast_func(TaskCreate) +define_cast_func(Dependences) +define_cast_func(TaskDependence) +define_cast_func(TaskSchedule) +define_cast_func(ImplicitTask) +define_cast_func(Masked) +define_cast_func(SyncRegion) +define_cast_func(MutexAcquire) +define_cast_func(Mutex) +define_cast_func(NestLock) +define_cast_func(Flush) +define_cast_func(Cancel) +define_cast_func(Target) +define_cast_func(TargetEmi) +define_cast_func(TargetDataOp) +define_cast_func(TargetDataOpEmi) +define_cast_func(TargetSubmit) +define_cast_func(TargetSubmitEmi) +define_cast_func(ControlTool) +define_cast_func(DeviceInitialize) +define_cast_func(DeviceFinalize) +define_cast_func(DeviceLoad) +define_cast_func(DeviceUnload) +define_cast_func(BufferRequest) +define_cast_func(BufferComplete) +define_cast_func(BufferRecord) +define_cast_func(BufferRecordDeallocation) + +class_equals_op(AssertionSyncPoint) +class_equals_op(AssertionSuspend) +class_equals_op(ThreadBegin) +class_equals_op(ThreadEnd) +class_equals_op(ParallelBegin) +class_equals_op(ParallelEnd) +class_equals_op(Work) +class_equals_op(Dispatch) +class_equals_op(TaskCreate) +class_equals_op(Dependences) +class_equals_op(TaskDependence) +class_equals_op(TaskSchedule) +class_equals_op(ImplicitTask) +class_equals_op(Masked) +class_equals_op(SyncRegion) +class_equals_op(MutexAcquire) +class_equals_op(Mutex) +class_equals_op(NestLock) +class_equals_op(Flush) +class_equals_op(Cancel) +class_equals_op(Target) +class_equals_op(TargetEmi) +class_equals_op(TargetDataOp) +class_equals_op(TargetDataOpEmi) +class_equals_op(TargetSubmit) +class_equals_op(TargetSubmitEmi) +class_equals_op(ControlTool) +class_equals_op(DeviceInitialize) +class_equals_op(DeviceFinalize) +class_equals_op(DeviceLoad) +class_equals_op(DeviceUnload) +class_equals_op(BufferRequest) +class_equals_op(BufferComplete) +class_equals_op(BufferRecord) +class_equals_op(BufferRecordDeallocation) +// clang-format on + +} // namespace internal + +} // namespace omptest diff --git a/openmp/tools/omptest/src/Logging.cpp b/openmp/tools/omptest/src/Logging.cpp new file mode 100644 index 0000000000000..28329c74d188d --- /dev/null +++ b/openmp/tools/omptest/src/Logging.cpp @@ -0,0 +1,177 @@ +//===- Logging.cpp - General logging class implementation -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implements ompTest-tailored logging. +/// +//===----------------------------------------------------------------------===// + +#include "Logging.h" + +using namespace omptest; +using namespace logging; + +Logger::Logger(Level LogLevel, std::ostream &OutStream, bool FormatOutput) + : LoggingLevel(LogLevel), OutStream(OutStream), FormatOutput(FormatOutput) { + // Flush any buffered output + OutStream << std::flush; +} + +Logger::~Logger() { + // Flush any buffered output + OutStream << std::flush; +} + +std::map> AggregatedFormatOptions{ + {Level::DIAGNOSTIC, {FormatOption::COLOR_LightBlue}}, + {Level::INFO, {FormatOption::COLOR_LightGray}}, + {Level::WARNING, {FormatOption::COLOR_LightYellow}}, + {Level::ERROR, {FormatOption::COLOR_Red}}, + {Level::CRITICAL, {FormatOption::COLOR_LightRed}}, + {Level::Default, {FormatOption::NONE}}, + {Level::ExpectedEvent, {FormatOption::BOLD, FormatOption::COLOR_Cyan}}, + {Level::ObservedEvent, {FormatOption::COLOR_Cyan}}, + {Level::OffendingEvent, {FormatOption::COLOR_Yellow}}}; + +const char *logging::to_string(Level LogLevel) { + switch (LogLevel) { + case Level::DIAGNOSTIC: + return "DIAGNOSTIC"; + case Level::INFO: + return "INFO"; + case Level::WARNING: + return "WARNING"; + case Level::ERROR: + return "ERROR"; + case Level::CRITICAL: + return "CRITICAL"; + default: + assert(false && "Requested string representation for unknown LogLevel"); + return "UNKNOWN"; + } +} + +std::string logging::getFormatSequence(Level LogLevel) { + auto Options = AggregatedFormatOptions[LogLevel]; + std::stringstream SS{"\033["}; + SS << "\033["; + if (!Options.empty()) { + for (auto &Option : AggregatedFormatOptions[LogLevel]) + SS << int(Option) << ';'; + SS.seekp(-1, SS.cur); + SS << 'm'; + } else { + // Fallback to NONE / reset formatting + SS << "0m"; + } + return SS.str(); +} + +std::string logging::format(const std::string &Message, FormatOption Option) { + std::stringstream SS{"\033["}; + SS << "\033["; + SS << int(Option) << 'm' << Message << "\033[0m"; + return SS.str(); +} + +std::string logging::format(const std::string &Message, + std::set Options) { + std::stringstream SS{"\033["}; + SS << "\033["; + for (auto &Option : Options) + SS << int(Option) << ';'; + SS.seekp(-1, SS.cur); + SS << 'm' << Message << "\033[0m"; + return SS.str(); +} + +void Logger::log(Level LogLevel, const std::string &Message) const { + // Serialize logging + std::lock_guard Lock(LogMutex); + + if (LoggingLevel > LogLevel) + return; + + if (FormatOutput) { + OutStream << getFormatSequence(LogLevel) << '[' << to_string(LogLevel) + << "] " << Message << getFormatSequence() << std::endl; + } else { + OutStream << '[' << to_string(LogLevel) << "] " << Message << std::endl; + } +} + +void Logger::eventMismatch(const omptest::OmptAssertEvent &OffendingEvent, + const std::string &Message, Level LogLevel) const { + // Serialize logging + std::lock_guard Lock(LogMutex); + if (LoggingLevel > LogLevel) + return; + + if (FormatOutput) { + OutStream << getFormatSequence(LogLevel) << '[' << to_string(LogLevel) + << "] " << getFormatSequence() + << format(Message, AggregatedFormatOptions[LogLevel]) + << "\n\tOffending event name='" + << format(OffendingEvent.getEventName(), + AggregatedFormatOptions[Level::OffendingEvent]) + << "'\n\tOffending='" + << format(OffendingEvent.toString(), + AggregatedFormatOptions[Level::OffendingEvent]) + << '\'' << std::endl; + } else { + OutStream << '[' << to_string(LogLevel) << "] " << Message + << "\n\tOffending event name='" << OffendingEvent.getEventName() + << "'\n\tOffending='" << OffendingEvent.toString() << '\'' + << std::endl; + } +} + +void Logger::eventMismatch(const omptest::OmptAssertEvent &ExpectedEvent, + const omptest::OmptAssertEvent &ObservedEvent, + const std::string &Message, Level LogLevel) const { + // Serialize logging + std::lock_guard Lock(LogMutex); + if (LoggingLevel > LogLevel) + return; + + if (FormatOutput) { + OutStream << getFormatSequence(LogLevel) << '[' << to_string(LogLevel) + << "] " << Message << getFormatSequence() + << "\n\tExpected event name='" + << format(ExpectedEvent.getEventName(), + AggregatedFormatOptions[Level::ExpectedEvent]) + << "' observe='" + << format(to_string(ExpectedEvent.getEventExpectedState()), + AggregatedFormatOptions[Level::ExpectedEvent]) + << "'\n\tObserved event name='" + << format(ObservedEvent.getEventName(), + AggregatedFormatOptions[Level::ObservedEvent]) + << "'\n\tExpected='" + << format(ExpectedEvent.toString(), + AggregatedFormatOptions[Level::ExpectedEvent]) + << "'\n\tObserved='" + << format(ObservedEvent.toString(), + AggregatedFormatOptions[Level::ObservedEvent]) + << '\'' << std::endl; + } else { + OutStream << '[' << to_string(LogLevel) << "] " << Message + << "\n\tExpected event name='" << ExpectedEvent.getEventName() + << "' observe='" + << to_string(ExpectedEvent.getEventExpectedState()) + << "'\n\tObserved event name='" << ObservedEvent.getEventName() + << "'\n\tExpected='" << ExpectedEvent.toString() + << "'\n\tObserved='" << ObservedEvent.toString() << '\'' + << std::endl; + } +} + +void Logger::setFormatOutput(bool Enabled) { FormatOutput = Enabled; } + +Level Logger::getLoggingLevel() const { return LoggingLevel; } + +void Logger::setLoggingLevel(Level LogLevel) { LoggingLevel = LogLevel; } diff --git a/openmp/tools/omptest/src/OmptAssertEvent.cpp b/openmp/tools/omptest/src/OmptAssertEvent.cpp new file mode 100644 index 0000000000000..b03f267a8c397 --- /dev/null +++ b/openmp/tools/omptest/src/OmptAssertEvent.cpp @@ -0,0 +1,587 @@ +//===- OmptAssertEvent.cpp - Assertion event implementations ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implements assertion event CTORs, for generally all observable events. +/// +//===----------------------------------------------------------------------===// + +#include "OmptAssertEvent.h" +#include + +using namespace omptest; + +const char *omptest::to_string(ObserveState State) { + switch (State) { + case ObserveState::generated: + return "generated"; + case ObserveState::always: + return "always"; + case ObserveState::never: + return "never"; + default: + assert(false && "Requested string representation for unknown ObserveState"); + return "UNKNOWN"; + } +} + +OmptAssertEvent::OmptAssertEvent(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + internal::InternalEvent *IE) + : Name(Name), Group(Group), ExpectedState(Expected), TheEvent(IE) {} + +OmptAssertEvent OmptAssertEvent::AssertionSyncPoint( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, const std::string &SyncPointName) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::AssertionSyncPoint(SyncPointName)); +} + +OmptAssertEvent +OmptAssertEvent::AssertionSuspend(const std::string &Name, + const std::string &Group, + const ObserveState &Expected) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::AssertionSuspend()); +} + +OmptAssertEvent OmptAssertEvent::ThreadBegin(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_thread_t ThreadType) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::ThreadBegin(ThreadType)); +} + +OmptAssertEvent OmptAssertEvent::ThreadEnd(const std::string &Name, + const std::string &Group, + const ObserveState &Expected) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, new internal::ThreadEnd()); +} + +OmptAssertEvent OmptAssertEvent::ParallelBegin(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + int NumThreads) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::ParallelBegin(NumThreads)); +} + +OmptAssertEvent OmptAssertEvent::ParallelEnd(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_data_t *ParallelData, + ompt_data_t *EncounteringTaskData, + int Flags, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::ParallelEnd(ParallelData, + EncounteringTaskData, Flags, + CodeptrRA)); +} + +OmptAssertEvent +OmptAssertEvent::Work(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_work_t WorkType, + ompt_scope_endpoint_t Endpoint, ompt_data_t *ParallelData, + ompt_data_t *TaskData, uint64_t Count, + const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::Work(WorkType, Endpoint, ParallelData, + TaskData, Count, CodeptrRA)); +} + +OmptAssertEvent +OmptAssertEvent::Dispatch(const std::string &Name, const std::string &Group, + const ObserveState &Expected, + ompt_data_t *ParallelData, ompt_data_t *TaskData, + ompt_dispatch_t Kind, ompt_data_t Instance) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent( + EName, EGroup, Expected, + new internal::Dispatch(ParallelData, TaskData, Kind, Instance)); +} + +OmptAssertEvent OmptAssertEvent::TaskCreate( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_data_t *EncounteringTaskData, + const ompt_frame_t *EncounteringTaskFrame, ompt_data_t *NewTaskData, + int Flags, int HasDependences, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent( + EName, EGroup, Expected, + new internal::TaskCreate(EncounteringTaskData, EncounteringTaskFrame, + NewTaskData, Flags, HasDependences, CodeptrRA)); +} + +OmptAssertEvent OmptAssertEvent::TaskSchedule(const std::string &Name, + const std::string &Group, + const ObserveState &Expected) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, new internal::TaskSchedule()); +} + +OmptAssertEvent OmptAssertEvent::ImplicitTask( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData, ompt_data_t *TaskData, + unsigned int ActualParallelism, unsigned int Index, int Flags) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::ImplicitTask(Endpoint, ParallelData, + TaskData, ActualParallelism, + Index, Flags)); +} + +OmptAssertEvent OmptAssertEvent::SyncRegion( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_sync_region_t Kind, + ompt_scope_endpoint_t Endpoint, ompt_data_t *ParallelData, + ompt_data_t *TaskData, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::SyncRegion(Kind, Endpoint, ParallelData, + TaskData, CodeptrRA)); +} + +OmptAssertEvent +OmptAssertEvent::Target(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_t Kind, + ompt_scope_endpoint_t Endpoint, int DeviceNum, + ompt_data_t *TaskData, ompt_id_t TargetId, + const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::Target(Kind, Endpoint, DeviceNum, + TaskData, TargetId, CodeptrRA)); +} + +OmptAssertEvent +OmptAssertEvent::TargetEmi(const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_t Kind, + ompt_scope_endpoint_t Endpoint, int DeviceNum, + ompt_data_t *TaskData, ompt_data_t *TargetTaskData, + ompt_data_t *TargetData, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::TargetEmi(Kind, Endpoint, DeviceNum, + TaskData, TargetTaskData, + TargetData, CodeptrRA)); +} + +OmptAssertEvent OmptAssertEvent::TargetDataOp( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_id_t TargetId, ompt_id_t HostOpId, + ompt_target_data_op_t OpType, void *SrcAddr, int SrcDeviceNum, + void *DstAddr, int DstDeviceNum, size_t Bytes, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::TargetDataOp( + TargetId, HostOpId, OpType, SrcAddr, SrcDeviceNum, + DstAddr, DstDeviceNum, Bytes, CodeptrRA)); +} + +OmptAssertEvent OmptAssertEvent::TargetDataOp( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_data_op_t OpType, size_t Bytes, + void *SrcAddr, void *DstAddr, int SrcDeviceNum, int DstDeviceNum, + ompt_id_t TargetId, ompt_id_t HostOpId, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::TargetDataOp( + TargetId, HostOpId, OpType, SrcAddr, SrcDeviceNum, + DstAddr, DstDeviceNum, Bytes, CodeptrRA)); +} + +OmptAssertEvent OmptAssertEvent::TargetDataOpEmi( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetTaskData, ompt_data_t *TargetData, ompt_id_t *HostOpId, + ompt_target_data_op_t OpType, void *SrcAddr, int SrcDeviceNum, + void *DstAddr, int DstDeviceNum, size_t Bytes, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent( + EName, EGroup, Expected, + new internal::TargetDataOpEmi(Endpoint, TargetTaskData, TargetData, + HostOpId, OpType, SrcAddr, SrcDeviceNum, + DstAddr, DstDeviceNum, Bytes, CodeptrRA)); +} + +OmptAssertEvent OmptAssertEvent::TargetDataOpEmi( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_target_data_op_t OpType, + ompt_scope_endpoint_t Endpoint, size_t Bytes, void *SrcAddr, void *DstAddr, + int SrcDeviceNum, int DstDeviceNum, ompt_data_t *TargetTaskData, + ompt_data_t *TargetData, ompt_id_t *HostOpId, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent( + EName, EGroup, Expected, + new internal::TargetDataOpEmi(Endpoint, TargetTaskData, TargetData, + HostOpId, OpType, SrcAddr, SrcDeviceNum, + DstAddr, DstDeviceNum, Bytes, CodeptrRA)); +} + +OmptAssertEvent OmptAssertEvent::TargetSubmit(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_id_t TargetId, + ompt_id_t HostOpId, + unsigned int RequestedNumTeams) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent( + EName, EGroup, Expected, + new internal::TargetSubmit(TargetId, HostOpId, RequestedNumTeams)); +} + +OmptAssertEvent OmptAssertEvent::TargetSubmit(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + unsigned int RequestedNumTeams, + ompt_id_t TargetId, + ompt_id_t HostOpId) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent( + EName, EGroup, Expected, + new internal::TargetSubmit(TargetId, HostOpId, RequestedNumTeams)); +} + +OmptAssertEvent OmptAssertEvent::TargetSubmitEmi( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetData, ompt_id_t *HostOpId, + unsigned int RequestedNumTeams) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::TargetSubmitEmi(Endpoint, TargetData, + HostOpId, + RequestedNumTeams)); +} + +OmptAssertEvent OmptAssertEvent::TargetSubmitEmi(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + unsigned int RequestedNumTeams, + ompt_scope_endpoint_t Endpoint, + ompt_data_t *TargetData, + ompt_id_t *HostOpId) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::TargetSubmitEmi(Endpoint, TargetData, + HostOpId, + RequestedNumTeams)); +} + +OmptAssertEvent OmptAssertEvent::ControlTool(const std::string &Name, + const std::string &Group, + const ObserveState &Expected) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, new internal::ControlTool()); +} + +OmptAssertEvent OmptAssertEvent::DeviceInitialize( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, int DeviceNum, const char *Type, + ompt_device_t *Device, ompt_function_lookup_t LookupFn, + const char *DocumentationStr) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::DeviceInitialize(DeviceNum, Type, Device, + LookupFn, + DocumentationStr)); +} + +OmptAssertEvent OmptAssertEvent::DeviceFinalize(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + int DeviceNum) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::DeviceFinalize(DeviceNum)); +} + +OmptAssertEvent +OmptAssertEvent::DeviceLoad(const std::string &Name, const std::string &Group, + const ObserveState &Expected, int DeviceNum, + const char *Filename, int64_t OffsetInFile, + void *VmaInFile, size_t Bytes, void *HostAddr, + void *DeviceAddr, uint64_t ModuleId) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent( + EName, EGroup, Expected, + new internal::DeviceLoad(DeviceNum, Filename, OffsetInFile, VmaInFile, + Bytes, HostAddr, DeviceAddr, ModuleId)); +} + +OmptAssertEvent OmptAssertEvent::DeviceUnload(const std::string &Name, + const std::string &Group, + const ObserveState &Expected) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, new internal::DeviceUnload()); +} + +OmptAssertEvent OmptAssertEvent::BufferRequest(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + int DeviceNum, + ompt_buffer_t **Buffer, + size_t *Bytes) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferRequest(DeviceNum, Buffer, Bytes)); +} + +OmptAssertEvent OmptAssertEvent::BufferComplete( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, int DeviceNum, ompt_buffer_t *Buffer, + size_t Bytes, ompt_buffer_cursor_t Begin, int BufferOwned) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferComplete(DeviceNum, Buffer, Bytes, + Begin, BufferOwned)); +} + +OmptAssertEvent OmptAssertEvent::BufferRecord(const std::string &Name, + const std::string &Group, + const ObserveState &Expected, + ompt_record_ompt_t *Record) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferRecord(Record)); +} + +OmptAssertEvent OmptAssertEvent::BufferRecord( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, ompt_target_t Kind, + ompt_scope_endpoint_t Endpoint, int DeviceNum, ompt_id_t TaskId, + ompt_id_t TargetId, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + + if (Type != ompt_callback_target) + assert(false && "CTOR only suited for type: 'ompt_callback_target'"); + + ompt_record_target_t Subrecord{Kind, Endpoint, DeviceNum, + TaskId, TargetId, CodeptrRA}; + + ompt_record_ompt_t *RecordPtr = + (ompt_record_ompt_t *)malloc(sizeof(ompt_record_ompt_t)); + memset(RecordPtr, 0, sizeof(ompt_record_ompt_t)); + RecordPtr->type = Type; + RecordPtr->time = expectedDefault(ompt_device_time_t); + RecordPtr->thread_id = expectedDefault(ompt_id_t); + RecordPtr->target_id = TargetId; + RecordPtr->record.target = Subrecord; + + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferRecord(RecordPtr)); +} + +OmptAssertEvent OmptAssertEvent::BufferRecord( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + ompt_target_data_op_t OpType, size_t Bytes, + std::pair Timeframe, void *SrcAddr, + void *DstAddr, int SrcDeviceNum, int DstDeviceNum, ompt_id_t TargetId, + ompt_id_t HostOpId, const void *CodeptrRA) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + + if (Type != ompt_callback_target_data_op) + assert(false && + "CTOR only suited for type: 'ompt_callback_target_data_op'"); + + ompt_record_target_data_op_t Subrecord{ + HostOpId, OpType, SrcAddr, SrcDeviceNum, DstAddr, + DstDeviceNum, Bytes, Timeframe.second, CodeptrRA}; + + ompt_record_ompt_t *RecordPtr = + (ompt_record_ompt_t *)malloc(sizeof(ompt_record_ompt_t)); + memset(RecordPtr, 0, sizeof(ompt_record_ompt_t)); + RecordPtr->type = Type; + RecordPtr->time = Timeframe.first; + RecordPtr->thread_id = expectedDefault(ompt_id_t); + RecordPtr->target_id = TargetId; + RecordPtr->record.target_data_op = Subrecord; + + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferRecord(RecordPtr)); +} + +OmptAssertEvent OmptAssertEvent::BufferRecord( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + ompt_target_data_op_t OpType, size_t Bytes, + ompt_device_time_t MinimumTimeDelta, void *SrcAddr, void *DstAddr, + int SrcDeviceNum, int DstDeviceNum, ompt_id_t TargetId, ompt_id_t HostOpId, + const void *CodeptrRA) { + return BufferRecord(Name, Group, Expected, Type, OpType, Bytes, + {MinimumTimeDelta, expectedDefault(ompt_device_time_t)}, + SrcAddr, DstAddr, SrcDeviceNum, DstDeviceNum, TargetId, + HostOpId, CodeptrRA); +} + +OmptAssertEvent OmptAssertEvent::BufferRecord( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + std::pair Timeframe, + unsigned int RequestedNumTeams, unsigned int GrantedNumTeams, + ompt_id_t TargetId, ompt_id_t HostOpId) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + + bool isDefault = (Timeframe.first == expectedDefault(ompt_device_time_t)); + isDefault &= (Timeframe.second == expectedDefault(ompt_device_time_t)); + isDefault &= (RequestedNumTeams == expectedDefault(unsigned int)); + isDefault &= (GrantedNumTeams == expectedDefault(unsigned int)); + isDefault &= (TargetId == expectedDefault(ompt_id_t)); + isDefault &= (HostOpId == expectedDefault(ompt_id_t)); + + ompt_record_ompt_t *RecordPtr = + (ompt_record_ompt_t *)malloc(sizeof(ompt_record_ompt_t)); + memset(RecordPtr, 0, sizeof(ompt_record_ompt_t)); + RecordPtr->type = Type; + + // This handles the simplest occurrence of a device tracing record + // We can only check for Type -- since all other properties are set to default + if (isDefault) { + RecordPtr->time = expectedDefault(ompt_device_time_t); + RecordPtr->thread_id = expectedDefault(ompt_id_t); + RecordPtr->target_id = expectedDefault(ompt_id_t); + if (Type == ompt_callback_target) { + ompt_record_target_t Subrecord{expectedDefault(ompt_target_t), + expectedDefault(ompt_scope_endpoint_t), + expectedDefault(int), + expectedDefault(ompt_id_t), + expectedDefault(ompt_id_t), + expectedDefault(void *)}; + RecordPtr->record.target = Subrecord; + } + + if (Type == ompt_callback_target_data_op) { + ompt_record_target_data_op_t Subrecord{ + expectedDefault(ompt_id_t), expectedDefault(ompt_target_data_op_t), + expectedDefault(void *), expectedDefault(int), + expectedDefault(void *), expectedDefault(int), + expectedDefault(size_t), expectedDefault(ompt_device_time_t), + expectedDefault(void *)}; + RecordPtr->record.target_data_op = Subrecord; + } + + if (Type == ompt_callback_target_submit) { + ompt_record_target_kernel_t Subrecord{ + expectedDefault(ompt_id_t), expectedDefault(unsigned int), + expectedDefault(unsigned int), expectedDefault(ompt_device_time_t)}; + RecordPtr->record.target_kernel = Subrecord; + } + + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferRecord(RecordPtr)); + } + + if (Type != ompt_callback_target_submit) + assert(false && "CTOR only suited for type: 'ompt_callback_target_submit'"); + + ompt_record_target_kernel_t Subrecord{HostOpId, RequestedNumTeams, + GrantedNumTeams, Timeframe.second}; + + RecordPtr->time = Timeframe.first; + RecordPtr->thread_id = expectedDefault(ompt_id_t); + RecordPtr->target_id = TargetId; + RecordPtr->record.target_kernel = Subrecord; + + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferRecord(RecordPtr)); +} + +OmptAssertEvent OmptAssertEvent::BufferRecord( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_callbacks_t Type, + ompt_device_time_t MinimumTimeDelta, unsigned int RequestedNumTeams, + unsigned int GrantedNumTeams, ompt_id_t TargetId, ompt_id_t HostOpId) { + return BufferRecord(Name, Group, Expected, Type, + {MinimumTimeDelta, expectedDefault(ompt_device_time_t)}, + RequestedNumTeams, GrantedNumTeams, TargetId, HostOpId); +} + +OmptAssertEvent OmptAssertEvent::BufferRecordDeallocation( + const std::string &Name, const std::string &Group, + const ObserveState &Expected, ompt_buffer_t *Buffer) { + auto EName = getName(Name); + auto EGroup = getGroup(Group); + return OmptAssertEvent(EName, EGroup, Expected, + new internal::BufferRecordDeallocation(Buffer)); +} + +std::string OmptAssertEvent::getEventName() const { return Name; } + +std::string OmptAssertEvent::getEventGroup() const { return Group; } + +ObserveState OmptAssertEvent::getEventExpectedState() const { + return ExpectedState; +} + +internal::EventTy OmptAssertEvent::getEventType() const { + return TheEvent->getType(); +} + +internal::InternalEvent *OmptAssertEvent::getEvent() const { + return TheEvent.get(); +} + +std::string OmptAssertEvent::toString(bool PrefixEventName) const { + std::string S; + if (PrefixEventName) + S.append(getEventName()).append(": "); + S.append((TheEvent == nullptr) ? "OmptAssertEvent" : TheEvent->toString()); + return S; +} + +bool omptest::operator==(const OmptAssertEvent &A, const OmptAssertEvent &B) { + assert(A.TheEvent.get() != nullptr && "A is valid"); + assert(B.TheEvent.get() != nullptr && "B is valid"); + + return A.TheEvent->getType() == B.TheEvent->getType() && + A.TheEvent->equals(B.TheEvent.get()); +} diff --git a/openmp/tools/omptest/src/OmptAsserter.cpp b/openmp/tools/omptest/src/OmptAsserter.cpp new file mode 100644 index 0000000000000..1c2f2dee69e16 --- /dev/null +++ b/openmp/tools/omptest/src/OmptAsserter.cpp @@ -0,0 +1,480 @@ +//===- OmptAsserter.cpp - Asserter-related implementations ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implements all asserter-related class methods, like: notifications, handling +/// of groups or determination of the testcase state. +/// +//===----------------------------------------------------------------------===// + +#include "OmptAsserter.h" +#include "Logging.h" + +#include + +using namespace omptest; +using namespace internal; + +// Initialize static members +std::mutex OmptAsserter::StaticMemberAccessMutex; +std::weak_ptr + OmptAsserter::EventGroupInterfaceInstance; +std::weak_ptr OmptAsserter::LoggingInstance; + +OmptAsserter::OmptAsserter() { + // Protect static members access + std::lock_guard Lock(StaticMemberAccessMutex); + + // Upgrade OmptEventGroupInterface weak_ptr to shared_ptr + { + EventGroups = EventGroupInterfaceInstance.lock(); + if (!EventGroups) { + // Coordinator doesn't exist or was previously destroyed, create a new + // one. + EventGroups = std::make_shared(); + // Store a weak reference to it + EventGroupInterfaceInstance = EventGroups; + } + // EventGroups is now a valid shared_ptr, either to a new or existing + // instance. + } + + // Upgrade logging::Logger weak_ptr to shared_ptr + { + Log = LoggingInstance.lock(); + if (!Log) { + // Coordinator doesn't exist or was previously destroyed, create a new + // one. + Log = std::make_shared(); + // Store a weak reference to it + LoggingInstance = Log; + } + // Log is now a valid shared_ptr, either to a new or existing instance. + } +} + +void OmptListener::setActive(bool Enabled) { Active = Enabled; } + +bool OmptListener::isActive() { return Active; } + +bool OmptListener::isSuppressedEventType(EventTy EvTy) { + return SuppressedEvents.find(EvTy) != SuppressedEvents.end(); +} + +void OmptListener::permitEvent(EventTy EvTy) { SuppressedEvents.erase(EvTy); } + +void OmptListener::suppressEvent(EventTy EvTy) { + SuppressedEvents.insert(EvTy); +} + +void OmptAsserter::insert(OmptAssertEvent &&AE) { + assert(false && "Base class 'insert' has undefined semantics."); +} + +void OmptAsserter::notify(OmptAssertEvent &&AE) { + // Ignore notifications while inactive + if (!isActive() || isSuppressedEventType(AE.getEventType())) + return; + + this->notifyImpl(std::move(AE)); +} + +AssertState OmptAsserter::checkState() { return State; } + +bool OmptAsserter::verifyEventGroups(const OmptAssertEvent &ExpectedEvent, + const OmptAssertEvent &ObservedEvent) { + assert(ExpectedEvent.getEventType() == ObservedEvent.getEventType() && + "Type mismatch: Expected != Observed event type"); + assert(EventGroups && "Missing EventGroups interface"); + + // Ignore all events within "default" group + auto GroupName = ExpectedEvent.getEventGroup(); + + if (GroupName == "default") + return true; + + // Get a pointer to the observed internal event + auto Event = ObservedEvent.getEvent(); + + switch (Event->getType()) { + case EventTy::Target: + if (auto E = static_cast(Event)) { + if (E->Endpoint == ompt_scope_begin) { + // Add new group since we entered a Target Region + EventGroups->addActiveEventGroup(GroupName, + AssertEventGroup{E->TargetId}); + } else if (E->Endpoint == ompt_scope_end) { + // Deprecate group since we return from a Target Region + EventGroups->deprecateActiveEventGroup(GroupName); + } + return true; + } + return false; + case EventTy::TargetEmi: + if (auto E = static_cast(Event)) { + if (E->Endpoint == ompt_scope_begin) { + // Add new group since we entered a Target Region + EventGroups->addActiveEventGroup( + GroupName, AssertEventGroup{E->TargetData->value}); + } else if (E->Endpoint == ompt_scope_end) { + // Deprecate group since we return from a Target Region + EventGroups->deprecateActiveEventGroup(GroupName); + } + return true; + } + return false; + case EventTy::TargetDataOp: + if (auto E = static_cast(Event)) + return EventGroups->checkActiveEventGroups(GroupName, + AssertEventGroup{E->TargetId}); + + return false; + case EventTy::TargetDataOpEmi: + if (auto E = static_cast(Event)) + return EventGroups->checkActiveEventGroups( + GroupName, AssertEventGroup{E->TargetData->value}); + + return false; + case EventTy::TargetSubmit: + if (auto E = static_cast(Event)) + return EventGroups->checkActiveEventGroups(GroupName, + AssertEventGroup{E->TargetId}); + + return false; + case EventTy::TargetSubmitEmi: + if (auto E = static_cast(Event)) + return EventGroups->checkActiveEventGroups( + GroupName, AssertEventGroup{E->TargetData->value}); + + return false; + case EventTy::BufferRecord: + // BufferRecords are delivered asynchronously: also check deprecated groups. + if (auto E = static_cast(Event)) + return (EventGroups->checkActiveEventGroups( + GroupName, AssertEventGroup{E->Record.target_id}) || + EventGroups->checkDeprecatedEventGroups( + GroupName, AssertEventGroup{E->Record.target_id})); + return false; + // Some event types do not need any handling + case EventTy::ThreadBegin: + case EventTy::ThreadEnd: + case EventTy::ParallelBegin: + case EventTy::ParallelEnd: + case EventTy::Work: + case EventTy::Dispatch: + case EventTy::TaskCreate: + case EventTy::Dependences: + case EventTy::TaskDependence: + case EventTy::TaskSchedule: + case EventTy::ImplicitTask: + case EventTy::Masked: + case EventTy::SyncRegion: + case EventTy::MutexAcquire: + case EventTy::Mutex: + case EventTy::NestLock: + case EventTy::Flush: + case EventTy::Cancel: + case EventTy::DeviceInitialize: + case EventTy::DeviceFinalize: + case EventTy::DeviceLoad: + case EventTy::DeviceUnload: + case EventTy::BufferRequest: + case EventTy::BufferComplete: + case EventTy::BufferRecordDeallocation: + return true; + // Some event types must not be encountered + case EventTy::None: + case EventTy::AssertionSyncPoint: + case EventTy::AssertionSuspend: + default: + assert(false && "Encountered invalid event type"); + } + + return true; +} + +void OmptAsserter::setOperationMode(AssertMode Mode) { OperationMode = Mode; } + +void OmptSequencedAsserter::insert(OmptAssertEvent &&AE) { + std::lock_guard Lock(AssertMutex); + Events.emplace_back(std::move(AE)); +} + +void OmptSequencedAsserter::notifyImpl(OmptAssertEvent &&AE) { + std::lock_guard Lock(AssertMutex); + // Ignore notifications while inactive, or for suppressed events + if (Events.empty() || !isActive() || isSuppressedEventType(AE.getEventType())) + return; + + ++NumNotifications; + + // Note: Order of these checks has semantic meaning. + // (1) Synchronization points should fail if there are remaining events, + // otherwise pass. (2) Regular notification while no further events are + // expected: fail. (3) Assertion suspension relies on a next expected event + // being available. (4) All other cases are considered 'regular' and match the + // next expected against the observed event. (5+6) Depending on the state / + // mode we signal failure if no other check has done already, or signaled pass + // by early-exit. + if (consumeSyncPoint(AE) || // Handle observed SyncPoint event + checkExcessNotify(AE) || // Check for remaining expected + consumeSuspend() || // Handle requested suspend + consumeRegularEvent(AE) || // Handle regular event + AssertionSuspended || // Ignore fail, if suspended + OperationMode == AssertMode::relaxed) // Ignore fail, if relaxed op-mode + return; + + Log->eventMismatch(Events[NextEvent], AE, + "[OmptSequencedAsserter] The events are not equal"); + State = AssertState::fail; +} + +bool OmptSequencedAsserter::consumeSyncPoint( + const omptest::OmptAssertEvent &AE) { + if (AE.getEventType() == EventTy::AssertionSyncPoint) { + auto NumRemainingEvents = getRemainingEventCount(); + // Upon encountering a SyncPoint, all events should have been processed + if (NumRemainingEvents == 0) + return true; + + Log->eventMismatch( + AE, + "[OmptSequencedAsserter] Encountered SyncPoint while still awaiting " + + std::to_string(NumRemainingEvents) + " events. Asserted " + + std::to_string(NumSuccessfulAsserts) + "/" + + std::to_string(Events.size()) + " events successfully."); + State = AssertState::fail; + return true; + } + + // Nothing to process: continue. + return false; +} + +bool OmptSequencedAsserter::checkExcessNotify( + const omptest::OmptAssertEvent &AE) { + if (NextEvent >= Events.size()) { + // If we are not expecting any more events and passively asserting: return + if (AssertionSuspended) + return true; + + Log->eventMismatch( + AE, "[OmptSequencedAsserter] Too many events to check (" + + std::to_string(NumNotifications) + "). Asserted " + + std::to_string(NumSuccessfulAsserts) + "/" + + std::to_string(Events.size()) + " events successfully."); + State = AssertState::fail; + return true; + } + + // Remaining expected events present: continue. + return false; +} + +bool OmptSequencedAsserter::consumeSuspend() { + // On AssertionSuspend -- enter 'passive' assertion. + // Since we may encounter multiple, successive AssertionSuspend events, loop + // until we hit the next non-AssertionSuspend event. + while (Events[NextEvent].getEventType() == EventTy::AssertionSuspend) { + AssertionSuspended = true; + // We just hit the very last event: indicate early exit. + if (++NextEvent >= Events.size()) + return true; + } + + // Continue with remaining notification logic. + return false; +} + +bool OmptSequencedAsserter::consumeRegularEvent( + const omptest::OmptAssertEvent &AE) { + // If we are actively asserting, increment the event counter. + // Otherwise: If passively asserting, we will keep waiting for a match. + auto &E = Events[NextEvent]; + if (E == AE && verifyEventGroups(E, AE)) { + if (E.getEventExpectedState() == ObserveState::always) { + ++NumSuccessfulAsserts; + } else if (E.getEventExpectedState() == ObserveState::never) { + Log->eventMismatch(E, AE, + "[OmptSequencedAsserter] Encountered forbidden event"); + State = AssertState::fail; + } + + // Return to active assertion + if (AssertionSuspended) + AssertionSuspended = false; + + // Match found, increment index and indicate early exit (success). + ++NextEvent; + return true; + } + + // Continue with remaining notification logic. + return false; +} + +size_t OmptSequencedAsserter::getRemainingEventCount() { + return std::count_if(Events.begin(), Events.end(), + [](const omptest::OmptAssertEvent &E) { + return E.getEventExpectedState() == + ObserveState::always; + }) - + NumSuccessfulAsserts; +} + +AssertState OmptSequencedAsserter::checkState() { + // This is called after the testcase executed. + // Once reached the number of successful notifications should be equal to the + // number of expected events. However, there may still be excluded as well as + // special asserter events remaining in the sequence. + for (size_t i = NextEvent; i < Events.size(); ++i) { + auto &E = Events[i]; + if (E.getEventExpectedState() == ObserveState::always) { + State = AssertState::fail; + Log->eventMismatch(E, "[OmptSequencedAsserter] Expected event was not " + "encountered (Remaining events: " + + std::to_string(getRemainingEventCount()) + ")"); + break; + } + } + + return State; +} + +void OmptEventAsserter::insert(OmptAssertEvent &&AE) { + std::lock_guard Lock(AssertMutex); + Events.emplace_back(std::move(AE)); +} + +void OmptEventAsserter::notifyImpl(OmptAssertEvent &&AE) { + std::lock_guard Lock(AssertMutex); + if (Events.empty() || !isActive() || isSuppressedEventType(AE.getEventType())) + return; + + if (NumEvents == 0) + NumEvents = Events.size(); + + ++NumNotifications; + + if (AE.getEventType() == EventTy::AssertionSyncPoint) { + auto NumRemainingEvents = getRemainingEventCount(); + // Upon encountering a SyncPoint, all events should have been processed + if (NumRemainingEvents == 0) + return; + + Log->eventMismatch( + AE, "[OmptEventAsserter] Encountered SyncPoint while still awaiting " + + std::to_string(NumRemainingEvents) + " events. Asserted " + + std::to_string(NumSuccessfulAsserts) + " events successfully."); + State = AssertState::fail; + return; + } + + for (size_t i = 0; i < Events.size(); ++i) { + auto &E = Events[i]; + if (E == AE && verifyEventGroups(E, AE)) { + if (E.getEventExpectedState() == ObserveState::always) { + Events.erase(Events.begin() + i); + ++NumSuccessfulAsserts; + } else if (E.getEventExpectedState() == ObserveState::never) { + Log->eventMismatch(E, AE, + "[OmptEventAsserter] Encountered forbidden event"); + State = AssertState::fail; + } + return; + } + } + + if (OperationMode == AssertMode::strict) { + Log->eventMismatch(AE, "[OmptEventAsserter] Too many events to check (" + + std::to_string(NumNotifications) + + "). Asserted " + + std::to_string(NumSuccessfulAsserts) + + " events successfully. (Remaining events: " + + std::to_string(getRemainingEventCount()) + ")"); + State = AssertState::fail; + return; + } +} + +size_t OmptEventAsserter::getRemainingEventCount() { + // size_t EventCount = std::count_if(Events.begin(), Events.end(), [](const + // omptest::OmptAssertEvent &E) { return E.getEventExpectedState() == + // ObserveState::always; }); + return std::count_if( + Events.begin(), Events.end(), [](const omptest::OmptAssertEvent &E) { + return E.getEventExpectedState() == ObserveState::always; + }); +} + +AssertState OmptEventAsserter::checkState() { + // This is called after the testcase executed. + // Once reached no more expected events should be in the queue + for (const auto &E : Events) { + // Check if any of the remaining events were expected to be observed + if (E.getEventExpectedState() == ObserveState::always) { + State = AssertState::fail; + Log->eventMismatch(E, "[OmptEventAsserter] Expected event was not " + "encountered (Remaining events: " + + std::to_string(getRemainingEventCount()) + ")"); + break; + } + } + + return State; +} + +void OmptEventReporter::notify(OmptAssertEvent &&AE) { + if (!isActive() || isSuppressedEventType(AE.getEventType())) + return; + + // Prepare notification, containing the newline to avoid stream interleaving. + auto Notification{AE.toString()}; + Notification.push_back('\n'); + OutStream << Notification; +} + +bool OmptEventGroupInterface::addActiveEventGroup( + const std::string &GroupName, omptest::AssertEventGroup Group) { + std::lock_guard Lock(GroupMutex); + auto EventGroup = ActiveEventGroups.find(GroupName); + if (EventGroup != ActiveEventGroups.end() && + EventGroup->second.TargetRegion == Group.TargetRegion) + return false; + ActiveEventGroups.emplace(GroupName, Group); + return true; +} + +bool OmptEventGroupInterface::deprecateActiveEventGroup( + const std::string &GroupName) { + std::lock_guard Lock(GroupMutex); + auto EventGroup = ActiveEventGroups.find(GroupName); + auto DeprecatedEventGroup = DeprecatedEventGroups.find(GroupName); + if (EventGroup == ActiveEventGroups.end() && + DeprecatedEventGroup != DeprecatedEventGroups.end()) + return false; + DeprecatedEventGroups.emplace(GroupName, EventGroup->second); + ActiveEventGroups.erase(GroupName); + return true; +} + +bool OmptEventGroupInterface::checkActiveEventGroups( + const std::string &GroupName, omptest::AssertEventGroup Group) { + std::lock_guard Lock(GroupMutex); + auto EventGroup = ActiveEventGroups.find(GroupName); + return (EventGroup != ActiveEventGroups.end() && + EventGroup->second.TargetRegion == Group.TargetRegion); +} + +bool OmptEventGroupInterface::checkDeprecatedEventGroups( + const std::string &GroupName, omptest::AssertEventGroup Group) { + std::lock_guard Lock(GroupMutex); + auto EventGroup = DeprecatedEventGroups.find(GroupName); + return (EventGroup != DeprecatedEventGroups.end() && + EventGroup->second.TargetRegion == Group.TargetRegion); +} diff --git a/openmp/tools/omptest/src/OmptCallbackHandler.cpp b/openmp/tools/omptest/src/OmptCallbackHandler.cpp new file mode 100644 index 0000000000000..0794a1c27a902 --- /dev/null +++ b/openmp/tools/omptest/src/OmptCallbackHandler.cpp @@ -0,0 +1,445 @@ +//===- OmptCallbackHandler.cpp - OMPT Callback handling impl. ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the OMPT callback handling implementations. +/// +//===----------------------------------------------------------------------===// + +#include "OmptCallbackHandler.h" + +using namespace omptest; + +OmptCallbackHandler *Handler = nullptr; + +OmptCallbackHandler &OmptCallbackHandler::get() { + if (Handler == nullptr) + Handler = new OmptCallbackHandler(); + + return *Handler; +} + +void OmptCallbackHandler::subscribe(OmptListener *Listener) { + Subscribers.push_back(Listener); +} + +void OmptCallbackHandler::clearSubscribers() { + replay(); + + Subscribers.clear(); +} + +void OmptCallbackHandler::replay() { + if (!RecordAndReplay) + return; + + for (auto &E : RecordedEvents) + for (const auto &S : Subscribers) + S->notify(std::move(E)); +} + +void OmptCallbackHandler::handleThreadBegin(ompt_thread_t ThreadType, + ompt_data_t *ThreadData) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::ThreadBegin( + "Thread Begin", "", ObserveState::generated, ThreadType)); + return; + } + + // Initial thread event likely to preceed assertion registration, so skip + if (ThreadType == ompt_thread_initial) + return; + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::ThreadBegin( + "Thread Begin", "", ObserveState::generated, ThreadType)); +} + +void OmptCallbackHandler::handleThreadEnd(ompt_data_t *ThreadData) { + if (RecordAndReplay) { + recordEvent( + OmptAssertEvent::ThreadEnd("Thread End", "", ObserveState::generated)); + return; + } + + for (const auto &S : Subscribers) + S->notify( + OmptAssertEvent::ThreadEnd("Thread End", "", ObserveState::generated)); +} + +void OmptCallbackHandler::handleTaskCreate( + ompt_data_t *EncounteringTaskData, + const ompt_frame_t *EncounteringTaskFrame, ompt_data_t *NewTaskData, + int Flags, int HasDependences, const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::TaskCreate( + "Task Create", "", ObserveState::generated, EncounteringTaskData, + EncounteringTaskFrame, NewTaskData, Flags, HasDependences, CodeptrRA)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::TaskCreate( + "Task Create", "", ObserveState::generated, EncounteringTaskData, + EncounteringTaskFrame, NewTaskData, Flags, HasDependences, CodeptrRA)); +} + +void OmptCallbackHandler::handleTaskSchedule(ompt_data_t *PriorTaskData, + ompt_task_status_t PriorTaskStatus, + ompt_data_t *NextTaskData) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::TaskSchedule("Task Schedule", "", + ObserveState::generated)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::TaskSchedule("Task Schedule", "", + ObserveState::generated)); +} + +void OmptCallbackHandler::handleImplicitTask(ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData, + ompt_data_t *TaskData, + unsigned int ActualParallelism, + unsigned int Index, int Flags) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::ImplicitTask( + "Implicit Task", "", ObserveState::generated, Endpoint, ParallelData, + TaskData, ActualParallelism, Index, Flags)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::ImplicitTask( + "Implicit Task", "", ObserveState::generated, Endpoint, ParallelData, + TaskData, ActualParallelism, Index, Flags)); +} + +void OmptCallbackHandler::handleParallelBegin( + ompt_data_t *EncounteringTaskData, + const ompt_frame_t *EncounteringTaskFrame, ompt_data_t *ParallelData, + unsigned int RequestedParallelism, int Flags, const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::ParallelBegin( + "Parallel Begin", "", ObserveState::generated, RequestedParallelism)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::ParallelBegin( + "Parallel Begin", "", ObserveState::generated, RequestedParallelism)); +} + +void OmptCallbackHandler::handleParallelEnd(ompt_data_t *ParallelData, + ompt_data_t *EncounteringTaskData, + int Flags, const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::ParallelEnd("Parallel End", "", + ObserveState::generated)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::ParallelEnd("Parallel End", "", + ObserveState::generated)); +} + +void OmptCallbackHandler::handleDeviceInitialize( + int DeviceNum, const char *Type, ompt_device_t *Device, + ompt_function_lookup_t LookupFn, const char *DocumentationStr) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::DeviceInitialize( + "Device Inititalize", "", ObserveState::generated, DeviceNum, Type, + Device, LookupFn, DocumentationStr)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::DeviceInitialize( + "Device Inititalize", "", ObserveState::generated, DeviceNum, Type, + Device, LookupFn, DocumentationStr)); +} + +void OmptCallbackHandler::handleDeviceFinalize(int DeviceNum) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::DeviceFinalize( + "Device Finalize", "", ObserveState::generated, DeviceNum)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::DeviceFinalize( + "Device Finalize", "", ObserveState::generated, DeviceNum)); +} + +void OmptCallbackHandler::handleTarget(ompt_target_t Kind, + ompt_scope_endpoint_t Endpoint, + int DeviceNum, ompt_data_t *TaskData, + ompt_id_t TargetId, + const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::Target("Target", "", ObserveState::generated, + Kind, Endpoint, DeviceNum, TaskData, + TargetId, CodeptrRA)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::Target("Target", "", ObserveState::generated, + Kind, Endpoint, DeviceNum, TaskData, + TargetId, CodeptrRA)); +} + +void OmptCallbackHandler::handleTargetEmi(ompt_target_t Kind, + ompt_scope_endpoint_t Endpoint, + int DeviceNum, ompt_data_t *TaskData, + ompt_data_t *TargetTaskData, + ompt_data_t *TargetData, + const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::TargetEmi( + "Target EMI", "", ObserveState::generated, Kind, Endpoint, DeviceNum, + TaskData, TargetTaskData, TargetData, CodeptrRA)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::TargetEmi( + "Target EMI", "", ObserveState::generated, Kind, Endpoint, DeviceNum, + TaskData, TargetTaskData, TargetData, CodeptrRA)); +} + +void OmptCallbackHandler::handleTargetSubmit(ompt_id_t TargetId, + ompt_id_t HostOpId, + unsigned int RequestedNumTeams) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::TargetSubmit("Target Submit", "", + ObserveState::generated, TargetId, + HostOpId, RequestedNumTeams)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::TargetSubmit("Target Submit", "", + ObserveState::generated, TargetId, + HostOpId, RequestedNumTeams)); +} + +void OmptCallbackHandler::handleTargetSubmitEmi( + ompt_scope_endpoint_t Endpoint, ompt_data_t *TargetData, + ompt_id_t *HostOpId, unsigned int RequestedNumTeams) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::TargetSubmitEmi( + "Target Submit EMI", "", ObserveState::generated, Endpoint, TargetData, + HostOpId, RequestedNumTeams)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::TargetSubmitEmi( + "Target Submit EMI", "", ObserveState::generated, Endpoint, TargetData, + HostOpId, RequestedNumTeams)); +} + +void OmptCallbackHandler::handleTargetDataOp( + ompt_id_t TargetId, ompt_id_t HostOpId, ompt_target_data_op_t OpType, + void *SrcAddr, int SrcDeviceNum, void *DstAddr, int DstDeviceNum, + size_t Bytes, const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::TargetDataOp( + "Target Data Op", "", ObserveState::generated, TargetId, HostOpId, + OpType, SrcAddr, SrcDeviceNum, DstAddr, DstDeviceNum, Bytes, + CodeptrRA)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::TargetDataOp( + "Target Data Op", "", ObserveState::generated, TargetId, HostOpId, + OpType, SrcAddr, SrcDeviceNum, DstAddr, DstDeviceNum, Bytes, + CodeptrRA)); +} + +void OmptCallbackHandler::handleTargetDataOpEmi( + ompt_scope_endpoint_t Endpoint, ompt_data_t *TargetTaskData, + ompt_data_t *TargetData, ompt_id_t *HostOpId, ompt_target_data_op_t OpType, + void *SrcAddr, int SrcDeviceNum, void *DstAddr, int DstDeviceNum, + size_t Bytes, const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::TargetDataOpEmi( + "Target Data Op EMI", "", ObserveState::generated, Endpoint, + TargetTaskData, TargetData, HostOpId, OpType, SrcAddr, SrcDeviceNum, + DstAddr, DstDeviceNum, Bytes, CodeptrRA)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::TargetDataOpEmi( + "Target Data Op EMI", "", ObserveState::generated, Endpoint, + TargetTaskData, TargetData, HostOpId, OpType, SrcAddr, SrcDeviceNum, + DstAddr, DstDeviceNum, Bytes, CodeptrRA)); +} + +void OmptCallbackHandler::handleDeviceLoad(int DeviceNum, const char *Filename, + int64_t OffsetInFile, + void *VmaInFile, size_t Bytes, + void *HostAddr, void *DeviceAddr, + uint64_t ModuleId) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::DeviceLoad( + "Device Load", "", ObserveState::generated, DeviceNum, Filename, + OffsetInFile, VmaInFile, Bytes, HostAddr, DeviceAddr, ModuleId)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::DeviceLoad( + "Device Load", "", ObserveState::generated, DeviceNum, Filename, + OffsetInFile, VmaInFile, Bytes, HostAddr, DeviceAddr, ModuleId)); +} + +void OmptCallbackHandler::handleDeviceUnload(int DeviceNum, uint64_t ModuleId) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::DeviceUnload("Device Unload", "", + ObserveState::generated)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::DeviceUnload("Device Unload", "", + ObserveState::generated)); +} + +void OmptCallbackHandler::handleBufferRequest(int DeviceNum, + ompt_buffer_t **Buffer, + size_t *Bytes) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::BufferRequest("Buffer Request", "", + ObserveState::generated, + DeviceNum, Buffer, Bytes)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::BufferRequest("Buffer Request", "", + ObserveState::generated, DeviceNum, + Buffer, Bytes)); +} + +void OmptCallbackHandler::handleBufferComplete(int DeviceNum, + ompt_buffer_t *Buffer, + size_t Bytes, + ompt_buffer_cursor_t Begin, + int BufferOwned) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::BufferComplete( + "Buffer Complete", "", ObserveState::generated, DeviceNum, Buffer, + Bytes, Begin, BufferOwned)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::BufferComplete( + "Buffer Complete", "", ObserveState::generated, DeviceNum, Buffer, + Bytes, Begin, BufferOwned)); +} + +void OmptCallbackHandler::handleBufferRecord(ompt_record_ompt_t *Record) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::BufferRecord("Buffer Record", "", + ObserveState::generated, Record)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::BufferRecord("Buffer Record", "", + ObserveState::generated, Record)); +} + +void OmptCallbackHandler::handleBufferRecordDeallocation( + ompt_buffer_t *Buffer) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::BufferRecordDeallocation( + "Buffer Deallocation", "", ObserveState::generated, Buffer)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::BufferRecordDeallocation( + "Buffer Deallocation", "", ObserveState::generated, Buffer)); +} + +void OmptCallbackHandler::handleWork(ompt_work_t WorkType, + ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData, + ompt_data_t *TaskData, uint64_t Count, + const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::Work("Work", "", ObserveState::generated, + WorkType, Endpoint, ParallelData, + TaskData, Count, CodeptrRA)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::Work("Work", "", ObserveState::generated, + WorkType, Endpoint, ParallelData, TaskData, + Count, CodeptrRA)); +} + +void OmptCallbackHandler::handleSyncRegion(ompt_sync_region_t Kind, + ompt_scope_endpoint_t Endpoint, + ompt_data_t *ParallelData, + ompt_data_t *TaskData, + const void *CodeptrRA) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::SyncRegion( + "SyncRegion", "", ObserveState::generated, Kind, Endpoint, ParallelData, + TaskData, CodeptrRA)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::SyncRegion( + "SyncRegion", "", ObserveState::generated, Kind, Endpoint, ParallelData, + TaskData, CodeptrRA)); +} + +void OmptCallbackHandler::handleDispatch(ompt_data_t *ParallelData, + ompt_data_t *TaskData, + ompt_dispatch_t Kind, + ompt_data_t Instance) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::Dispatch("Dispatch", "", + ObserveState::generated, ParallelData, + TaskData, Kind, Instance)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::Dispatch("Dispatch", "", ObserveState::generated, + ParallelData, TaskData, Kind, + Instance)); +} + +void OmptCallbackHandler::handleAssertionSyncPoint( + const std::string &SyncPointName) { + if (RecordAndReplay) { + recordEvent(OmptAssertEvent::AssertionSyncPoint( + "Assertion SyncPoint", "", ObserveState::generated, SyncPointName)); + return; + } + + for (const auto &S : Subscribers) + S->notify(OmptAssertEvent::AssertionSyncPoint( + "Assertion SyncPoint", "", ObserveState::generated, SyncPointName)); +} + +void OmptCallbackHandler::recordEvent(OmptAssertEvent &&Event) { + RecordedEvents.emplace_back(std::forward(Event)); +} diff --git a/openmp/tools/omptest/src/OmptTester.cpp b/openmp/tools/omptest/src/OmptTester.cpp new file mode 100644 index 0000000000000..22de91046fbdc --- /dev/null +++ b/openmp/tools/omptest/src/OmptTester.cpp @@ -0,0 +1,504 @@ +//===- OmptTester.cpp - ompTest OMPT tool implementation --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file represents the core implementation file for the ompTest library. +/// It provides the actual OMPT tool implementation: registers callbacks, etc. +/// OMPT callbacks are passed to their corresponding handler, which in turn +/// notifies all registered asserters. +/// +//===----------------------------------------------------------------------===// + +#include "OmptTester.h" + +#include +#include +#include +#include + +using namespace omptest; + +// Callback handler, which receives and relays OMPT callbacks +extern OmptCallbackHandler *Handler; + +// EventListener, which will actually print the OMPT events +static OmptEventReporter *EventReporter; + +// From openmp/runtime/test/ompt/callback.h +#define register_ompt_callback_t(name, type) \ + do { \ + type f_##name = &on_##name; \ + if (ompt_set_callback(name, (ompt_callback_t)f_##name) == ompt_set_never) \ + printf("0: Could not register callback '" #name "'\n"); \ + } while (0) + +#define register_ompt_callback(name) register_ompt_callback_t(name, name##_t) + +#define OMPT_BUFFER_REQUEST_SIZE 256 + +#ifdef OPENMP_LIBOMPTEST_BUILD_STANDALONE +std::map TestRegistrar::Tests; +#endif + +static std::atomic NextOpId{0x8000000000000001}; +static bool UseEMICallbacks = false; +static bool UseTracing = false; +static bool RunAsTestSuite = false; +static bool ColoredLog = false; + +// OMPT entry point handles +static ompt_set_trace_ompt_t ompt_set_trace_ompt = 0; +static ompt_start_trace_t ompt_start_trace = 0; +static ompt_flush_trace_t ompt_flush_trace = 0; +static ompt_stop_trace_t ompt_stop_trace = 0; +static ompt_get_record_ompt_t ompt_get_record_ompt = 0; +static ompt_advance_buffer_cursor_t ompt_advance_buffer_cursor = 0; +static ompt_get_record_type_t ompt_get_record_type_fn = 0; + +// OMPT device side tracing: Currently traced devices +typedef std::unordered_set OmptDeviceSetTy; +typedef std::unique_ptr OmptDeviceSetPtrTy; +static OmptDeviceSetPtrTy TracedDevices; + +// OMPT callbacks + +// Trace record callbacks +static void on_ompt_callback_buffer_request(int device_num, + ompt_buffer_t **buffer, + size_t *bytes) { + *bytes = OMPT_BUFFER_REQUEST_SIZE; + *buffer = malloc(*bytes); + OmptCallbackHandler::get().handleBufferRequest(device_num, buffer, bytes); +} + +// Note: This callback must handle a null begin cursor. Currently, +// ompt_get_record_ompt, print_record_ompt, and +// ompt_advance_buffer_cursor handle a null cursor. +static void on_ompt_callback_buffer_complete( + int device_num, ompt_buffer_t *buffer, + size_t bytes, /* bytes returned in this callback */ + ompt_buffer_cursor_t begin, int buffer_owned) { + OmptCallbackHandler::get().handleBufferComplete(device_num, buffer, bytes, + begin, buffer_owned); + + int Status = 1; + ompt_buffer_cursor_t CurrentPos = begin; + while (Status) { + ompt_record_ompt_t *Record = ompt_get_record_ompt(buffer, CurrentPos); + if (ompt_get_record_type_fn(buffer, CurrentPos) != ompt_record_ompt) { + printf("WARNING: received non-ompt type buffer object\n"); + } + // TODO: Sometimes it may happen that the retrieved record may be null?! + // Only handle non-null records + if (Record != nullptr) + OmptCallbackHandler::get().handleBufferRecord(Record); + Status = ompt_advance_buffer_cursor(/*device=*/NULL, buffer, bytes, + CurrentPos, &CurrentPos); + } + if (buffer_owned) { + OmptCallbackHandler::get().handleBufferRecordDeallocation(buffer); + free(buffer); + } +} + +static ompt_set_result_t set_trace_ompt(ompt_device_t *Device) { + if (!ompt_set_trace_ompt) + return ompt_set_error; + + if (UseEMICallbacks) { + ompt_set_trace_ompt(Device, /*enable=*/1, + /*etype=*/ompt_callback_target_emi); + ompt_set_trace_ompt(Device, /*enable=*/1, + /*etype=*/ompt_callback_target_data_op_emi); + ompt_set_trace_ompt(Device, /*enable=*/1, + /*etype=*/ompt_callback_target_submit_emi); + } else { + ompt_set_trace_ompt(Device, /*enable=*/1, /*etype=*/ompt_callback_target); + ompt_set_trace_ompt(Device, /*enable=*/1, + /*etype=*/ompt_callback_target_data_op); + ompt_set_trace_ompt(Device, /*enable=*/1, + /*etype=*/ompt_callback_target_submit); + } + + return ompt_set_always; +} + +/////// HOST-RELATED ////// + +static void on_ompt_callback_thread_begin(ompt_thread_t thread_type, + ompt_data_t *thread_data) { + OmptCallbackHandler::get().handleThreadBegin(thread_type, thread_data); +} + +static void on_ompt_callback_thread_end(ompt_data_t *thread_data) { + OmptCallbackHandler::get().handleThreadEnd(thread_data); +} + +static void on_ompt_callback_parallel_begin( + ompt_data_t *encountering_task_data, + const ompt_frame_t *encountering_task_frame, ompt_data_t *parallel_data, + unsigned int requested_parallelism, int flags, const void *codeptr_ra) { + OmptCallbackHandler::get().handleParallelBegin( + encountering_task_data, encountering_task_frame, parallel_data, + requested_parallelism, flags, codeptr_ra); +} + +static void on_ompt_callback_parallel_end(ompt_data_t *parallel_data, + ompt_data_t *encountering_task_data, + int flags, const void *codeptr_ra) { + OmptCallbackHandler::get().handleParallelEnd( + parallel_data, encountering_task_data, flags, codeptr_ra); +} + +static void +on_ompt_callback_task_create(ompt_data_t *encountering_task_data, + const ompt_frame_t *encountering_task_frame, + ompt_data_t *new_task_data, int flags, + int has_dependences, const void *codeptr_ra) { + OmptCallbackHandler::get().handleTaskCreate( + encountering_task_data, encountering_task_frame, new_task_data, flags, + has_dependences, codeptr_ra); +} + +static void on_ompt_callback_task_schedule(ompt_data_t *prior_task_data, + ompt_task_status_t prior_task_status, + ompt_data_t *next_task_data) { + OmptCallbackHandler::get().handleTaskSchedule( + prior_task_data, prior_task_status, next_task_data); +} + +static void on_ompt_callback_implicit_task(ompt_scope_endpoint_t endpoint, + ompt_data_t *parallel_data, + ompt_data_t *task_data, + unsigned int actual_parallelism, + unsigned int index, int flags) { + OmptCallbackHandler::get().handleImplicitTask( + endpoint, parallel_data, task_data, actual_parallelism, index, flags); +} + +// Callbacks as of Table 19.4, which are not considered required for a minimal +// conforming OMPT implementation. +static void on_ompt_callback_work(ompt_work_t work_type, + ompt_scope_endpoint_t endpoint, + ompt_data_t *parallel_data, + ompt_data_t *task_data, uint64_t count, + const void *codeptr_ra) { + OmptCallbackHandler::get().handleWork(work_type, endpoint, parallel_data, + task_data, count, codeptr_ra); +} + +static void on_ompt_callback_dispatch(ompt_data_t *parallel_data, + ompt_data_t *task_data, + ompt_dispatch_t kind, + ompt_data_t instance) { + OmptCallbackHandler::get().handleDispatch(parallel_data, task_data, kind, + instance); +} + +static void on_ompt_callback_sync_region(ompt_sync_region_t kind, + ompt_scope_endpoint_t endpoint, + ompt_data_t *parallel_data, + ompt_data_t *task_data, + const void *codeptr_ra) { + OmptCallbackHandler::get().handleSyncRegion(kind, endpoint, parallel_data, + task_data, codeptr_ra); +} + +/////// DEVICE-RELATED ////// + +// Synchronous callbacks +static void on_ompt_callback_device_initialize(int device_num, const char *type, + ompt_device_t *device, + ompt_function_lookup_t lookup, + const char *documentation) { + OmptCallbackHandler::get().handleDeviceInitialize(device_num, type, device, + lookup, documentation); + if (!UseTracing) + return; + + if (!lookup) { + printf("Trace collection disabled on device %d\n", device_num); + return; + } + + ompt_set_trace_ompt = (ompt_set_trace_ompt_t)lookup("ompt_set_trace_ompt"); + ompt_start_trace = (ompt_start_trace_t)lookup("ompt_start_trace"); + ompt_flush_trace = (ompt_flush_trace_t)lookup("ompt_flush_trace"); + ompt_stop_trace = (ompt_stop_trace_t)lookup("ompt_stop_trace"); + ompt_get_record_ompt = (ompt_get_record_ompt_t)lookup("ompt_get_record_ompt"); + ompt_advance_buffer_cursor = + (ompt_advance_buffer_cursor_t)lookup("ompt_advance_buffer_cursor"); + + ompt_get_record_type_fn = + (ompt_get_record_type_t)lookup("ompt_get_record_type"); + if (!ompt_get_record_type_fn) { + printf("WARNING: No function ompt_get_record_type found in device " + "callbacks\n"); + } + + static bool IsDeviceMapInitialized = false; + if (!IsDeviceMapInitialized) { + TracedDevices = std::make_unique(); + IsDeviceMapInitialized = true; + } + + set_trace_ompt(device); + + // In many scenarios, this will be a good place to start the + // trace. If start_trace is called from the main program before this + // callback is dispatched, the start_trace handle will be null. This + // is because this device_init callback is invoked during the first + // target construct implementation. + + start_trace(device); +} + +static void on_ompt_callback_device_finalize(int device_num) { + OmptCallbackHandler::get().handleDeviceFinalize(device_num); +} + +static void on_ompt_callback_device_load(int device_num, const char *filename, + int64_t offset_in_file, + void *vma_in_file, size_t bytes, + void *host_addr, void *device_addr, + uint64_t module_id) { + OmptCallbackHandler::get().handleDeviceLoad( + device_num, filename, offset_in_file, vma_in_file, bytes, host_addr, + device_addr, module_id); +} + +static void on_ompt_callback_device_unload(int device_num, uint64_t module_id) { + OmptCallbackHandler::get().handleDeviceUnload(device_num, module_id); +} + +static void on_ompt_callback_target_data_op( + ompt_id_t target_id, ompt_id_t host_op_id, ompt_target_data_op_t optype, + void *src_addr, int src_device_num, void *dest_addr, int dest_device_num, + size_t bytes, const void *codeptr_ra) { + OmptCallbackHandler::get().handleTargetDataOp( + target_id, host_op_id, optype, src_addr, src_device_num, dest_addr, + dest_device_num, bytes, codeptr_ra); +} + +static void on_ompt_callback_target(ompt_target_t kind, + ompt_scope_endpoint_t endpoint, + int device_num, ompt_data_t *task_data, + ompt_id_t target_id, + const void *codeptr_ra) { + OmptCallbackHandler::get().handleTarget(kind, endpoint, device_num, task_data, + target_id, codeptr_ra); +} + +static void on_ompt_callback_target_submit(ompt_id_t target_id, + ompt_id_t host_op_id, + unsigned int requested_num_teams) { + OmptCallbackHandler::get().handleTargetSubmit(target_id, host_op_id, + requested_num_teams); +} + +static void on_ompt_callback_target_data_op_emi( + ompt_scope_endpoint_t endpoint, ompt_data_t *target_task_data, + ompt_data_t *target_data, ompt_id_t *host_op_id, + ompt_target_data_op_t optype, void *src_addr, int src_device_num, + void *dest_addr, int dest_device_num, size_t bytes, + const void *codeptr_ra) { + assert(codeptr_ra != 0 && "Unexpected null codeptr"); + // Both src and dest must not be null + // However, for omp_target_alloc only the END call holds a value for one of + // the two entries + if (optype != ompt_target_data_alloc) + assert((src_addr != 0 || dest_addr != 0) && "Both src and dest addr null"); + if (endpoint == ompt_scope_begin) + *host_op_id = NextOpId.fetch_add(1, std::memory_order_relaxed); + OmptCallbackHandler::get().handleTargetDataOpEmi( + endpoint, target_task_data, target_data, host_op_id, optype, src_addr, + src_device_num, dest_addr, dest_device_num, bytes, codeptr_ra); +} + +static void on_ompt_callback_target_emi(ompt_target_t kind, + ompt_scope_endpoint_t endpoint, + int device_num, ompt_data_t *task_data, + ompt_data_t *target_task_data, + ompt_data_t *target_data, + const void *codeptr_ra) { + assert(codeptr_ra != 0 && "Unexpected null codeptr"); + if (endpoint == ompt_scope_begin) + target_data->value = NextOpId.fetch_add(1, std::memory_order_relaxed); + OmptCallbackHandler::get().handleTargetEmi(kind, endpoint, device_num, + task_data, target_task_data, + target_data, codeptr_ra); +} + +static void on_ompt_callback_target_submit_emi( + ompt_scope_endpoint_t endpoint, ompt_data_t *target_data, + ompt_id_t *host_op_id, unsigned int requested_num_teams) { + OmptCallbackHandler::get().handleTargetSubmitEmi( + endpoint, target_data, host_op_id, requested_num_teams); +} + +static void on_ompt_callback_target_map(ompt_id_t target_id, + unsigned int nitems, void **host_addr, + void **device_addr, size_t *bytes, + unsigned int *mapping_flags, + const void *codeptr_ra) { + assert(0 && "Target map callback is unimplemented"); +} + +static void on_ompt_callback_target_map_emi(ompt_data_t *target_data, + unsigned int nitems, + void **host_addr, + void **device_addr, size_t *bytes, + unsigned int *mapping_flags, + const void *codeptr_ra) { + assert(0 && "Target map emi callback is unimplemented"); +} + +/// Load the value of a given boolean environmental variable. +bool getBoolEnvironmentVariable(const char *VariableName) { + if (VariableName == nullptr) + return false; + if (const char *EnvValue = std::getenv(VariableName)) { + std::string S{EnvValue}; + for (auto &C : S) + C = (char)std::tolower(C); + if (S == "1" || S == "on" || S == "true" || S == "yes") + return true; + } + return false; +} + +/// Called by the OMP runtime to initialize the OMPT +int ompt_initialize(ompt_function_lookup_t lookup, int initial_device_num, + ompt_data_t *tool_data) { + ompt_set_callback_t ompt_set_callback = nullptr; + ompt_set_callback = (ompt_set_callback_t)lookup("ompt_set_callback"); + if (!ompt_set_callback) + return 0; // failure + + UseEMICallbacks = getBoolEnvironmentVariable("OMPTEST_USE_OMPT_EMI"); + UseTracing = getBoolEnvironmentVariable("OMPTEST_USE_OMPT_TRACING"); + RunAsTestSuite = getBoolEnvironmentVariable("OMPTEST_RUN_AS_TESTSUITE"); + ColoredLog = getBoolEnvironmentVariable("OMPTEST_LOG_COLORED"); + + register_ompt_callback(ompt_callback_thread_begin); + register_ompt_callback(ompt_callback_thread_end); + register_ompt_callback(ompt_callback_parallel_begin); + register_ompt_callback(ompt_callback_parallel_end); + register_ompt_callback(ompt_callback_work); + // register_ompt_callback(ompt_callback_dispatch); + register_ompt_callback(ompt_callback_task_create); + // register_ompt_callback(ompt_callback_dependences); + // register_ompt_callback(ompt_callback_task_dependence); + register_ompt_callback(ompt_callback_task_schedule); + register_ompt_callback(ompt_callback_implicit_task); + // register_ompt_callback(ompt_callback_masked); + register_ompt_callback(ompt_callback_sync_region); + // register_ompt_callback(ompt_callback_mutex_acquire); + // register_ompt_callback(ompt_callback_mutex); + // register_ompt_callback(ompt_callback_nestLock); + // register_ompt_callback(ompt_callback_flush); + // register_ompt_callback(ompt_callback_cancel); + register_ompt_callback(ompt_callback_device_initialize); + register_ompt_callback(ompt_callback_device_finalize); + register_ompt_callback(ompt_callback_device_load); + register_ompt_callback(ompt_callback_device_unload); + + if (UseEMICallbacks) { + register_ompt_callback(ompt_callback_target_emi); + register_ompt_callback(ompt_callback_target_submit_emi); + register_ompt_callback(ompt_callback_target_data_op_emi); + register_ompt_callback(ompt_callback_target_map_emi); + } else { + register_ompt_callback(ompt_callback_target); + register_ompt_callback(ompt_callback_target_submit); + register_ompt_callback(ompt_callback_target_data_op); + register_ompt_callback(ompt_callback_target_map); + } + + // Construct & subscribe the reporter, so it will be notified of events + EventReporter = new OmptEventReporter(); + OmptCallbackHandler::get().subscribe(EventReporter); + + if (RunAsTestSuite) + EventReporter->setActive(false); + + return 1; // success +} + +void ompt_finalize(ompt_data_t *tool_data) { + assert(Handler && "Callback handler should be present at this point"); + assert(EventReporter && "EventReporter should be present at this point"); + delete Handler; + delete EventReporter; +} + +#ifdef __cplusplus +extern "C" { +#endif +/// Called from the OMP Runtime to start / initialize the tool +ompt_start_tool_result_t *ompt_start_tool(unsigned int omp_version, + const char *runtime_version) { + static ompt_start_tool_result_t ompt_start_tool_result = { + &ompt_initialize, &ompt_finalize, {0}}; + return &ompt_start_tool_result; +} + +int start_trace(ompt_device_t *Device) { + if (!ompt_start_trace) + return 0; + + // This device will be traced + assert(TracedDevices->find(Device) == TracedDevices->end() && + "Device already present in the map"); + TracedDevices->insert(Device); + + return ompt_start_trace(Device, &on_ompt_callback_buffer_request, + &on_ompt_callback_buffer_complete); +} + +int flush_trace(ompt_device_t *Device) { + if (!ompt_flush_trace) + return 0; + return ompt_flush_trace(Device); +} + +int flush_traced_devices() { + if (!ompt_flush_trace || TracedDevices == nullptr) + return 0; + + size_t NumFlushedDevices = 0; + for (auto Device : *TracedDevices) + if (ompt_flush_trace(Device) == 1) + ++NumFlushedDevices; + + // Provide time to process triggered assert events + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + + return (NumFlushedDevices == TracedDevices->size()); +} + +int stop_trace(ompt_device_t *Device) { + if (!ompt_stop_trace) + return 0; + + // This device will not be traced anymore + assert(TracedDevices->find(Device) != TracedDevices->end() && + "Device not present in the map"); + TracedDevices->erase(Device); + + return ompt_stop_trace(Device); +} + +// This is primarily used to stop unwanted prints from happening. +void libomptest_global_eventreporter_set_active(bool State) { + assert(EventReporter && "EventReporter should be present at this point"); + EventReporter->setActive(State); +} +#ifdef __cplusplus +} +#endif diff --git a/openmp/tools/omptest/src/OmptTesterStandalone.cpp b/openmp/tools/omptest/src/OmptTesterStandalone.cpp new file mode 100644 index 0000000000000..d4f68b4576536 --- /dev/null +++ b/openmp/tools/omptest/src/OmptTesterStandalone.cpp @@ -0,0 +1,147 @@ +//===- OmptTesterStandalone.cpp - Standalone unit testing impl. -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file represents the 'standalone' ompTest unit testing core +/// implementation, defining the general test suite and test case execution. +/// +//===----------------------------------------------------------------------===// + +#include "OmptTesterStandalone.h" +#include "OmptCallbackHandler.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace omptest; + +Error TestCase::exec() { + Error E; + E.Fail = false; + + if (IsDisabled) + return E; + + OmptCallbackHandler::get().subscribe(SequenceAsserter.get()); + OmptCallbackHandler::get().subscribe(SetAsserter.get()); + OmptCallbackHandler::get().subscribe(EventReporter.get()); + + execImpl(); + + // Actively flush potential in-flight trace records + flush_traced_devices(); + + // We remove subscribers to not be notified of events after our test case + // finished. + OmptCallbackHandler::get().clearSubscribers(); + omptest::AssertState SequenceResultState = SequenceAsserter->checkState(); + omptest::AssertState SetResultState = SetAsserter->checkState(); + bool AnyFail = SequenceResultState == omptest::AssertState::fail || + SetResultState == omptest::AssertState::fail; + bool AllPass = SequenceResultState == omptest::AssertState::pass && + SetResultState == omptest::AssertState::pass; + if (ExpectedState == omptest::AssertState::pass && AnyFail) + E.Fail = true; + else if (ExpectedState == omptest::AssertState::fail && AllPass) + E.Fail = true; + if (AnyFail) + ResultState = omptest::AssertState::fail; + return E; +} + +TestSuite::TestSuite(TestSuite &&O) { + Name = O.Name; + TestCases.swap(O.TestCases); +} + +void TestSuite::setup() {} + +void TestSuite::teardown() {} + +TestSuite::TestCaseVec::iterator TestSuite::begin() { + return TestCases.begin(); +} + +TestSuite::TestCaseVec::iterator TestSuite::end() { return TestCases.end(); } + +TestRegistrar &TestRegistrar::get() { + static TestRegistrar TR; + return TR; +} + +std::vector TestRegistrar::getTestSuites() { + std::vector TSs; + for (auto &[k, v] : Tests) + TSs.emplace_back(std::move(v)); + return TSs; +} + +void TestRegistrar::addCaseToSuite(TestCase *TC, std::string TSName) { + auto &TS = Tests[TSName]; + if (TS.Name.empty()) + TS.Name = TSName; + TS.TestCases.emplace_back(TC); +} + +Registerer::Registerer(TestCase *TC, const std::string SuiteName) { + std::cout << "Adding " << TC->Name << " to " << SuiteName << std::endl; + TestRegistrar::get().addCaseToSuite(TC, SuiteName); +} + +int Runner::run() { + int ErrorCount = 0; + for (auto &TS : TestSuites) { + std::cout << "\n======\nExecuting for " << TS.Name << std::endl; + TS.setup(); + for (auto &TC : TS) { + std::cout << "\nExecuting " << TC->Name << std::endl; + if (Error Err = TC->exec()) { + reportError(Err); + abortOrKeepGoing(); + ++ErrorCount; + } + } + TS.teardown(); + } + printSummary(); + return ErrorCount; +} + +void Runner::reportError(const Error &Err) {} + +void Runner::abortOrKeepGoing() {} + +void Runner::printSummary() { + std::cout << "\n====== SUMMARY\n"; + for (auto &TS : TestSuites) { + std::cout << " - " << TS.Name; + for (auto &TC : TS) { + std::string Result; + if (TC->IsDisabled) { + Result = "-#-#-"; + } else if (TC->ResultState == TC->ExpectedState) { + if (TC->ResultState == omptest::AssertState::pass) + Result = "PASS"; + else if (TC->ResultState == omptest::AssertState::fail) + Result = "XFAIL"; + } else { + if (TC->ResultState == omptest::AssertState::fail) + Result = "FAIL"; + else if (TC->ResultState == omptest::AssertState::pass) + Result = "UPASS"; + } + std::cout << "\n " << std::setw(5) << Result << " : " << TC->Name; + } + std::cout << std::endl; + } +} diff --git a/openmp/tools/omptest/test/CMakeLists.txt b/openmp/tools/omptest/test/CMakeLists.txt new file mode 100644 index 0000000000000..427893313cc67 --- /dev/null +++ b/openmp/tools/omptest/test/CMakeLists.txt @@ -0,0 +1,28 @@ +##===----------------------------------------------------------------------===## +# +# Add ompTest unit tests to check-openmp. +# +##===----------------------------------------------------------------------===## + +# Target: ompTest library unit tests +file(GLOB UNITTEST_SOURCES "unittests/*.cpp") +add_executable(omptest-unittests ${UNITTEST_SOURCES}) + +# Add local and LLVM-provided GoogleTest include directories. +target_include_directories(omptest-unittests PRIVATE + ../include + ${LLVM_THIRD_PARTY_DIR}/unittest/googletest/include) + +target_link_libraries(omptest-unittests PRIVATE omptest) + +set_target_properties(omptest-unittests PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +# Add ompTest unit tests to check-openmp +add_openmp_testsuite(check-ompt-omptest "Running OMPT ompTest unit tests" + ${CMAKE_CURRENT_BINARY_DIR} DEPENDS omptest-unittests) + +# Configure the lit.site.cfg.in file +set(AUTO_GEN_COMMENT "## Autogenerated by OPENMP_TOOLS_OMPTEST_TEST " + "configuration.\n# Do not edit!") +configure_file(lit.site.cfg.in lit.site.cfg @ONLY) diff --git a/openmp/tools/omptest/test/lit.cfg b/openmp/tools/omptest/test/lit.cfg new file mode 100644 index 0000000000000..69c401aed83b8 --- /dev/null +++ b/openmp/tools/omptest/test/lit.cfg @@ -0,0 +1,26 @@ +# -*- Python -*- vim: set ft=python ts=4 sw=4 expandtab tw=79: +# Configuration file for the 'lit' test runner. + +import os +import lit.formats + +# Tell pylint that we know config and lit_config exist somewhere. +if 'PYLINT_IMPORT' in os.environ: + config = object() + lit_config = object() + +# name: The name of this test suite. +config.name = 'OMPT ompTest' + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = [''] + +# test_source_root: The root path where tests are located. +config.test_source_root = config.test_obj_root + +# test_exec_root: The root object directory where output is placed +config.test_exec_root = config.test_obj_root + +# test format, match (omptest-)unittests +# Matched binaries (GoogleTests) are executed +config.test_format = lit.formats.GoogleTest(".", "unittests") diff --git a/openmp/tools/omptest/test/lit.site.cfg.in b/openmp/tools/omptest/test/lit.site.cfg.in new file mode 100644 index 0000000000000..4fa8c7e349681 --- /dev/null +++ b/openmp/tools/omptest/test/lit.site.cfg.in @@ -0,0 +1,9 @@ +@AUTO_GEN_COMMENT@ + +config.test_obj_root = "@CMAKE_CURRENT_BINARY_DIR@" + +import lit.llvm +lit.llvm.initialize(lit_config, config) + +# Let the main config do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/openmp/tools/omptest/test/unittests/asserter-seq-test.cpp b/openmp/tools/omptest/test/unittests/asserter-seq-test.cpp new file mode 100644 index 0000000000000..34ceb7597b791 --- /dev/null +++ b/openmp/tools/omptest/test/unittests/asserter-seq-test.cpp @@ -0,0 +1,358 @@ +#include "OmptAliases.h" +#include "OmptAsserter.h" +#include +#include + +#include "gtest/gtest.h" + +using namespace omptest; +using OAE = omptest::OmptAssertEvent; +using OS = omptest::ObserveState; + +/// SequencedAsserter test-fixture class to avoid code duplication among tests. +class OmptSequencedAsserterTest : public testing::Test { +protected: + OmptSequencedAsserterTest() { + // Construct default sequenced asserter + SeqAsserter = std::make_unique(); + + // Silence all potential log prints + SeqAsserter->getLog()->setLoggingLevel(logging::Level::SILENT); + } + + std::unique_ptr SeqAsserter; +}; + +TEST_F(OmptSequencedAsserterTest, DefaultState) { + // Assertion should neither start as 'deactivated' nor 'suspended' + ASSERT_EQ(SeqAsserter->isActive(), true); + ASSERT_EQ(SeqAsserter->AssertionSuspended, false); + + // Assertion should begin with event ID zero + ASSERT_EQ(SeqAsserter->NextEvent, 0); + + // Assertion should begin without previous notifications or assertions + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + + // There should be no expected events + ASSERT_EQ(SeqAsserter->Events.empty(), true); + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 0); + + // Default mode should be strict + ASSERT_NE(SeqAsserter->getOperationMode(), AssertMode::relaxed); + ASSERT_EQ(SeqAsserter->getOperationMode(), AssertMode::strict); + + // Default state should be passing + ASSERT_NE(SeqAsserter->getState(), AssertState::fail); + ASSERT_EQ(SeqAsserter->getState(), AssertState::pass); + ASSERT_NE(SeqAsserter->checkState(), AssertState::fail); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); +} + +TEST_F(OmptSequencedAsserterTest, IgnoreNotificationsWhenEmpty) { + // ParallelBegin events are suppressed by default + auto SuppressedEvent = OAE::ParallelBegin( + /*Name=*/"ParBegin", /*Group=*/"", /*Expected=*/OS::always, + /*NumThreads=*/3); + + // DeviceFinalize events are not ignored by default + auto IgnoredEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + + // Situation: There is nothing to assert. + // Result: All notifications are ignored. + // Hence, check that the perceived count of notifications remains unchanged + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + + SeqAsserter->notify(std::move(SuppressedEvent)); + + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + SeqAsserter->notify(std::move(IgnoredEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); +} + +TEST_F(OmptSequencedAsserterTest, IgnoreNotificationsWhileDeactivated) { + auto ExpectedEvent = OAE::DeviceUnload( + /*Name=*/"DevUnload", /*Group=*/"", /*Expected=*/OS::always); + SeqAsserter->insert(std::move(ExpectedEvent)); + ASSERT_EQ(SeqAsserter->Events.empty(), false); + + // Deactivate asserter, effectively ignoring notifications + SeqAsserter->setActive(false); + ASSERT_EQ(SeqAsserter->isActive(), false); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + + // DeviceFinalize events are not ignored by default + auto IgnoredEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + SeqAsserter->notify(std::move(IgnoredEvent)); + + // Assertion was deactivated: No change + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + + SeqAsserter->setActive(true); + ASSERT_EQ(SeqAsserter->isActive(), true); + + auto ObservedEvent = OAE::DeviceUnload( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always); + SeqAsserter->notify(std::move(ObservedEvent)); + + // Assertion was activated, one notification expected + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 1); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); +} + +TEST_F(OmptSequencedAsserterTest, AddEvent) { + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 0); + auto ExpectedEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + SeqAsserter->insert(std::move(ExpectedEvent)); + // Sanity check: Notifications should not be triggered + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + // Adding an expected event must change the event count but not the state + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 1); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + ASSERT_EQ(SeqAsserter->getState(), AssertState::pass); +} + +TEST_F(OmptSequencedAsserterTest, AddEventIgnoreSuppressed) { + auto ExpectedEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + SeqAsserter->insert(std::move(ExpectedEvent)); + // ParallelBegin events are suppressed by default + auto SuppressedEvent = OAE::ParallelBegin( + /*Name=*/"ParBegin", /*Group=*/"", /*Expected=*/OS::always, + /*NumThreads=*/3); + // Situation: There is one expected event and ParallelBegins are suppressed. + // Notification count remains unchanged for suppressed events + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + SeqAsserter->notify(std::move(SuppressedEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + ASSERT_EQ(SeqAsserter->getState(), AssertState::pass); +} + +TEST_F(OmptSequencedAsserterTest, AddEventObservePass) { + auto ExpectedEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + SeqAsserter->insert(std::move(ExpectedEvent)); + // DeviceFinalize events are not ignored by default + auto ObservedEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + SeqAsserter->notify(std::move(ObservedEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 1); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); +} + +TEST_F(OmptSequencedAsserterTest, AddEventObserveFail) { + auto ExpectedEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + SeqAsserter->insert(std::move(ExpectedEvent)); + // DeviceFinalize events are not ignored by default + // Provide wrong DeviceNum + auto ObservedEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/23); + + SeqAsserter->notify(std::move(ObservedEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + // Observed and expected event do not match: Fail + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::fail); +} + +TEST_F(OmptSequencedAsserterTest, AddEventObserveDifferentType) { + auto ExpectedEvent = OAE::DeviceUnload( + /*Name=*/"DevUnload", /*Group=*/"", /*Expected=*/OS::always); + SeqAsserter->insert(std::move(ExpectedEvent)); + // DeviceFinalize events are not ignored by default + auto ObservedEvent = OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7); + + SeqAsserter->notify(std::move(ObservedEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + // Observed and expected event do not match: Fail + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::fail); +} + +TEST_F(OmptSequencedAsserterTest, CheckTargetGroupNoEffect) { + // Situation: Groups are designed to be used as an indicator -WITHIN- target + // regions. Hence, comparing two target regions w.r.t. their groups has no + // effect on pass or fail. + + auto ExpectedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, + /*DeviceNum=*/7, /*TaskData=*/nullptr, /*TargetId=*/23, + /*CodeptrRA=*/nullptr); + SeqAsserter->insert(std::move(ExpectedEvent)); + ASSERT_EQ(SeqAsserter->Events.empty(), false); + + // Deactivate asserter, effectively ignoring notifications + SeqAsserter->setActive(false); + ASSERT_EQ(SeqAsserter->isActive(), false); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + + // Target events are not ignored by default + auto ObservedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, /*DeviceNum=*/7, + /*TaskData=*/nullptr, /*TargetId=*/23, /*CodeptrRA=*/nullptr); + SeqAsserter->notify(std::move(ObservedEvent)); + + // Assertion was deactivated: No change + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 1); + + // Re-activate asserter + SeqAsserter->setActive(true); + ASSERT_EQ(SeqAsserter->isActive(), true); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + + // Actually observe a target event from "AnotherGroup" + auto AnotherObservedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"AnotherGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, /*DeviceNum=*/7, + /*TaskData=*/nullptr, /*TargetId=*/23, /*CodeptrRA=*/nullptr); + SeqAsserter->notify(std::move(AnotherObservedEvent)); + + // Observed all expected events; groups of target regions do not affect pass + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 1); + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 0); + + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); +} + +TEST_F(OmptSequencedAsserterTest, CheckSyncPoint) { + auto ExpectedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, + /*DeviceNum=*/7, /*TaskData=*/nullptr, /*TargetId=*/23, + /*CodeptrRA=*/nullptr); + SeqAsserter->insert(std::move(ExpectedEvent)); + ASSERT_EQ(SeqAsserter->Events.empty(), false); + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 1); + + // Target events are not ignored by default + auto ObservedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, /*DeviceNum=*/7, + /*TaskData=*/nullptr, /*TargetId=*/23, /*CodeptrRA=*/nullptr); + SeqAsserter->notify(std::move(ObservedEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + + SeqAsserter->notify(OAE::AssertionSyncPoint( + /*Name=*/"", /*Group=*/"", /*Expected=*/OS::always, + /*SyncPointName=*/"SyncPoint 1")); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 2); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 1); + + // All events processed: SyncPoint "passes" + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); + + auto AnotherExpectedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, + /*DeviceNum=*/7, /*TaskData=*/nullptr, /*TargetId=*/23, + /*CodeptrRA=*/nullptr); + + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 0); + SeqAsserter->insert(std::move(AnotherExpectedEvent)); + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 1); + + // Remaining events present: SyncPoint "fails" + SeqAsserter->notify(OAE::AssertionSyncPoint( + /*Name=*/"", /*Group=*/"", /*Expected=*/OS::always, + /*SyncPointName=*/"SyncPoint 2")); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::fail); +} + +TEST_F(OmptSequencedAsserterTest, CheckExcessNotify) { + auto ExpectedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, + /*DeviceNum=*/7, /*TaskData=*/nullptr, /*TargetId=*/23, + /*CodeptrRA=*/nullptr); + SeqAsserter->insert(std::move(ExpectedEvent)); + ASSERT_EQ(SeqAsserter->Events.empty(), false); + ASSERT_EQ(SeqAsserter->getRemainingEventCount(), 1); + + // Target events are not ignored by default + auto ObservedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, /*DeviceNum=*/7, + /*TaskData=*/nullptr, /*TargetId=*/23, /*CodeptrRA=*/nullptr); + SeqAsserter->notify(std::move(ObservedEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + + // All events processed: pass + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); + + // Target events are not ignored by default + auto AnotherObservedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, /*DeviceNum=*/7, + /*TaskData=*/nullptr, /*TargetId=*/23, /*CodeptrRA=*/nullptr); + + // No more events expected: notify "fails" + SeqAsserter->notify(std::move(AnotherObservedEvent)); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 2); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::fail); +} + +TEST_F(OmptSequencedAsserterTest, CheckSuspend) { + SeqAsserter->insert(OAE::AssertionSuspend( + /*Name=*/"", /*Group=*/"", /*Expected=*/OS::never)); + ASSERT_EQ(SeqAsserter->Events.empty(), false); + + // Being notified while the next expected event is a "suspend" should change + // the asserter's state + ASSERT_EQ(SeqAsserter->getNotificationCount(), 0); + ASSERT_EQ(SeqAsserter->AssertionSuspended, false); + SeqAsserter->notify(OAE::DeviceFinalize( + /*Name=*/"DevFini", /*Group=*/"", /*Expected=*/OS::always, + /*DeviceNum=*/7)); + ASSERT_EQ(SeqAsserter->AssertionSuspended, true); + ASSERT_EQ(SeqAsserter->getNotificationCount(), 1); + + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 0); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); + + auto ExpectedEvent = OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, + /*DeviceNum=*/7, /*TaskData=*/nullptr, /*TargetId=*/23, + /*CodeptrRA=*/nullptr); + SeqAsserter->insert(std::move(ExpectedEvent)); + + // Being notified with an observed event, which matches the next expected + // event, resumes assertion (suspended = false) + ASSERT_EQ(SeqAsserter->AssertionSuspended, true); + SeqAsserter->notify(OAE::Target( + /*Name=*/"Target", /*Group=*/"MyTargetGroup", /*Expected=*/OS::always, + /*Kind=*/TARGET, /*Endpoint=*/BEGIN, + /*DeviceNum=*/7, /*TaskData=*/nullptr, /*TargetId=*/23, + /*CodeptrRA=*/nullptr)); + ASSERT_EQ(SeqAsserter->AssertionSuspended, false); + + ASSERT_EQ(SeqAsserter->getNotificationCount(), 2); + ASSERT_EQ(SeqAsserter->getSuccessfulAssertionCount(), 1); + ASSERT_EQ(SeqAsserter->checkState(), AssertState::pass); +} diff --git a/openmp/tools/omptest/test/unittests/internal-event-test.cpp b/openmp/tools/omptest/test/unittests/internal-event-test.cpp new file mode 100644 index 0000000000000..a53025460c7e0 --- /dev/null +++ b/openmp/tools/omptest/test/unittests/internal-event-test.cpp @@ -0,0 +1,530 @@ +#include "InternalEvent.h" +#include +#include + +#include "gtest/gtest.h" + +using namespace omptest; + +TEST(InternalEvent_toString, AssertionSyncPoint) { + internal::AssertionSyncPoint SP{/*Name=*/"Test Sync Point"}; + + EXPECT_EQ(SP.toString(), "Assertion SyncPoint: 'Test Sync Point'"); +} + +TEST(InternalEvent_toString, ThreadBegin) { + internal::ThreadBegin TB{/*ThreadType=*/ompt_thread_t::ompt_thread_initial}; + + EXPECT_EQ(TB.toString(), "OMPT Callback ThreadBegin: ThreadType=1"); +} + +TEST(InternalEvent_toString, ThreadEnd) { + internal::ThreadEnd TE{}; + + EXPECT_EQ(TE.toString(), "OMPT Callback ThreadEnd"); +} + +TEST(InternalEvent_toString, ParallelBegin) { + internal::ParallelBegin PB{/*NumThreads=*/31}; + + EXPECT_EQ(PB.toString(), "OMPT Callback ParallelBegin: NumThreads=31"); +} + +TEST(InternalEvent_toString, ParallelEnd) { + internal::ParallelEnd PE{/*ParallelData=*/(ompt_data_t *)0x11, + /*EncounteringTaskData=*/(ompt_data_t *)0x22, + /*Flags=*/31, + /*CodeptrRA=*/(const void *)0x33}; + + EXPECT_EQ(PE.toString(), "OMPT Callback ParallelEnd"); +} + +TEST(InternalEvent_toString, Work) { + internal::Work WK{/*WorkType=*/ompt_work_t::ompt_work_loop_dynamic, + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_beginend, + /*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*Count=*/31, + /*CodeptrRA=*/(const void *)0x33}; + + EXPECT_EQ(WK.toString(), + "OMPT Callback Work: work_type=11 endpoint=3 parallel_data=0x11 " + "task_data=0x22 count=31 codeptr=0x33"); +} + +TEST(InternalEvent_toString, Dispatch_iteration) { + ompt_data_t DI{.value = 31}; + internal::Dispatch D{/*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*Kind=*/ompt_dispatch_t::ompt_dispatch_iteration, + /*Instance=*/DI}; + + EXPECT_EQ(D.toString(), "OMPT Callback Dispatch: parallel_data=0x11 " + "task_data=0x22 kind=1 instance=[it=31]"); +} + +TEST(InternalEvent_toString, Dispatch_section) { + ompt_data_t DI{.ptr = (void *)0x33}; + internal::Dispatch D{/*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*Kind=*/ompt_dispatch_t::ompt_dispatch_section, + /*Instance=*/DI}; + + EXPECT_EQ(D.toString(), "OMPT Callback Dispatch: parallel_data=0x11 " + "task_data=0x22 kind=2 instance=[ptr=0x33]"); +} + +TEST(InternalEvent_toString, Dispatch_chunks) { + ompt_dispatch_chunk_t DC{.start = 7, .iterations = 31}; + ompt_data_t DI{.ptr = (void *)&DC}; + + internal::Dispatch DLoop{ + /*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*Kind=*/ompt_dispatch_t::ompt_dispatch_ws_loop_chunk, + /*Instance=*/DI}; + + internal::Dispatch DTask{ + /*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*Kind=*/ompt_dispatch_t::ompt_dispatch_taskloop_chunk, + /*Instance=*/DI}; + + internal::Dispatch DDist{ + /*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*Kind=*/ompt_dispatch_t::ompt_dispatch_distribute_chunk, + /*Instance=*/DI}; + + ompt_data_t DINull{.ptr = nullptr}; + internal::Dispatch DDistNull{ + /*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*Kind=*/ompt_dispatch_t::ompt_dispatch_distribute_chunk, + /*Instance=*/DINull}; + + EXPECT_EQ(DLoop.toString(), + "OMPT Callback Dispatch: parallel_data=0x11 " + "task_data=0x22 kind=3 instance=[chunk=(start=7, iterations=31)]"); + + EXPECT_EQ(DTask.toString(), + "OMPT Callback Dispatch: parallel_data=0x11 " + "task_data=0x22 kind=4 instance=[chunk=(start=7, iterations=31)]"); + + EXPECT_EQ(DDist.toString(), + "OMPT Callback Dispatch: parallel_data=0x11 " + "task_data=0x22 kind=5 instance=[chunk=(start=7, iterations=31)]"); + + EXPECT_EQ(DDistNull.toString(), "OMPT Callback Dispatch: parallel_data=0x11 " + "task_data=0x22 kind=5"); +} + +TEST(InternalEvent_toString, TaskCreate) { + internal::TaskCreate TC{/*EncounteringTaskData=*/(ompt_data_t *)0x11, + /*EncounteringTaskFrame=*/(const ompt_frame_t *)0x22, + /*NewTaskData=*/(ompt_data_t *)0x33, + /*Flags=*/7, + /*HasDependences=*/31, + /*CodeptrRA=*/(const void *)0x44}; + + EXPECT_EQ(TC.toString(), + "OMPT Callback TaskCreate: encountering_task_data=0x11 " + "encountering_task_frame=0x22 new_task_data=0x33 flags=7 " + "has_dependences=31 codeptr=0x44"); +} + +TEST(InternalEvent_toString, ImplicitTask) { + internal::ImplicitTask IT{ + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_begin, + /*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*ActualParallelism=*/7, + /*Index=*/31, + /*Flags=*/127}; + + EXPECT_EQ(IT.toString(), + "OMPT Callback ImplicitTask: endpoint=1 parallel_data=0x11 " + "task_data=0x22 actual_parallelism=7 index=31 flags=127"); +} + +TEST(InternalEvent_toString, SyncRegion) { + internal::SyncRegion SR{ + /*Kind=*/ompt_sync_region_t::ompt_sync_region_taskwait, + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_end, + /*ParallelData=*/(ompt_data_t *)0x11, + /*TaskData=*/(ompt_data_t *)0x22, + /*CodeptrRA=*/(const void *)0x33}; + + EXPECT_EQ(SR.toString(), "OMPT Callback SyncRegion: kind=5 endpoint=2 " + "parallel_data=0x11 task_data=0x22 codeptr=0x33"); +} + +TEST(InternalEvent_toString, Target) { + internal::Target T{/*Kind=*/ompt_target_t::ompt_target_enter_data_nowait, + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_end, + /*DeviceNum=*/7, + /*TaskData=*/(ompt_data_t *)0x11, + /*TargetId=*/(ompt_id_t)31, + /*CodeptrRA=*/(const void *)0x22}; + + EXPECT_EQ(T.toString(), "Callback Target: target_id=31 kind=10 " + "endpoint=2 device_num=7 code=0x22"); +} + +TEST(InternalEvent_toString, TargetEmi) { + ompt_data_t TaskData{.value = 31}; + ompt_data_t TargetTaskData{.value = 127}; + ompt_data_t TargetData{.value = 8191}; + + internal::TargetEmi T{/*Kind=*/ompt_target_t::ompt_target_update, + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_begin, + /*DeviceNum=*/7, + /*TaskData=*/(ompt_data_t *)&TaskData, + /*TargetTaskData=*/(ompt_data_t *)&TargetTaskData, + /*TargetData=*/(ompt_data_t *)&TargetData, + /*CodeptrRA=*/(const void *)0x11}; + + internal::TargetEmi TDataNull{ + /*Kind=*/ompt_target_t::ompt_target_update, + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_begin, + /*DeviceNum=*/7, + /*TaskData=*/(ompt_data_t *)&TaskData, + /*TargetTaskData=*/(ompt_data_t *)nullptr, + /*TargetData=*/(ompt_data_t *)&TargetData, + /*CodeptrRA=*/(const void *)0x11}; + + std::ostringstream StreamT1; + std::ostringstream StreamT2; + std::string CallBackPrefix{ + "Callback Target EMI: kind=4 endpoint=1 device_num=7"}; + StreamT1 << CallBackPrefix << std::showbase << std::hex; + StreamT1 << " task_data=" << &TaskData << " (0x1f)"; + StreamT1 << " target_task_data=" << &TargetTaskData << " (0x7f)"; + StreamT1 << " target_data=" << &TargetData << " (0x1fff)"; + StreamT1 << " code=0x11"; + + StreamT2 << CallBackPrefix << std::showbase << std::hex; + StreamT2 << " task_data=" << &TaskData << " (0x1f)"; + StreamT2 << " target_task_data=(nil) (0x0)"; + StreamT2 << " target_data=" << &TargetData << " (0x1fff)"; + StreamT2 << " code=0x11"; + + EXPECT_EQ(T.toString(), StreamT1.str()); + EXPECT_EQ(TDataNull.toString(), StreamT2.str()); +} + +TEST(InternalEvent_toString, TargetDataOp) { + internal::TargetDataOp TDO{ + /*TargetId=*/7, + /*HostOpId=*/31, + /*OpType=*/ompt_target_data_op_t::ompt_target_data_associate, + /*SrcAddr=*/(void *)0x11, + /*SrcDeviceNum=*/127, + /*DstAddr=*/(void *)0x22, + /*DstDeviceNum=*/8191, + /*Bytes=*/4096, + /*CodeptrRA=*/(const void *)0x33}; + + EXPECT_EQ( + TDO.toString(), + " Callback DataOp: target_id=7 host_op_id=31 optype=5 src=0x11 " + "src_device_num=127 dest=0x22 dest_device_num=8191 bytes=4096 code=0x33"); +} + +TEST(InternalEvent_toString, TargetDataOpEmi) { + ompt_data_t TargetTaskData{.value = 31}; + ompt_data_t TargetData{.value = 127}; + ompt_id_t HostOpId = 8191; + + internal::TargetDataOpEmi TDO{ + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_begin, + /*TargetTaskData=*/(ompt_data_t *)&TargetTaskData, + /*TargetData=*/(ompt_data_t *)&TargetData, + /*HostOpId=*/(ompt_id_t *)&HostOpId, + /*OpType=*/ompt_target_data_op_t::ompt_target_data_disassociate, + /*SrcAddr=*/(void *)0x11, + /*SrcDeviceNum=*/1, + /*DstAddr=*/(void *)0x22, + /*DstDeviceNum=*/2, + /*Bytes=*/4096, + /*CodeptrRA=*/(const void *)0x33}; + + // Set HostOpId=nullptr + internal::TargetDataOpEmi TDO_HostOpIdNull{ + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_begin, + /*TargetTaskData=*/(ompt_data_t *)&TargetTaskData, + /*TargetData=*/(ompt_data_t *)&TargetData, + /*HostOpId=*/(ompt_id_t *)nullptr, + /*OpType=*/ompt_target_data_op_t::ompt_target_data_disassociate, + /*SrcAddr=*/(void *)0x11, + /*SrcDeviceNum=*/1, + /*DstAddr=*/(void *)0x22, + /*DstDeviceNum=*/2, + /*Bytes=*/4096, + /*CodeptrRA=*/(const void *)0x33}; + + std::ostringstream StreamTDO1; + std::ostringstream StreamTDO2; + std::string CallBackPrefix{" Callback DataOp EMI: endpoint=1 optype=6"}; + std::string CallBackSuffix{ + " src=0x11 src_device_num=1 dest=0x22 dest_device_num=2 " + "bytes=4096 code=0x33"}; + StreamTDO1 << CallBackPrefix << std::showbase << std::hex; + StreamTDO1 << " target_task_data=" << &TargetTaskData << " (0x1f)"; + StreamTDO1 << " target_data=" << &TargetData << " (0x7f)"; + StreamTDO1 << " host_op_id=" << &HostOpId << " (0x1fff)"; + StreamTDO1 << CallBackSuffix; + + StreamTDO2 << CallBackPrefix << std::showbase << std::hex; + StreamTDO2 << " target_task_data=" << &TargetTaskData << " (0x1f)"; + StreamTDO2 << " target_data=" << &TargetData << " (0x7f)"; + StreamTDO2 << " host_op_id=(nil) (0x0)"; + StreamTDO2 << CallBackSuffix; + + EXPECT_EQ(TDO.toString(), StreamTDO1.str()); + EXPECT_EQ(TDO_HostOpIdNull.toString(), StreamTDO2.str()); +} + +TEST(InternalEvent_toString, TargetSubmit) { + internal::TargetSubmit TS{/*TargetId=*/7, + /*HostOpId=*/31, + /*RequestedNumTeams=*/127}; + + EXPECT_EQ(TS.toString(), + " Callback Submit: target_id=7 host_op_id=31 req_num_teams=127"); +} + +TEST(InternalEvent_toString, TargetSubmitEmi) { + ompt_data_t TargetData{.value = 127}; + ompt_id_t HostOpId = 8191; + internal::TargetSubmitEmi TS{ + /*Endpoint=*/ompt_scope_endpoint_t::ompt_scope_begin, + /*TargetData=*/(ompt_data_t *)&TargetData, + /*HostOpId=*/(ompt_id_t *)&HostOpId, + /*RequestedNumTeams=*/7}; + + std::ostringstream StreamTS; + std::string CallBackPrefix{ + " Callback Submit EMI: endpoint=1 req_num_teams=7"}; + StreamTS << CallBackPrefix << std::showbase << std::hex; + StreamTS << " target_data=" << &TargetData << " (0x7f)"; + StreamTS << " host_op_id=" << &HostOpId << " (0x1fff)"; + + EXPECT_EQ(TS.toString(), StreamTS.str()); +} + +TEST(InternalEvent_toString, DeviceInitialize) { + const char *Type = "DeviceType"; + const char *DocStr = "DocumentationString"; + + internal::DeviceInitialize DI{/*DeviceNum=*/7, + /*Type=*/Type, + /*Device=*/(ompt_device_t *)0x11, + /*LookupFn=*/(ompt_function_lookup_t)0x22, + /*DocStr=*/DocStr}; + + internal::DeviceInitialize DINull{/*DeviceNum=*/0, + /*Type=*/nullptr, + /*Device=*/nullptr, + /*LookupFn=*/(ompt_function_lookup_t)0x0, + /*DocStr=*/nullptr}; + + std::ostringstream StreamDI; + std::string CallBackPrefix{"Callback Init: device_num=7 type=DeviceType " + "device=0x11 lookup=0x22 doc="}; + StreamDI << CallBackPrefix << std::showbase << std::hex; + StreamDI << (uint64_t)DocStr; + EXPECT_EQ(DI.toString(), StreamDI.str()); + + // TODO This looks inconsistent: (null) vs. (nil) + EXPECT_EQ(DINull.toString(), "Callback Init: device_num=0 type=(null) " + "device=(nil) lookup=(nil) doc=(nil)"); +} + +TEST(InternalEvent_toString, DeviceFinalize) { + internal::DeviceFinalize DF{/*DeviceNum=*/7}; + + EXPECT_EQ(DF.toString(), "Callback Fini: device_num=7"); +} + +TEST(InternalEvent_toString, DeviceLoad) { + const char *Filename = "FilenameToLoad"; + + internal::DeviceLoad DL{/*DeviceNum=*/7, + /*Filename=*/Filename, + /*OffsetInFile=*/31, + /*VmaInFile=*/(void *)0x11, + /*Bytes=*/127, + /*HostAddr=*/(void *)0x22, + /*DeviceAddr=*/(void *)0x33, + /*ModuleId=*/8191}; + + internal::DeviceLoad DLNull{/*DeviceNum=*/0, + /*Filename=*/nullptr, + /*OffsetInFile=*/0, + /*VmaInFile=*/nullptr, + /*Bytes=*/0, + /*HostAddr=*/nullptr, + /*DeviceAddr=*/nullptr, + /*ModuleId=*/0}; + + EXPECT_EQ( + DL.toString(), + "Callback Load: device_num:7 module_id:8191 " + "filename:FilenameToLoad host_adddr:0x22 device_addr:0x33 bytes:127"); + + // TODO This looks inconsistent: (null) vs. (nil) and ':' instead of '=' + EXPECT_EQ(DLNull.toString(), + "Callback Load: device_num:0 module_id:0 filename:(null) " + "host_adddr:(nil) device_addr:(nil) bytes:0"); +} + +TEST(InternalEvent_toString, BufferRequest) { + size_t Bytes = 7; + ompt_buffer_t *Buffer = (void *)0x11; + + internal::BufferRequest BR{/*DeviceNum=*/31, + /*Buffer=*/&Buffer, + /*Bytes=*/&Bytes}; + + internal::BufferRequest BRNull{/*DeviceNum=*/127, + /*Buffer=*/nullptr, + /*Bytes=*/nullptr}; + + EXPECT_EQ(BR.toString(), + "Allocated 7 bytes at 0x11 in buffer request callback"); + EXPECT_EQ(BRNull.toString(), + "Allocated 0 bytes at (nil) in buffer request callback"); +} + +TEST(InternalEvent_toString, BufferComplete) { + ompt_buffer_t *Buffer = (void *)0x11; + + internal::BufferComplete BC{/*DeviceNum=*/7, + /*Buffer=*/Buffer, + /*Bytes=*/127, + /*Begin=*/8191, + /*BufferOwned=*/1}; + + internal::BufferComplete BCNull{/*DeviceNum=*/0, + /*Buffer=*/nullptr, + /*Bytes=*/0, + /*Begin=*/0, + /*BufferOwned=*/0}; + + EXPECT_EQ(BC.toString(), + "Executing buffer complete callback: 7 0x11 127 0x1fff 1"); + EXPECT_EQ(BCNull.toString(), + "Executing buffer complete callback: 0 (nil) 0 (nil) 0"); +} + +TEST(InternalEvent_toString, BufferRecordInvalid) { + ompt_record_ompt_t InvalidRecord{ + /*type=*/ompt_callbacks_t::ompt_callback_parallel_begin, + /*time=*/7, + /*thread_id=*/31, + /*target_id=*/127, + /*record=*/{.parallel_begin = {}}}; + + internal::BufferRecord BRNull{/*RecordPtr=*/nullptr}; + internal::BufferRecord BRInvalid{/*RecordPtr=*/&InvalidRecord}; + + std::ostringstream StreamBRInvalid; + StreamBRInvalid << "rec=" << std::showbase << std::hex << &InvalidRecord; + StreamBRInvalid << " type=3 (unsupported record type)"; + + EXPECT_EQ(BRNull.toString(), "rec=(nil) type=0 (unsupported record type)"); + EXPECT_EQ(BRInvalid.toString(), StreamBRInvalid.str()); +} + +TEST(InternalEvent_toString, BufferRecordTarget) { + ompt_record_target_t SubRecordTarget{ + /*kind=*/ompt_target_t::ompt_target_update, + /*endpoint=*/ompt_scope_endpoint_t::ompt_scope_begin, + /*device_num=*/2, + /*task_id=*/127, + /*target_id=*/31, + /*codeptr_ra=*/(const void *)0x11}; + + ompt_record_ompt_t TargetRecord{ + /*type=*/ompt_callbacks_t::ompt_callback_target, + /*time=*/7, + /*thread_id=*/29, + /*target_id=*/31, + /*record*/ {.target = SubRecordTarget}}; + + internal::BufferRecord BR{/*RecordPtr=*/&TargetRecord}; + + std::ostringstream StreamBR; + StreamBR << "rec=" << std::showbase << std::hex << &TargetRecord; + StreamBR << " type=8 (Target task) time=7 thread_id=29 target_id=31 kind=4"; + StreamBR << " endpoint=1 device=2 task_id=127 codeptr=0x11"; + + EXPECT_EQ(BR.toString(), StreamBR.str()); +} + +TEST(InternalEvent_toString, BufferRecordDataOp) { + ompt_record_target_data_op_t SubRecordTargetDataOp{ + /*host_op_id=*/7, + /*optype=*/ompt_target_data_op_t::ompt_target_data_alloc_async, + /*src_addr=*/(void *)0x11, + /*src_device_num=*/1, + /*dest_addr=*/(void *)0x22, + /*dest_device_num=*/2, + /*bytes=*/127, + /*end_time=*/128, + /*codeptr_ra=*/(const void *)0x33, + }; + + ompt_record_ompt_t DataOpRecord{ + /*type=*/ompt_callbacks_t::ompt_callback_target_data_op_emi, + /*time=*/8, + /*thread_id=*/3, + /*target_id=*/5, + /*record=*/{.target_data_op = SubRecordTargetDataOp}}; + + internal::BufferRecord BR{/*RecordPtr=*/&DataOpRecord}; + + std::ostringstream StreamBR; + StreamBR << "rec=" << std::showbase << std::hex << &DataOpRecord; + StreamBR << " type=34 (Target data op) time=8 thread_id=3 target_id=5"; + StreamBR << " host_op_id=7 optype=17 src_addr=0x11 src_device=1"; + StreamBR << " dest_addr=0x22 dest_device=2 bytes=127 end_time=128"; + StreamBR << " duration=120 ns codeptr=0x33"; + + EXPECT_EQ(BR.toString(), StreamBR.str()); +} + +TEST(InternalEvent_toString, BufferRecordKernel) { + ompt_record_target_kernel_t SubRecordTargetKernel{ + /*host_op_id=*/11, + /*requested_num_teams=*/127, + /*granted_num_teams=*/63, + /*end_time=*/8191, + }; + + ompt_record_ompt_t KernelRecord{ + /*type=*/ompt_callbacks_t::ompt_callback_target_submit_emi, + /*time=*/9, + /*thread_id=*/19, + /*target_id=*/33, + /*record=*/{.target_kernel = SubRecordTargetKernel}}; + + internal::BufferRecord BR{/*RecordPtr=*/&KernelRecord}; + + std::ostringstream StreamBR; + StreamBR << "rec=" << std::showbase << std::hex << &KernelRecord; + StreamBR << " type=35 (Target kernel) time=9 thread_id=19 target_id=33"; + StreamBR << " host_op_id=11 requested_num_teams=127 granted_num_teams=63"; + StreamBR << " end_time=8191 duration=8182 ns"; + + EXPECT_EQ(BR.toString(), StreamBR.str()); +} + +TEST(InternalEvent_toString, BufferRecordDeallocation) { + internal::BufferRecordDeallocation BRD{/*Buffer=*/(ompt_record_ompt_t *)0x11}; + internal::BufferRecordDeallocation BRDNull{/*Buffer=*/nullptr}; + + EXPECT_EQ(BRD.toString(), "Deallocated 0x11"); + EXPECT_EQ(BRDNull.toString(), "Deallocated (nil)"); +} diff --git a/openmp/tools/omptest/test/unittests/internal-util-test.cpp b/openmp/tools/omptest/test/unittests/internal-util-test.cpp new file mode 100644 index 0000000000000..6a9868b85c3a3 --- /dev/null +++ b/openmp/tools/omptest/test/unittests/internal-util-test.cpp @@ -0,0 +1,95 @@ +#include "InternalEvent.h" +#include + +#include "gtest/gtest.h" + +using namespace omptest; + +TEST(InternalUtility, ExpectedDefault_Integer) { + // int: -2147483648 (decimal) = 0x80000000 (hexadecimal) + EXPECT_EQ(expectedDefault(int), 0x80000000); + EXPECT_EQ(expectedDefault(int), (0x1 << 31)); + // int64_t: -9223372036854775808 (decimal) = 0x8000000000000000 (hexadecimal) + EXPECT_EQ(expectedDefault(int64_t), 0x8000000000000000); + EXPECT_EQ(expectedDefault(int64_t), (0x1L << 63)); +} + +TEST(InternalUtility, ExpectedDefault_Zero) { + // Expectedly zero + EXPECT_EQ(expectedDefault(size_t), 0); + EXPECT_EQ(expectedDefault(unsigned int), 0); + EXPECT_EQ(expectedDefault(ompt_id_t), 0); + EXPECT_EQ(expectedDefault(ompt_dispatch_t), 0); + EXPECT_EQ(expectedDefault(ompt_device_time_t), 0); +} + +TEST(InternalUtility, ExpectedDefault_Nullpointer) { + // Expectedly nullptr + EXPECT_EQ(expectedDefault(const char *), nullptr); + EXPECT_EQ(expectedDefault(const void *), nullptr); + EXPECT_EQ(expectedDefault(int *), nullptr); + EXPECT_EQ(expectedDefault(void *), nullptr); + EXPECT_EQ(expectedDefault(ompt_data_t *), nullptr); + EXPECT_EQ(expectedDefault(ompt_device_t *), nullptr); + EXPECT_EQ(expectedDefault(ompt_frame_t *), nullptr); + EXPECT_EQ(expectedDefault(ompt_function_lookup_t), nullptr); + EXPECT_EQ(expectedDefault(ompt_id_t *), nullptr); +} + +TEST(InternalUtility, MakeHexString_PointerValues) { + // IsPointer should only affect zero value + EXPECT_EQ(util::makeHexString(0, /*IsPointer=*/true), "(nil)"); + EXPECT_EQ(util::makeHexString(0, /*IsPointer=*/false), "0x0"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true), "0xff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/false), "0xff"); +} + +TEST(InternalUtility, MakeHexString_MinimumBytes) { + // Return a minimum length, based on the (minimum) requested bytes + EXPECT_EQ(util::makeHexString(15, /*IsPointer=*/true, /*MinBytes=*/0), "0xf"); + EXPECT_EQ(util::makeHexString(15, /*IsPointer=*/true, /*MinBytes=*/1), + "0x0f"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/0), + "0xff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/1), + "0xff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/2), + "0x00ff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/3), + "0x0000ff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/4), + "0x000000ff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/5), + "0x00000000ff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/6), + "0x0000000000ff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/7), + "0x000000000000ff"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/8), + "0x00000000000000ff"); + + // Default to four bytes, if request exceeds eight byte range + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, /*MinBytes=*/9), + "0x000000ff"); + + // Disregard requested minimum byte width, if actual value exceeds it + EXPECT_EQ(util::makeHexString(1024, /*IsPointer=*/true, /*MinBytes=*/1), + "0x400"); +} + +TEST(InternalUtility, MakeHexString_HexBase) { + // Cut off "0x" when requested + EXPECT_EQ(util::makeHexString(0, /*IsPointer=*/true, /*MinBytes=*/0, + /*ShowHexBase=*/false), + "(nil)"); + EXPECT_EQ(util::makeHexString(0, /*IsPointer=*/false, /*MinBytes=*/0, + /*ShowHexBase=*/false), + "0"); + EXPECT_EQ(util::makeHexString(0, /*IsPointer=*/false, /*MinBytes=*/1, + /*ShowHexBase=*/false), + "00"); + EXPECT_EQ(util::makeHexString(255, /*IsPointer=*/true, + /*MinBytes=*/2, + /*ShowHexBase=*/false), + "00ff"); +} diff --git a/openmp/tools/omptest/test/unittests/main-test.cpp b/openmp/tools/omptest/test/unittests/main-test.cpp new file mode 100644 index 0000000000000..2eba663e49c8e --- /dev/null +++ b/openmp/tools/omptest/test/unittests/main-test.cpp @@ -0,0 +1,141 @@ +#include "OmptAssertEvent.h" +#include "OmptAsserter.h" +#include "OmptTester.h" +#include + +#include "gtest/gtest.h" + +using OS = omptest::ObserveState; +using OAE = omptest::OmptAssertEvent; + +TEST(CompareOperatorTests, ThreadBeginIdentity) { + auto TBInitial = + OAE::ThreadBegin("dflt", "", OS::always, ompt_thread_initial); + auto TBWorker = OAE::ThreadBegin("dflt", "", OS::always, ompt_thread_worker); + auto TBOther = OAE::ThreadBegin("dflt", "", OS::always, ompt_thread_other); + auto TBUnknown = + OAE::ThreadBegin("dflt", "", OS::always, ompt_thread_unknown); + + ASSERT_EQ(TBInitial, TBInitial); + ASSERT_EQ(TBWorker, TBWorker); + ASSERT_EQ(TBOther, TBOther); + ASSERT_EQ(TBUnknown, TBUnknown); +} + +TEST(CompareOperatorTests, ThreadEndIdentity) { + auto TE = OAE::ThreadEnd("dflt", "", OS::always); + + ASSERT_EQ(TE, TE); +} + +TEST(CompareOperatorTests, ParallelBeginIdentity) { + auto PBNumT = OAE::ParallelBegin("thrdenable", "", OS::always, 3); + + ASSERT_EQ(PBNumT, PBNumT); +} + +TEST(CompareOperatorTests, ParallelEndIdentity) { + auto PEDflt = OAE::ParallelEnd("dflt", "", OS::always); + // TODO: Add cases with parallel data set, task data set, flags + + ASSERT_EQ(PEDflt, PEDflt); +} + +TEST(CompareOperatorTests, WorkIdentity) { + auto WDLoopBgn = + OAE::Work("loopbgn", "", OS::always, ompt_work_loop, ompt_scope_begin); + auto WDLoopEnd = + OAE::Work("loobend", "", OS::always, ompt_work_loop, ompt_scope_end); + + ASSERT_EQ(WDLoopBgn, WDLoopBgn); + ASSERT_EQ(WDLoopEnd, WDLoopEnd); + + auto WDSectionsBgn = OAE::Work("sectionsbgn", "", OS::always, + ompt_work_sections, ompt_scope_begin); + auto WDSectionsEnd = OAE::Work("sectionsend", "", OS::always, + ompt_work_sections, ompt_scope_end); + + // TODO: singleexecutor, single_other, workshare, distribute, taskloop, scope, + // loop_static, loop_dynamic, loop_guided, loop_other + + ASSERT_EQ(WDSectionsBgn, WDSectionsBgn); + ASSERT_EQ(WDSectionsEnd, WDSectionsEnd); +} + +TEST(CompareOperatorTests, DispatchIdentity) { + auto DIDflt = OAE::Dispatch("dflt", "", OS::always); + + ASSERT_EQ(DIDflt, DIDflt); +} + +TEST(CompareOperatorTests, TaskCreateIdentity) { + auto TCDflt = OAE::TaskCreate("dflt", "", OS::always); + + ASSERT_EQ(TCDflt, TCDflt); +} + +TEST(CompareOperatorTests, TaskScheduleIdentity) { + auto TS = OAE::TaskSchedule("dflt", "", OS::always); + + ASSERT_EQ(TS, TS); +} + +TEST(CompareOperatorTests, ImplicitTaskIdentity) { + auto ITDfltBgn = + OAE::ImplicitTask("dfltbgn", "", OS::always, ompt_scope_begin); + auto ITDfltEnd = OAE::ImplicitTask("dfltend", "", OS::always, ompt_scope_end); + + ASSERT_EQ(ITDfltBgn, ITDfltBgn); + ASSERT_EQ(ITDfltEnd, ITDfltEnd); +} + +TEST(CompareOperatorTests, SyncRegionIdentity) { + auto SRDfltBgn = + OAE::SyncRegion("srdfltbgn", "", OS::always, + ompt_sync_region_barrier_explicit, ompt_scope_begin); + auto SRDfltEnd = + OAE::SyncRegion("srdfltend", "", OS::always, + ompt_sync_region_barrier_explicit, ompt_scope_end); + + ASSERT_EQ(SRDfltBgn, SRDfltBgn); + ASSERT_EQ(SRDfltEnd, SRDfltEnd); +} + +TEST(CompareOperatorTests, TargetIdentity) { + auto TargetDfltBgn = + OAE::Target("dfltbgn", "", OS::always, ompt_target, ompt_scope_begin); + auto TargetDfltEnd = + OAE::Target("dfltend", "", OS::always, ompt_target, ompt_scope_end); + + ASSERT_EQ(TargetDfltBgn, TargetDfltBgn); + ASSERT_EQ(TargetDfltEnd, TargetDfltEnd); + + auto TargetDevBgn = OAE::Target("tgtdevbgn", "", OS::always, ompt_target, + ompt_scope_begin, 1); + auto TargetDevEnd = + OAE::Target("tgtdevend", "", OS::always, ompt_target, ompt_scope_end, 1); + + ASSERT_EQ(TargetDevBgn, TargetDevBgn); + ASSERT_EQ(TargetDevEnd, TargetDevEnd); +} + +TEST(CompareOperatorTests, BufferRecordIdentity) { + // Default, no time limit or anything + auto BRDflt = + OAE::BufferRecord("dflt", "", OS::always, ompt_callback_target_submit); + + // Minimum time set, no max time + auto BRMinSet = OAE::BufferRecord("minset", "", OS::always, + ompt_callback_target_submit, 10); + + // Minimum time and maximum time set + auto BRMinMaxSet = OAE::BufferRecord("minmaxset", "", OS::always, + ompt_callback_target_submit, {10, 100}); + + ASSERT_EQ(BRDflt, BRDflt); + ASSERT_EQ(BRMinSet, BRMinSet); + ASSERT_EQ(BRMinMaxSet, BRMinMaxSet); +} + +// Add main definition +OMPTEST_TESTSUITE_MAIN()