485 lines · cpp
1//===- OmptAsserter.cpp - Asserter-related implementations ------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// Implements all asserter-related class methods, like: notifications, handling11/// of groups or determination of the testcase state.12///13//===----------------------------------------------------------------------===//14 15#include "OmptAsserter.h"16#include "Logging.h"17 18#include <algorithm>19 20using namespace omptest;21using namespace internal;22 23// Initialize static members24std::mutex OmptAsserter::StaticMemberAccessMutex;25std::weak_ptr<OmptEventGroupInterface>26 OmptAsserter::EventGroupInterfaceInstance;27std::weak_ptr<logging::Logger> OmptAsserter::LoggingInstance;28 29OmptAsserter::OmptAsserter() {30 // Protect static members access31 std::lock_guard<std::mutex> Lock(StaticMemberAccessMutex);32 33 // Upgrade OmptEventGroupInterface weak_ptr to shared_ptr34 {35 EventGroups = EventGroupInterfaceInstance.lock();36 if (!EventGroups) {37 // Coordinator doesn't exist or was previously destroyed, create a new38 // one.39 EventGroups = std::make_shared<OmptEventGroupInterface>();40 // Store a weak reference to it41 EventGroupInterfaceInstance = EventGroups;42 }43 // EventGroups is now a valid shared_ptr, either to a new or existing44 // instance.45 }46 47 // Upgrade logging::Logger weak_ptr to shared_ptr48 {49 Log = LoggingInstance.lock();50 if (!Log) {51 // Coordinator doesn't exist or was previously destroyed, create a new52 // one.53 Log = std::make_shared<logging::Logger>();54 // Store a weak reference to it55 LoggingInstance = Log;56 }57 // Log is now a valid shared_ptr, either to a new or existing instance.58 }59}60 61void OmptListener::setActive(bool Enabled) { Active = Enabled; }62 63bool OmptListener::isActive() { return Active; }64 65bool OmptListener::isSuppressedEventType(EventTy EvTy) {66 return SuppressedEvents.find(EvTy) != SuppressedEvents.end();67}68 69void OmptListener::permitEvent(EventTy EvTy) { SuppressedEvents.erase(EvTy); }70 71void OmptListener::suppressEvent(EventTy EvTy) {72 SuppressedEvents.insert(EvTy);73}74 75void OmptAsserter::insert(OmptAssertEvent &&AE) {76 assert(false && "Base class 'insert' has undefined semantics.");77}78 79void OmptAsserter::notify(OmptAssertEvent &&AE) {80 // Ignore notifications while inactive81 if (!isActive() || isSuppressedEventType(AE.getEventType()))82 return;83 84 this->notifyImpl(std::move(AE));85}86 87AssertState OmptAsserter::checkState() { return State; }88 89bool OmptAsserter::verifyEventGroups(const OmptAssertEvent &ExpectedEvent,90 const OmptAssertEvent &ObservedEvent) {91 assert(ExpectedEvent.getEventType() == ObservedEvent.getEventType() &&92 "Type mismatch: Expected != Observed event type");93 assert(EventGroups && "Missing EventGroups interface");94 95 // Ignore all events within "default" group96 auto GroupName = ExpectedEvent.getEventGroup();97 98 if (GroupName == "default")99 return true;100 101 // Get a pointer to the observed internal event102 auto Event = ObservedEvent.getEvent();103 104 switch (Event->Type) {105 case EventTy::Target:106 if (auto E = static_cast<const internal::Target *>(Event)) {107 if (E->Endpoint == ompt_scope_begin) {108 // Add new group since we entered a Target Region109 EventGroups->addActiveEventGroup(GroupName,110 AssertEventGroup{E->TargetId});111 } else if (E->Endpoint == ompt_scope_end) {112 // Deprecate group since we return from a Target Region113 EventGroups->deprecateActiveEventGroup(GroupName);114 }115 return true;116 }117 return false;118 case EventTy::TargetEmi:119 if (auto E = static_cast<const internal::TargetEmi *>(Event)) {120 if (E->Endpoint == ompt_scope_begin) {121 // Add new group since we entered a Target Region122 EventGroups->addActiveEventGroup(123 GroupName, AssertEventGroup{E->TargetData->value});124 } else if (E->Endpoint == ompt_scope_end) {125 // Deprecate group since we return from a Target Region126 EventGroups->deprecateActiveEventGroup(GroupName);127 }128 return true;129 }130 return false;131 case EventTy::TargetDataOp:132 if (auto E = static_cast<const internal::TargetDataOp *>(Event))133 return EventGroups->checkActiveEventGroups(GroupName,134 AssertEventGroup{E->TargetId});135 136 return false;137 case EventTy::TargetDataOpEmi:138 if (auto E = static_cast<const internal::TargetDataOpEmi *>(Event))139 return EventGroups->checkActiveEventGroups(140 GroupName, AssertEventGroup{E->TargetData->value});141 142 return false;143 case EventTy::TargetSubmit:144 if (auto E = static_cast<const internal::TargetSubmit *>(Event))145 return EventGroups->checkActiveEventGroups(GroupName,146 AssertEventGroup{E->TargetId});147 148 return false;149 case EventTy::TargetSubmitEmi:150 if (auto E = static_cast<const internal::TargetSubmitEmi *>(Event))151 return EventGroups->checkActiveEventGroups(152 GroupName, AssertEventGroup{E->TargetData->value});153 154 return false;155 case EventTy::BufferRecord:156 // BufferRecords are delivered asynchronously: also check deprecated groups.157 if (auto E = static_cast<const internal::BufferRecord *>(Event))158 return (EventGroups->checkActiveEventGroups(159 GroupName, AssertEventGroup{E->Record.target_id}) ||160 EventGroups->checkDeprecatedEventGroups(161 GroupName, AssertEventGroup{E->Record.target_id}));162 return false;163 // Some event types do not need any handling164 case EventTy::ThreadBegin:165 case EventTy::ThreadEnd:166 case EventTy::ParallelBegin:167 case EventTy::ParallelEnd:168 case EventTy::Work:169 case EventTy::Dispatch:170 case EventTy::TaskCreate:171 case EventTy::Dependences:172 case EventTy::TaskDependence:173 case EventTy::TaskSchedule:174 case EventTy::ImplicitTask:175 case EventTy::Masked:176 case EventTy::SyncRegion:177 case EventTy::MutexAcquire:178 case EventTy::Mutex:179 case EventTy::NestLock:180 case EventTy::Flush:181 case EventTy::Cancel:182 case EventTy::DeviceInitialize:183 case EventTy::DeviceFinalize:184 case EventTy::DeviceLoad:185 case EventTy::DeviceUnload:186 case EventTy::BufferRequest:187 case EventTy::BufferComplete:188 case EventTy::BufferRecordDeallocation:189 return true;190 // Some event types must not be encountered191 case EventTy::None:192 case EventTy::AssertionSyncPoint:193 case EventTy::AssertionSuspend:194 default:195 Log->log("Observed invalid event type: " + Event->toString(),196 logging::Level::Critical);197 __builtin_unreachable();198 }199 200 return true;201}202 203void OmptAsserter::setOperationMode(AssertMode Mode) { OperationMode = Mode; }204 205void OmptSequencedAsserter::insert(OmptAssertEvent &&AE) {206 std::lock_guard<std::mutex> Lock(AssertMutex);207 Events.emplace_back(std::move(AE));208}209 210void OmptSequencedAsserter::notifyImpl(OmptAssertEvent &&AE) {211 std::lock_guard<std::mutex> Lock(AssertMutex);212 // Ignore notifications while inactive, or for suppressed events213 if (Events.empty() || !isActive() || isSuppressedEventType(AE.getEventType()))214 return;215 216 ++NumNotifications;217 218 // Note: Order of these checks has semantic meaning.219 // (1) Synchronization points should fail if there are remaining events,220 // otherwise pass. (2) Regular notification while no further events are221 // expected: fail. (3) Assertion suspension relies on a next expected event222 // being available. (4) All other cases are considered 'regular' and match the223 // next expected against the observed event. (5+6) Depending on the state /224 // mode we signal failure if no other check has done already, or signaled pass225 // by early-exit.226 if (consumeSyncPoint(AE) || // Handle observed SyncPoint event227 checkExcessNotify(AE) || // Check for remaining expected228 consumeSuspend() || // Handle requested suspend229 consumeRegularEvent(AE) || // Handle regular event230 AssertionSuspended || // Ignore fail, if suspended231 OperationMode == AssertMode::Relaxed) // Ignore fail, if Relaxed op-mode232 return;233 234 Log->logEventMismatch("[OmptSequencedAsserter] The events are not equal",235 Events[NextEvent], AE);236 State = AssertState::Fail;237}238 239bool OmptSequencedAsserter::consumeSyncPoint(240 const omptest::OmptAssertEvent &AE) {241 if (AE.getEventType() == EventTy::AssertionSyncPoint) {242 auto NumRemainingEvents = getRemainingEventCount();243 // Upon encountering a SyncPoint, all events should have been processed244 if (NumRemainingEvents == 0)245 return true;246 247 Log->logEventMismatch(248 "[OmptSequencedAsserter] Encountered SyncPoint while still awaiting " +249 std::to_string(NumRemainingEvents) + " events. Asserted " +250 std::to_string(NumSuccessfulAsserts) + "/" +251 std::to_string(Events.size()) + " events successfully.",252 AE);253 State = AssertState::Fail;254 return true;255 }256 257 // Nothing to process: continue.258 return false;259}260 261bool OmptSequencedAsserter::checkExcessNotify(262 const omptest::OmptAssertEvent &AE) {263 if (NextEvent >= Events.size()) {264 // If we are not expecting any more events and passively asserting: return265 if (AssertionSuspended)266 return true;267 268 Log->logEventMismatch(269 "[OmptSequencedAsserter] Too many events to check (" +270 std::to_string(NumNotifications) + "). Asserted " +271 std::to_string(NumSuccessfulAsserts) + "/" +272 std::to_string(Events.size()) + " events successfully.",273 AE);274 State = AssertState::Fail;275 return true;276 }277 278 // Remaining expected events present: continue.279 return false;280}281 282bool OmptSequencedAsserter::consumeSuspend() {283 // On AssertionSuspend -- enter 'passive' assertion.284 // Since we may encounter multiple, successive AssertionSuspend events, loop285 // until we hit the next non-AssertionSuspend event.286 while (Events[NextEvent].getEventType() == EventTy::AssertionSuspend) {287 AssertionSuspended = true;288 // We just hit the very last event: indicate early exit.289 if (++NextEvent >= Events.size())290 return true;291 }292 293 // Continue with remaining notification logic.294 return false;295}296 297bool OmptSequencedAsserter::consumeRegularEvent(298 const omptest::OmptAssertEvent &AE) {299 // If we are actively asserting, increment the event counter.300 // Otherwise: If passively asserting, we will keep waiting for a match.301 auto &E = Events[NextEvent];302 if (E == AE && verifyEventGroups(E, AE)) {303 if (E.getEventExpectedState() == ObserveState::Always) {304 ++NumSuccessfulAsserts;305 } else if (E.getEventExpectedState() == ObserveState::Never) {306 Log->logEventMismatch(307 "[OmptSequencedAsserter] Encountered forbidden event", E, AE);308 State = AssertState::Fail;309 }310 311 // Return to active assertion312 if (AssertionSuspended)313 AssertionSuspended = false;314 315 // Match found, increment index and indicate early exit (success).316 ++NextEvent;317 return true;318 }319 320 // Continue with remaining notification logic.321 return false;322}323 324size_t OmptSequencedAsserter::getRemainingEventCount() {325 return std::count_if(Events.begin(), Events.end(),326 [](const omptest::OmptAssertEvent &E) {327 return E.getEventExpectedState() ==328 ObserveState::Always;329 }) -330 NumSuccessfulAsserts;331}332 333AssertState OmptSequencedAsserter::checkState() {334 // This is called after the testcase executed.335 // Once reached the number of successful notifications should be equal to the336 // number of expected events. However, there may still be excluded as well as337 // special asserter events remaining in the sequence.338 for (size_t i = NextEvent; i < Events.size(); ++i) {339 auto &E = Events[i];340 if (E.getEventExpectedState() == ObserveState::Always) {341 State = AssertState::Fail;342 Log->logEventMismatch("[OmptSequencedAsserter] Expected event was not "343 "encountered (Remaining events: " +344 std::to_string(getRemainingEventCount()) + ")",345 E);346 break;347 }348 }349 350 return State;351}352 353void OmptEventAsserter::insert(OmptAssertEvent &&AE) {354 std::lock_guard<std::mutex> Lock(AssertMutex);355 Events.emplace_back(std::move(AE));356}357 358void OmptEventAsserter::notifyImpl(OmptAssertEvent &&AE) {359 std::lock_guard<std::mutex> Lock(AssertMutex);360 if (Events.empty() || !isActive() || isSuppressedEventType(AE.getEventType()))361 return;362 363 if (NumEvents == 0)364 NumEvents = Events.size();365 366 ++NumNotifications;367 368 if (AE.getEventType() == EventTy::AssertionSyncPoint) {369 auto NumRemainingEvents = getRemainingEventCount();370 // Upon encountering a SyncPoint, all events should have been processed371 if (NumRemainingEvents == 0)372 return;373 374 Log->logEventMismatch(375 "[OmptEventAsserter] Encountered SyncPoint while still awaiting " +376 std::to_string(NumRemainingEvents) + " events. Asserted " +377 std::to_string(NumSuccessfulAsserts) + " events successfully.",378 AE);379 State = AssertState::Fail;380 return;381 }382 383 for (size_t i = 0; i < Events.size(); ++i) {384 auto &E = Events[i];385 if (E == AE && verifyEventGroups(E, AE)) {386 if (E.getEventExpectedState() == ObserveState::Always) {387 Events.erase(Events.begin() + i);388 ++NumSuccessfulAsserts;389 } else if (E.getEventExpectedState() == ObserveState::Never) {390 Log->logEventMismatch("[OmptEventAsserter] Encountered forbidden event",391 E, AE);392 State = AssertState::Fail;393 }394 return;395 }396 }397 398 if (OperationMode == AssertMode::Strict) {399 Log->logEventMismatch("[OmptEventAsserter] Too many events to check (" +400 std::to_string(NumNotifications) +401 "). Asserted " +402 std::to_string(NumSuccessfulAsserts) +403 " events successfully. (Remaining events: " +404 std::to_string(getRemainingEventCount()) + ")",405 AE);406 State = AssertState::Fail;407 return;408 }409}410 411size_t OmptEventAsserter::getRemainingEventCount() {412 return std::count_if(413 Events.begin(), Events.end(), [](const omptest::OmptAssertEvent &E) {414 return E.getEventExpectedState() == ObserveState::Always;415 });416}417 418AssertState OmptEventAsserter::checkState() {419 // This is called after the testcase executed.420 // Once reached no more expected events should be in the queue421 for (const auto &E : Events) {422 // Check if any of the remaining events were expected to be observed423 if (E.getEventExpectedState() == ObserveState::Always) {424 State = AssertState::Fail;425 Log->logEventMismatch("[OmptEventAsserter] Expected event was not "426 "encountered (Remaining events: " +427 std::to_string(getRemainingEventCount()) + ")",428 E);429 break;430 }431 }432 433 return State;434}435 436void OmptEventReporter::notify(OmptAssertEvent &&AE) {437 if (!isActive() || isSuppressedEventType(AE.getEventType()))438 return;439 440 // Prepare notification, containing the newline to avoid stream interleaving.441 auto Notification{AE.toString()};442 Notification.push_back('\n');443 OutStream << Notification;444}445 446bool OmptEventGroupInterface::addActiveEventGroup(447 const std::string &GroupName, omptest::AssertEventGroup Group) {448 std::lock_guard<std::mutex> Lock(GroupMutex);449 auto EventGroup = ActiveEventGroups.find(GroupName);450 if (EventGroup != ActiveEventGroups.end() &&451 EventGroup->second.TargetRegion == Group.TargetRegion)452 return false;453 ActiveEventGroups.emplace(GroupName, Group);454 return true;455}456 457bool OmptEventGroupInterface::deprecateActiveEventGroup(458 const std::string &GroupName) {459 std::lock_guard<std::mutex> Lock(GroupMutex);460 auto EventGroup = ActiveEventGroups.find(GroupName);461 auto DeprecatedEventGroup = DeprecatedEventGroups.find(GroupName);462 if (EventGroup == ActiveEventGroups.end() &&463 DeprecatedEventGroup != DeprecatedEventGroups.end())464 return false;465 DeprecatedEventGroups.emplace(GroupName, EventGroup->second);466 ActiveEventGroups.erase(GroupName);467 return true;468}469 470bool OmptEventGroupInterface::checkActiveEventGroups(471 const std::string &GroupName, omptest::AssertEventGroup Group) {472 std::lock_guard<std::mutex> Lock(GroupMutex);473 auto EventGroup = ActiveEventGroups.find(GroupName);474 return (EventGroup != ActiveEventGroups.end() &&475 EventGroup->second.TargetRegion == Group.TargetRegion);476}477 478bool OmptEventGroupInterface::checkDeprecatedEventGroups(479 const std::string &GroupName, omptest::AssertEventGroup Group) {480 std::lock_guard<std::mutex> Lock(GroupMutex);481 auto EventGroup = DeprecatedEventGroups.find(GroupName);482 return (EventGroup != DeprecatedEventGroups.end() &&483 EventGroup->second.TargetRegion == Group.TargetRegion);484}485