LLVM OpenMP 22.0.0git
OmptAsserter.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// Implements all asserter-related class methods, like: notifications, handling
11/// 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 members
24std::mutex OmptAsserter::StaticMemberAccessMutex;
25std::weak_ptr<OmptEventGroupInterface>
26 OmptAsserter::EventGroupInterfaceInstance;
27std::weak_ptr<logging::Logger> OmptAsserter::LoggingInstance;
28
30 // Protect static members access
31 std::lock_guard<std::mutex> Lock(StaticMemberAccessMutex);
32
33 // Upgrade OmptEventGroupInterface weak_ptr to shared_ptr
34 {
35 EventGroups = EventGroupInterfaceInstance.lock();
36 if (!EventGroups) {
37 // Coordinator doesn't exist or was previously destroyed, create a new
38 // one.
39 EventGroups = std::make_shared<OmptEventGroupInterface>();
40 // Store a weak reference to it
41 EventGroupInterfaceInstance = EventGroups;
42 }
43 // EventGroups is now a valid shared_ptr, either to a new or existing
44 // instance.
45 }
46
47 // Upgrade logging::Logger weak_ptr to shared_ptr
48 {
49 Log = LoggingInstance.lock();
50 if (!Log) {
51 // Coordinator doesn't exist or was previously destroyed, create a new
52 // one.
53 Log = std::make_shared<logging::Logger>();
54 // Store a weak reference to it
55 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
66 return SuppressedEvents.find(EvTy) != SuppressedEvents.end();
67}
68
69void OmptListener::permitEvent(EventTy EvTy) { SuppressedEvents.erase(EvTy); }
70
72 SuppressedEvents.insert(EvTy);
73}
74
76 assert(false && "Base class 'insert' has undefined semantics.");
77}
78
80 // Ignore notifications while inactive
81 if (!isActive() || isSuppressedEventType(AE.getEventType()))
82 return;
83
84 this->notifyImpl(std::move(AE));
85}
86
88
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" group
96 auto GroupName = ExpectedEvent.getEventGroup();
97
98 if (GroupName == "default")
99 return true;
100
101 // Get a pointer to the observed internal event
102 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 Region
109 EventGroups->addActiveEventGroup(GroupName,
110 AssertEventGroup{E->TargetId});
111 } else if (E->Endpoint == ompt_scope_end) {
112 // Deprecate group since we return from a Target Region
113 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 Region
122 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 Region
126 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 handling
164 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 encountered
191 case EventTy::None:
192 case EventTy::AssertionSyncPoint:
193 case EventTy::AssertionSuspend:
194 default:
195 Log->log("Observed invalid event type: " + Event->toString(),
197 __builtin_unreachable();
198 }
199
200 return true;
201}
202
204
206 std::lock_guard<std::mutex> Lock(AssertMutex);
207 Events.emplace_back(std::move(AE));
208}
209
211 std::lock_guard<std::mutex> Lock(AssertMutex);
212 // Ignore notifications while inactive, or for suppressed events
213 if (Events.empty() || !isActive() || isSuppressedEventType(AE.getEventType()))
214 return;
215
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 are
221 // expected: fail. (3) Assertion suspension relies on a next expected event
222 // being available. (4) All other cases are considered 'regular' and match the
223 // 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 pass
225 // by early-exit.
226 if (consumeSyncPoint(AE) || // Handle observed SyncPoint event
227 checkExcessNotify(AE) || // Check for remaining expected
228 consumeSuspend() || // Handle requested suspend
229 consumeRegularEvent(AE) || // Handle regular event
230 AssertionSuspended || // Ignore fail, if suspended
231 OperationMode == AssertMode::Relaxed) // Ignore fail, if Relaxed op-mode
232 return;
233
234 Log->logEventMismatch("[OmptSequencedAsserter] The events are not equal",
235 Events[NextEvent], AE);
237}
238
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 processed
244 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);
254 return true;
255 }
256
257 // Nothing to process: continue.
258 return false;
259}
260
262 const omptest::OmptAssertEvent &AE) {
263 if (NextEvent >= Events.size()) {
264 // If we are not expecting any more events and passively asserting: return
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);
275 return true;
276 }
277
278 // Remaining expected events present: continue.
279 return false;
280}
281
283 // On AssertionSuspend -- enter 'passive' assertion.
284 // Since we may encounter multiple, successive AssertionSuspend events, loop
285 // 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
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) {
305 } else if (E.getEventExpectedState() == ObserveState::Never) {
306 Log->logEventMismatch(
307 "[OmptSequencedAsserter] Encountered forbidden event", E, AE);
309 }
310
311 // Return to active assertion
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
325 return std::count_if(Events.begin(), Events.end(),
326 [](const omptest::OmptAssertEvent &E) {
327 return E.getEventExpectedState() ==
328 ObserveState::Always;
329 }) -
331}
332
334 // This is called after the testcase executed.
335 // Once reached the number of successful notifications should be equal to the
336 // number of expected events. However, there may still be excluded as well as
337 // 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) {
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
354 std::lock_guard<std::mutex> Lock(AssertMutex);
355 Events.emplace_back(std::move(AE));
356}
357
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
367
368 if (AE.getEventType() == EventTy::AssertionSyncPoint) {
369 auto NumRemainingEvents = getRemainingEventCount();
370 // Upon encountering a SyncPoint, all events should have been processed
371 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);
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);
389 } else if (E.getEventExpectedState() == ObserveState::Never) {
390 Log->logEventMismatch("[OmptEventAsserter] Encountered forbidden event",
391 E, AE);
393 }
394 return;
395 }
396 }
397
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);
407 return;
408 }
409}
410
412 return std::count_if(
413 Events.begin(), Events.end(), [](const omptest::OmptAssertEvent &E) {
414 return E.getEventExpectedState() == ObserveState::Always;
415 });
416}
417
419 // This is called after the testcase executed.
420 // Once reached no more expected events should be in the queue
421 for (const auto &E : Events) {
422 // Check if any of the remaining events were expected to be observed
423 if (E.getEventExpectedState() == ObserveState::Always) {
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
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
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
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
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
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}
Provides ompTest-tailored logging, with log-levels and formatting/coloring.
Contains all asserter-related class declarations and important enums.
virtual omptest::AssertState checkState()
Determine and return the asserter's state.
std::shared_ptr< OmptEventGroupInterface > EventGroups
Pointer to the OmptEventGroupInterface.
AssertMode OperationMode
Operation mode during assertion / notification.
std::mutex AssertMutex
Mutex to avoid data races w.r.t. event notifications and/or insertions.
int NumNotifications
The total number of effective notifications.
void notify(omptest::OmptAssertEvent &&AE) override
Called from the CallbackHandler with a corresponding AssertEvent to which callback was handled.
virtual void insert(omptest::OmptAssertEvent &&AE)
Add an event to the asserter's internal data structure.
virtual void notifyImpl(omptest::OmptAssertEvent &&AE)=0
Implemented in subclasses to implement what should actually be done with the notification.
int NumSuccessfulAsserts
The number of successful assertion checks.
void setOperationMode(AssertMode Mode)
Set the asserter's mode of operation w.r.t. assertion.
bool verifyEventGroups(const omptest::OmptAssertEvent &ExpectedEvent, const omptest::OmptAssertEvent &ObservedEvent)
Check the observed events' group association.
std::shared_ptr< logging::Logger > Log
Pointer to the logging instance.
omptest::AssertState State
The asserter's current state.
bool checkActiveEventGroups(const std::string &GroupName, omptest::AssertEventGroup Group)
Check if given group is currently part of the active event groups.
bool checkDeprecatedEventGroups(const std::string &GroupName, omptest::AssertEventGroup Group)
Check if given group is currently part of the deprecated event groups.
bool addActiveEventGroup(const std::string &GroupName, omptest::AssertEventGroup Group)
Add given group to the set of active event groups.
bool deprecateActiveEventGroup(const std::string &GroupName)
Move given group from the set of active event groups to the set of previously active event groups.
void notify(omptest::OmptAssertEvent &&AE) override
Called from the CallbackHandler with a corresponding AssertEvent to which callback was handled.
void suppressEvent(omptest::internal::EventTy EvTy)
Add the given event type to the set of suppressed events.
bool isSuppressedEventType(omptest::internal::EventTy EvTy)
Check if the given event type is from the set of suppressed event types.
void permitEvent(omptest::internal::EventTy EvTy)
Remove the given event type to the set of suppressed events.
void setActive(bool Enabled)
Control whether this asserter should be considered 'active'.
bool isActive()
Check if this asserter is considered 'active'.
size_t getRemainingEventCount() override
Get the number of currently remaining events, with: ObserveState::Always.
bool consumeSuspend()
Notification helper function, implementing Suspend logic.
bool consumeRegularEvent(const omptest::OmptAssertEvent &AE)
Notification helper function, implementing regular event notification logic.
void insert(omptest::OmptAssertEvent &&AE) override
Add the event to the in-sequence set of events that the asserter should check for.
bool consumeSyncPoint(const omptest::OmptAssertEvent &AE)
Notification helper function, implementing SyncPoint logic.
virtual void notifyImpl(omptest::OmptAssertEvent &&AE) override
Implements the asserter's actual logic.
omptest::AssertState checkState() override
Determine and return the asserter's state.
size_t NextEvent
Index of the next, expected event.
bool checkExcessNotify(const omptest::OmptAssertEvent &AE)
Notification helper function, implementing excess event notification logic.
std::vector< omptest::OmptAssertEvent > Events
#define i
Definition kmp_stub.cpp:87
EventTy
Enum values are used for comparison of observed and asserted events List is based on OpenMP 5....
POD type, which holds the target region id, corresponding to an event group.
Assertion event struct, provides statically callable CTORs.
internal::EventTy getEventType() const
Return the actual event type enum value.
virtual void notifyImpl(omptest::OmptAssertEvent &&AE) override
Implements the asserter's logic.
size_t getRemainingEventCount() override
Get the number of currently remaining events, with: ObserveState::Always.
void insert(omptest::OmptAssertEvent &&AE) override
Add the event to the set of events that the asserter should check for.
omptest::AssertState checkState() override
Determine and return the asserter's state.
std::vector< omptest::OmptAssertEvent > Events
For now use vector (but do set semantics)