LLVM OpenMP
ompt-tsan.cpp
Go to the documentation of this file.
1/*
2 * ompt-tsan.cpp -- Archer runtime library, TSan annotations for Archer
3 */
4
5//===----------------------------------------------------------------------===//
6//
7// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8// See https://llvm.org/LICENSE.txt for details.
9// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef __STDC_FORMAT_MACROS
14#define __STDC_FORMAT_MACROS
15#endif
16
17#include <algorithm>
18#include <atomic>
19#include <cassert>
20#include <cstdlib>
21#include <cstring>
22#include <dlfcn.h>
23#include <inttypes.h>
24#include <iostream>
25#include <list>
26#include <mutex>
27#include <sstream>
28#include <string>
29#include <sys/resource.h>
30#include <unistd.h>
31#include <unordered_map>
32#include <vector>
33
34#include "omp-tools.h"
35
36// Define attribute that indicates that the fall through from the previous
37// case label is intentional and should not be diagnosed by a compiler
38// Code from libcxx/include/__config
39// Use a function like macro to imply that it must be followed by a semicolon
40#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough)
41#define KMP_FALLTHROUGH() [[fallthrough]]
42// icc cannot properly tell this attribute is absent so force off
43#elif defined(__INTEL_COMPILER)
44#define KMP_FALLTHROUGH() ((void)0)
45#elif __has_cpp_attribute(clang::fallthrough)
46#define KMP_FALLTHROUGH() [[clang::fallthrough]]
47#elif __has_attribute(fallthrough) || __GNUC__ >= 7
48#define KMP_FALLTHROUGH() __attribute__((__fallthrough__))
49#else
50#define KMP_FALLTHROUGH() ((void)0)
51#endif
52
54
55namespace {
56class ArcherFlags {
57public:
58#if (LLVM_VERSION) >= 40
59 int flush_shadow{0};
60#endif
61 int print_max_rss{0};
62 int verbose{0};
63 int enabled{1};
64 int report_data_leak{0};
65 int ignore_serial{0};
66 std::atomic<int> all_memory{0};
67
68 ArcherFlags(const char *env) {
69 if (env) {
70 std::vector<std::string> tokens;
71 std::string token;
72 std::string str(env);
73 std::istringstream iss(str);
74 int tmp_int;
75 while (std::getline(iss, token, ' '))
76 tokens.push_back(token);
77
78 for (std::vector<std::string>::iterator it = tokens.begin();
79 it != tokens.end(); ++it) {
80#if (LLVM_VERSION) >= 40
81 if (sscanf(it->c_str(), "flush_shadow=%d", &flush_shadow))
82 continue;
83#endif
84 if (sscanf(it->c_str(), "print_max_rss=%d", &print_max_rss))
85 continue;
86 if (sscanf(it->c_str(), "verbose=%d", &verbose))
87 continue;
88 if (sscanf(it->c_str(), "report_data_leak=%d", &report_data_leak))
89 continue;
90 if (sscanf(it->c_str(), "enable=%d", &enabled))
91 continue;
92 if (sscanf(it->c_str(), "ignore_serial=%d", &ignore_serial))
93 continue;
94 if (sscanf(it->c_str(), "all_memory=%d", &tmp_int)) {
95 all_memory = tmp_int;
96 continue;
97 }
98 std::cerr << "Illegal values for ARCHER_OPTIONS variable: " << token
99 << std::endl;
100 }
101 }
102 }
103};
104
105class TsanFlags {
106public:
107 int ignore_noninstrumented_modules;
108
109 TsanFlags(const char *env) : ignore_noninstrumented_modules(0) {
110 if (env) {
111 std::vector<std::string> tokens;
112 std::string str(env);
113 auto end = str.end();
114 auto it = str.begin();
115 auto is_sep = [](char c) {
116 return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
117 c == '\r';
118 };
119 while (it != end) {
120 auto next_it = std::find_if(it, end, is_sep);
121 tokens.emplace_back(it, next_it);
122 it = next_it;
123 if (it != end) {
124 ++it;
125 }
126 }
127
128 for (const auto &token : tokens) {
129 // we are interested in ignore_noninstrumented_modules to print a
130 // warning
131 if (sscanf(token.c_str(), "ignore_noninstrumented_modules=%d",
132 &ignore_noninstrumented_modules))
133 continue;
134 }
135 }
136 }
137};
138} // namespace
139
140#if (LLVM_VERSION) >= 40
141extern "C" {
142int __attribute__((weak)) __archer_get_omp_status();
143void __attribute__((weak)) __tsan_flush_memory() {}
144}
145#endif
146static ArcherFlags *archer_flags;
147
148#ifndef TsanHappensBefore
149
150template <typename... Args> static void __ompt_tsan_func(Args...) {}
151
152#define DECLARE_TSAN_FUNCTION(name, ...) \
153 static void (*name)(__VA_ARGS__) = __ompt_tsan_func<__VA_ARGS__>;
154
155// Thread Sanitizer is a tool that finds races in code.
156// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations .
157// tsan detects these exact functions by name.
158extern "C" {
159DECLARE_TSAN_FUNCTION(AnnotateHappensAfter, const char *, int,
160 const volatile void *)
161DECLARE_TSAN_FUNCTION(AnnotateHappensBefore, const char *, int,
162 const volatile void *)
163DECLARE_TSAN_FUNCTION(AnnotateIgnoreWritesBegin, const char *, int)
164DECLARE_TSAN_FUNCTION(AnnotateIgnoreWritesEnd, const char *, int)
165DECLARE_TSAN_FUNCTION(AnnotateNewMemory, const char *, int,
166 const volatile void *, size_t)
167DECLARE_TSAN_FUNCTION(__tsan_func_entry, const void *)
168DECLARE_TSAN_FUNCTION(__tsan_func_exit)
169}
170
171// This marker is used to define a happens-before arc. The race detector will
172// infer an arc from the begin to the end when they share the same pointer
173// argument.
174#define TsanHappensBefore(cv) AnnotateHappensBefore(__FILE__, __LINE__, cv)
175
176// This marker defines the destination of a happens-before arc.
177#define TsanHappensAfter(cv) AnnotateHappensAfter(__FILE__, __LINE__, cv)
178
179// Ignore any races on writes between here and the next TsanIgnoreWritesEnd.
180#define TsanIgnoreWritesBegin() AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
181
182// Resume checking for racy writes.
183#define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
184
185// We don't really delete the clock for now
186#define TsanDeleteClock(cv)
187
188// newMemory
189#define TsanNewMemory(addr, size) \
190 AnnotateNewMemory(__FILE__, __LINE__, addr, size)
191#define TsanFreeMemory(addr, size) \
192 AnnotateNewMemory(__FILE__, __LINE__, addr, size)
193#endif
194
195// Function entry/exit
196#define TsanFuncEntry(pc) __tsan_func_entry(pc)
197#define TsanFuncExit() __tsan_func_exit()
198
199/// Required OMPT inquiry functions.
200static ompt_get_parallel_info_t ompt_get_parallel_info;
201
202typedef char ompt_tsan_clockid;
203
204static uint64_t my_next_id() {
205 static uint64_t ID = 0;
206 uint64_t ret = __sync_fetch_and_add(&ID, 1);
207 return ret;
208}
209
210static int pagesize{0};
211
212// Data structure to provide a threadsafe pool of reusable objects.
213// DataPool<Type of objects>
214namespace {
215template <typename T> struct DataPool final {
216 static __thread DataPool<T> *ThreadDataPool;
217 std::mutex DPMutex{};
218
219 // store unused objects
220 std::vector<T *> DataPointer{};
221 std::vector<T *> RemoteDataPointer{};
222
223 // store all allocated memory to finally release
224 std::list<void *> memory;
225
226 // count remotely returned data (RemoteDataPointer.size())
227 std::atomic<int> remote{0};
228
229 // totally allocated data objects in pool
230 int total{0};
231#ifdef DEBUG_DATA
232 int remoteReturn{0};
233 int localReturn{0};
234
235 int getRemote() { return remoteReturn + remote; }
236 int getLocal() { return localReturn; }
237#endif
238 int getTotal() { return total; }
239 int getMissing() {
240 return total - DataPointer.size() - RemoteDataPointer.size();
241 }
242
243 // fill the pool by allocating a page of memory
244 void newDatas() {
245 if (remote > 0) {
246 const std::lock_guard<std::mutex> lock(DPMutex);
247 // DataPointer is empty, so just swap the vectors
248 DataPointer.swap(RemoteDataPointer);
249 remote = 0;
250 return;
251 }
252 // calculate size of an object including padding to cacheline size
253 size_t elemSize = sizeof(T);
254 size_t paddedSize = (((elemSize - 1) / 64) + 1) * 64;
255 // number of padded elements to allocate
256 int ndatas = pagesize / paddedSize;
257 char *datas = (char *)malloc(ndatas * paddedSize);
258 memory.push_back(datas);
259 for (int i = 0; i < ndatas; i++) {
260 DataPointer.push_back(new (datas + i * paddedSize) T(this));
261 }
262 total += ndatas;
263 }
264
265 // get data from the pool
266 T *getData() {
267 T *ret;
268 if (DataPointer.empty())
269 newDatas();
270 ret = DataPointer.back();
271 DataPointer.pop_back();
272 return ret;
273 }
274
275 // accesses to the thread-local datapool don't need locks
276 void returnOwnData(T *data) {
277 DataPointer.emplace_back(data);
278#ifdef DEBUG_DATA
279 localReturn++;
280#endif
281 }
282
283 // returning to a remote datapool using lock
284 void returnData(T *data) {
285 const std::lock_guard<std::mutex> lock(DPMutex);
286 RemoteDataPointer.emplace_back(data);
287 remote++;
288#ifdef DEBUG_DATA
289 remoteReturn++;
290#endif
291 }
292
293 ~DataPool() {
294 // we assume all memory is returned when the thread finished / destructor is
295 // called
296 if (archer_flags->report_data_leak && getMissing() != 0) {
297 printf("ERROR: While freeing DataPool (%s) we are missing %i data "
298 "objects.\n",
299 __PRETTY_FUNCTION__, getMissing());
300 exit(-3);
301 }
302 for (auto i : DataPointer)
303 if (i)
304 i->~T();
305 for (auto i : RemoteDataPointer)
306 if (i)
307 i->~T();
308 for (auto i : memory)
309 if (i)
310 free(i);
311 }
312};
313
314template <typename T> struct DataPoolEntry {
315 DataPool<T> *owner;
316
317 static T *New() { return DataPool<T>::ThreadDataPool->getData(); }
318
319 void Delete() {
320 static_cast<T *>(this)->Reset();
321 if (owner == DataPool<T>::ThreadDataPool)
322 owner->returnOwnData(static_cast<T *>(this));
323 else
324 owner->returnData(static_cast<T *>(this));
325 }
326
327 DataPoolEntry(DataPool<T> *dp) : owner(dp) {}
328};
329
330struct DependencyData;
331typedef DataPool<DependencyData> DependencyDataPool;
332template <>
333__thread DependencyDataPool *DependencyDataPool::ThreadDataPool = nullptr;
334
335/// Data structure to store additional information for task dependency.
336struct DependencyData final : DataPoolEntry<DependencyData> {
339 ompt_tsan_clockid inoutset;
340 void *GetInPtr() { return &in; }
341 void *GetOutPtr() { return &out; }
342 void *GetInoutsetPtr() { return &inoutset; }
343
344 void Reset() {}
345
346 static DependencyData *New() { return DataPoolEntry<DependencyData>::New(); }
347
348 DependencyData(DataPool<DependencyData> *dp)
349 : DataPoolEntry<DependencyData>(dp) {}
350};
351
352struct TaskDependency {
353 void *inPtr;
354 void *outPtr;
355 void *inoutsetPtr;
356 ompt_dependence_type_t type;
357 TaskDependency(DependencyData *depData, ompt_dependence_type_t type)
358 : inPtr(depData->GetInPtr()), outPtr(depData->GetOutPtr()),
359 inoutsetPtr(depData->GetInoutsetPtr()), type(type) {}
360 void AnnotateBegin() {
361 if (type == ompt_dependence_type_out ||
362 type == ompt_dependence_type_inout ||
363 type == ompt_dependence_type_mutexinoutset) {
364 TsanHappensAfter(inPtr);
365 TsanHappensAfter(outPtr);
366 TsanHappensAfter(inoutsetPtr);
367 } else if (type == ompt_dependence_type_in) {
368 TsanHappensAfter(outPtr);
369 TsanHappensAfter(inoutsetPtr);
370 } else if (type == ompt_dependence_type_inoutset) {
371 TsanHappensAfter(inPtr);
372 TsanHappensAfter(outPtr);
373 }
374 }
375 void AnnotateEnd() {
376 if (type == ompt_dependence_type_out ||
377 type == ompt_dependence_type_inout ||
378 type == ompt_dependence_type_mutexinoutset) {
379 TsanHappensBefore(outPtr);
380 } else if (type == ompt_dependence_type_in) {
381 TsanHappensBefore(inPtr);
382 } else if (type == ompt_dependence_type_inoutset) {
383 TsanHappensBefore(inoutsetPtr);
384 }
385 }
386};
387
388struct ParallelData;
389typedef DataPool<ParallelData> ParallelDataPool;
390template <>
391__thread ParallelDataPool *ParallelDataPool::ThreadDataPool = nullptr;
392
393/// Data structure to store additional information for parallel regions.
394struct ParallelData final : DataPoolEntry<ParallelData> {
395
396 // Parallel fork is just another barrier, use Barrier[1]
397
398 /// Two addresses for relationships with barriers.
399 ompt_tsan_clockid Barrier[2];
400
401 const void *codePtr;
402
403 void *GetParallelPtr() { return &(Barrier[1]); }
404
405 void *GetBarrierPtr(unsigned Index) { return &(Barrier[Index]); }
406
407 ParallelData *Init(const void *codeptr) {
408 codePtr = codeptr;
409 return this;
410 }
411
412 void Reset() {}
413
414 static ParallelData *New(const void *codeptr) {
415 return DataPoolEntry<ParallelData>::New()->Init(codeptr);
416 }
417
418 ParallelData(DataPool<ParallelData> *dp) : DataPoolEntry<ParallelData>(dp) {}
419};
420
421static inline ParallelData *ToParallelData(ompt_data_t *parallel_data) {
422 return reinterpret_cast<ParallelData *>(parallel_data->ptr);
423}
424
425struct Taskgroup;
426typedef DataPool<Taskgroup> TaskgroupPool;
427template <> __thread TaskgroupPool *TaskgroupPool::ThreadDataPool = nullptr;
428
429/// Data structure to support stacking of taskgroups and allow synchronization.
430struct Taskgroup final : DataPoolEntry<Taskgroup> {
431 /// Its address is used for relationships of the taskgroup's task set.
433
434 /// Reference to the parent taskgroup.
435 Taskgroup *Parent;
436
437 void *GetPtr() { return &Ptr; }
438
439 Taskgroup *Init(Taskgroup *parent) {
440 Parent = parent;
441 return this;
442 }
443
444 void Reset() {}
445
446 static Taskgroup *New(Taskgroup *Parent) {
447 return DataPoolEntry<Taskgroup>::New()->Init(Parent);
448 }
449
450 Taskgroup(DataPool<Taskgroup> *dp) : DataPoolEntry<Taskgroup>(dp) {}
451};
452
453enum ArcherTaskFlag { ArcherTaskFulfilled = 0x00010000 };
454
455struct TaskData;
456typedef DataPool<TaskData> TaskDataPool;
457template <> __thread TaskDataPool *TaskDataPool::ThreadDataPool = nullptr;
458
459/// Data structure to store additional information for tasks.
460struct TaskData final : DataPoolEntry<TaskData> {
461 /// Its address is used for relationships of this task.
462 ompt_tsan_clockid Task{0};
463
464 /// Child tasks use its address to declare a relationship to a taskwait in
465 /// this task.
466 ompt_tsan_clockid Taskwait{0};
467
468 /// Child tasks use its address to model omp_all_memory dependencies
469 ompt_tsan_clockid AllMemory[2]{0};
470
471 /// Index of which barrier to use next.
472 char BarrierIndex{0};
473
474 /// Whether this task is currently executing a barrier.
475 bool InBarrier{false};
476
477 /// Whether this task is an included task.
478 int TaskType{0};
479
480 /// count execution phase
481 int execution{0};
482
483 /// Count how often this structure has been put into child tasks + 1.
484 std::atomic_int RefCount{1};
485
486 /// Reference to the parent that created this task.
487 TaskData *Parent{nullptr};
488
489 /// Reference to the team of this task.
490 ParallelData *Team{nullptr};
491
492 /// Reference to the current taskgroup that this task either belongs to or
493 /// that it just created.
494 Taskgroup *TaskGroup{nullptr};
495
496 /// Dependency information for this task.
497 TaskDependency *Dependencies{nullptr};
498
499 /// Number of dependency entries.
500 unsigned DependencyCount{0};
501
502 // The dependency-map stores DependencyData objects representing
503 // the dependency variables used on the sibling tasks created from
504 // this task
505 // We expect a rare need for the dependency-map, so alloc on demand
506 std::unordered_map<void *, DependencyData *> *DependencyMap{nullptr};
507
508#ifdef DEBUG
509 int freed{0};
510#endif
511
512 bool isIncluded() { return TaskType & ompt_task_undeferred; }
513 bool isUntied() { return TaskType & ompt_task_untied; }
514 bool isFinal() { return TaskType & ompt_task_final; }
515 bool isMergable() { return TaskType & ompt_task_mergeable; }
516 bool isMerged() { return TaskType & ompt_task_merged; }
517
518 bool isExplicit() { return TaskType & ompt_task_explicit; }
519 bool isImplicit() { return TaskType & ompt_task_implicit; }
520 bool isInitial() { return TaskType & ompt_task_initial; }
521 bool isTarget() { return TaskType & ompt_task_target; }
522
523 bool isFulfilled() { return TaskType & ArcherTaskFulfilled; }
524 void setFulfilled() { TaskType |= ArcherTaskFulfilled; }
525
526 void setAllMemoryDep() { AllMemory[0] = 1; }
527 bool hasAllMemoryDep() { return AllMemory[0]; }
528
529 void *GetTaskPtr() { return &Task; }
530
531 void *GetTaskwaitPtr() { return &Taskwait; }
532
533 void *GetLastAllMemoryPtr() { return AllMemory; }
534 void *GetNextAllMemoryPtr() { return AllMemory + 1; }
535
536 TaskData *Init(TaskData *parent, int taskType) {
537 TaskType = taskType;
538 Parent = parent;
539 Team = Parent->Team;
540 BarrierIndex = Parent->BarrierIndex;
541 if (Parent != nullptr) {
542 Parent->RefCount++;
543 // Copy over pointer to taskgroup. This task may set up its own stack
544 // but for now belongs to its parent's taskgroup.
545 TaskGroup = Parent->TaskGroup;
546 }
547 return this;
548 }
549
550 TaskData *Init(ParallelData *team, int taskType) {
551 TaskType = taskType;
552 execution = 1;
553 Team = team;
554 return this;
555 }
556
557 void Reset() {
558 InBarrier = false;
559 TaskType = 0;
560 execution = 0;
561 BarrierIndex = 0;
562 RefCount = 1;
563 Parent = nullptr;
564 Team = nullptr;
565 TaskGroup = nullptr;
566 if (DependencyMap) {
567 for (auto i : *DependencyMap)
568 i.second->Delete();
569 delete DependencyMap;
570 }
571 DependencyMap = nullptr;
572 if (Dependencies)
573 free(Dependencies);
574 Dependencies = nullptr;
575 DependencyCount = 0;
576#ifdef DEBUG
577 freed = 0;
578#endif
579 }
580
581 static TaskData *New(TaskData *parent, int taskType) {
582 return DataPoolEntry<TaskData>::New()->Init(parent, taskType);
583 }
584
585 static TaskData *New(ParallelData *team, int taskType) {
586 return DataPoolEntry<TaskData>::New()->Init(team, taskType);
587 }
588
589 TaskData(DataPool<TaskData> *dp) : DataPoolEntry<TaskData>(dp) {}
590};
591} // namespace
592
593static inline TaskData *ToTaskData(ompt_data_t *task_data) {
594 if (task_data)
595 return reinterpret_cast<TaskData *>(task_data->ptr);
596 return nullptr;
597}
598
599/// Store a mutex for each wait_id to resolve race condition with callbacks.
600static std::unordered_map<ompt_wait_id_t, std::mutex> Locks;
601static std::mutex LocksMutex;
602
603static void ompt_tsan_thread_begin(ompt_thread_t thread_type,
604 ompt_data_t *thread_data) {
605 ParallelDataPool::ThreadDataPool = new ParallelDataPool;
606 TsanNewMemory(ParallelDataPool::ThreadDataPool,
607 sizeof(ParallelDataPool::ThreadDataPool));
608 TaskgroupPool::ThreadDataPool = new TaskgroupPool;
609 TsanNewMemory(TaskgroupPool::ThreadDataPool,
610 sizeof(TaskgroupPool::ThreadDataPool));
611 TaskDataPool::ThreadDataPool = new TaskDataPool;
612 TsanNewMemory(TaskDataPool::ThreadDataPool,
613 sizeof(TaskDataPool::ThreadDataPool));
614 DependencyDataPool::ThreadDataPool = new DependencyDataPool;
615 TsanNewMemory(DependencyDataPool::ThreadDataPool,
616 sizeof(DependencyDataPool::ThreadDataPool));
617 thread_data->value = my_next_id();
618}
619
620static void ompt_tsan_thread_end(ompt_data_t *thread_data) {
622 delete ParallelDataPool::ThreadDataPool;
623 delete TaskgroupPool::ThreadDataPool;
624 delete TaskDataPool::ThreadDataPool;
625 delete DependencyDataPool::ThreadDataPool;
627}
628
629/// OMPT event callbacks for handling parallel regions.
630
631static void ompt_tsan_parallel_begin(ompt_data_t *parent_task_data,
632 const ompt_frame_t *parent_task_frame,
633 ompt_data_t *parallel_data,
634 uint32_t requested_team_size, int flag,
635 const void *codeptr_ra) {
636 ParallelData *Data = ParallelData::New(codeptr_ra);
637 parallel_data->ptr = Data;
638
639 TsanHappensBefore(Data->GetParallelPtr());
640 if (archer_flags->ignore_serial && ToTaskData(parent_task_data)->isInitial())
642}
643
644static void ompt_tsan_parallel_end(ompt_data_t *parallel_data,
645 ompt_data_t *task_data, int flag,
646 const void *codeptr_ra) {
647 if (archer_flags->ignore_serial && ToTaskData(task_data)->isInitial())
649 ParallelData *Data = ToParallelData(parallel_data);
650 TsanHappensAfter(Data->GetBarrierPtr(0));
651 TsanHappensAfter(Data->GetBarrierPtr(1));
652
653 Data->Delete();
654
655#if (LLVM_VERSION >= 40)
656 if (&__archer_get_omp_status) {
657 if (__archer_get_omp_status() == 0 && archer_flags->flush_shadow)
658 __tsan_flush_memory();
659 }
660#endif
661}
662
663static void ompt_tsan_implicit_task(ompt_scope_endpoint_t endpoint,
664 ompt_data_t *parallel_data,
665 ompt_data_t *task_data,
666 unsigned int team_size,
667 unsigned int thread_num, int type) {
668 switch (endpoint) {
669 case ompt_scope_begin:
670 if (type & ompt_task_initial) {
671 parallel_data->ptr = ParallelData::New(nullptr);
672 }
673 task_data->ptr = TaskData::New(ToParallelData(parallel_data), type);
674 TsanHappensAfter(ToParallelData(parallel_data)->GetParallelPtr());
675 TsanFuncEntry(ToParallelData(parallel_data)->codePtr);
676 break;
677 case ompt_scope_end: {
678 TaskData *Data = ToTaskData(task_data);
679#ifdef DEBUG
680 assert(Data->freed == 0 && "Implicit task end should only be called once!");
681 Data->freed = 1;
682#endif
683 assert(Data->RefCount == 1 &&
684 "All tasks should have finished at the implicit barrier!");
685 if (type & ompt_task_initial) {
686 Data->Team->Delete();
687 }
688 Data->Delete();
689 TsanFuncExit();
690 break;
691 }
692 case ompt_scope_beginend:
693 // Should not occur according to OpenMP 5.1
694 // Tested in OMPT tests
695 break;
696 }
697}
698
699static void ompt_tsan_sync_region(ompt_sync_region_t kind,
700 ompt_scope_endpoint_t endpoint,
701 ompt_data_t *parallel_data,
702 ompt_data_t *task_data,
703 const void *codeptr_ra) {
704 TaskData *Data = ToTaskData(task_data);
705 switch (endpoint) {
706 case ompt_scope_begin:
707 case ompt_scope_beginend:
708 TsanFuncEntry(codeptr_ra);
709 switch (kind) {
710 case ompt_sync_region_barrier_implementation:
711 case ompt_sync_region_barrier_implicit:
712 case ompt_sync_region_barrier_explicit:
713 case ompt_sync_region_barrier_implicit_parallel:
714 case ompt_sync_region_barrier_implicit_workshare:
715 case ompt_sync_region_barrier_teams:
716 case ompt_sync_region_barrier: {
717 char BarrierIndex = Data->BarrierIndex;
718 TsanHappensBefore(Data->Team->GetBarrierPtr(BarrierIndex));
719
720 if (hasReductionCallback < ompt_set_always) {
721 // We ignore writes inside the barrier. These would either occur during
722 // 1. reductions performed by the runtime which are guaranteed to be
723 // race-free.
724 // 2. execution of another task.
725 // For the latter case we will re-enable tracking in task_switch.
726 Data->InBarrier = true;
728 }
729
730 break;
731 }
732
733 case ompt_sync_region_taskwait:
734 break;
735
736 case ompt_sync_region_taskgroup:
737 Data->TaskGroup = Taskgroup::New(Data->TaskGroup);
738 break;
739
740 case ompt_sync_region_reduction:
741 // should never be reached
742 break;
743 }
744 if (endpoint == ompt_scope_begin)
745 break;
747 case ompt_scope_end:
748 TsanFuncExit();
749 switch (kind) {
750 case ompt_sync_region_barrier_implementation:
751 case ompt_sync_region_barrier_implicit:
752 case ompt_sync_region_barrier_explicit:
753 case ompt_sync_region_barrier_implicit_parallel:
754 case ompt_sync_region_barrier_implicit_workshare:
755 case ompt_sync_region_barrier_teams:
756 case ompt_sync_region_barrier: {
757 if (hasReductionCallback < ompt_set_always) {
758 // We want to track writes after the barrier again.
759 Data->InBarrier = false;
761 }
762
763 char BarrierIndex = Data->BarrierIndex;
764 // Barrier will end after it has been entered by all threads.
765 if (parallel_data)
766 TsanHappensAfter(Data->Team->GetBarrierPtr(BarrierIndex));
767
768 // It is not guaranteed that all threads have exited this barrier before
769 // we enter the next one. So we will use a different address.
770 // We are however guaranteed that this current barrier is finished
771 // by the time we exit the next one. So we can then reuse the first
772 // address.
773 Data->BarrierIndex = (BarrierIndex + 1) % 2;
774 break;
775 }
776
777 case ompt_sync_region_taskwait: {
778 if (Data->execution > 1)
779 TsanHappensAfter(Data->GetTaskwaitPtr());
780 break;
781 }
782
783 case ompt_sync_region_taskgroup: {
784 assert(Data->TaskGroup != nullptr &&
785 "Should have at least one taskgroup!");
786
787 TsanHappensAfter(Data->TaskGroup->GetPtr());
788
789 // Delete this allocated taskgroup, all descendent task are finished by
790 // now.
791 Taskgroup *Parent = Data->TaskGroup->Parent;
792 Data->TaskGroup->Delete();
793 Data->TaskGroup = Parent;
794 break;
795 }
796
797 case ompt_sync_region_reduction:
798 // Should not occur according to OpenMP 5.1
799 // Tested in OMPT tests
800 break;
801 }
802 break;
803 }
804}
805
806static void ompt_tsan_reduction(ompt_sync_region_t kind,
807 ompt_scope_endpoint_t endpoint,
808 ompt_data_t *parallel_data,
809 ompt_data_t *task_data,
810 const void *codeptr_ra) {
811 switch (endpoint) {
812 case ompt_scope_begin:
813 switch (kind) {
814 case ompt_sync_region_reduction:
816 break;
817 default:
818 break;
819 }
820 break;
821 case ompt_scope_end:
822 switch (kind) {
823 case ompt_sync_region_reduction:
825 break;
826 default:
827 break;
828 }
829 break;
830 case ompt_scope_beginend:
831 // Should not occur according to OpenMP 5.1
832 // Tested in OMPT tests
833 // Would have no implications for DR detection
834 break;
835 }
836}
837
838/// OMPT event callbacks for handling tasks.
839
841 ompt_data_t *parent_task_data, /* id of parent task */
842 const ompt_frame_t *parent_frame, /* frame data for parent task */
843 ompt_data_t *new_task_data, /* id of created task */
844 int type, int has_dependences,
845 const void *codeptr_ra) /* pointer to outlined function */
846{
847 TaskData *Data;
848 assert(new_task_data->ptr == NULL &&
849 "Task data should be initialized to NULL");
850 if (type & ompt_task_initial) {
851 ompt_data_t *parallel_data;
852 int team_size = 1;
853 ompt_get_parallel_info(0, &parallel_data, &team_size);
854 ParallelData *PData = ParallelData::New(nullptr);
855 parallel_data->ptr = PData;
856
857 Data = TaskData::New(PData, type);
858 new_task_data->ptr = Data;
859 } else if (type & ompt_task_undeferred) {
860 Data = TaskData::New(ToTaskData(parent_task_data), type);
861 new_task_data->ptr = Data;
862 } else if (type & ompt_task_explicit || type & ompt_task_target) {
863 Data = TaskData::New(ToTaskData(parent_task_data), type);
864 new_task_data->ptr = Data;
865
866 // Use the newly created address. We cannot use a single address from the
867 // parent because that would declare wrong relationships with other
868 // sibling tasks that may be created before this task is started!
869 TsanHappensBefore(Data->GetTaskPtr());
870 ToTaskData(parent_task_data)->execution++;
871 }
872}
873
874static void freeTask(TaskData *task) {
875 while (task != nullptr && --task->RefCount == 0) {
876 TaskData *Parent = task->Parent;
877 task->Delete();
878 task = Parent;
879 }
880}
881
882// LastAllMemoryPtr marks the beginning of an all_memory epoch
883// NextAllMemoryPtr marks the end of an all_memory epoch
884// All tasks with depend begin execution after LastAllMemoryPtr
885// and end before NextAllMemoryPtr
886static void releaseDependencies(TaskData *task) {
887 if (archer_flags->all_memory) {
888 if (task->hasAllMemoryDep()) {
889 TsanHappensBefore(task->Parent->GetLastAllMemoryPtr());
890 TsanHappensBefore(task->Parent->GetNextAllMemoryPtr());
891 } else if (task->DependencyCount)
892 TsanHappensBefore(task->Parent->GetNextAllMemoryPtr());
893 }
894 for (unsigned i = 0; i < task->DependencyCount; i++) {
895 task->Dependencies[i].AnnotateEnd();
896 }
897}
898
899static void acquireDependencies(TaskData *task) {
900 if (archer_flags->all_memory) {
901 if (task->hasAllMemoryDep())
902 TsanHappensAfter(task->Parent->GetNextAllMemoryPtr());
903 else if (task->DependencyCount)
904 TsanHappensAfter(task->Parent->GetLastAllMemoryPtr());
905 }
906 for (unsigned i = 0; i < task->DependencyCount; i++) {
907 task->Dependencies[i].AnnotateBegin();
908 }
909}
910
911static void completeTask(TaskData *FromTask) {
912 if (!FromTask)
913 return;
914 // Task-end happens after a possible omp_fulfill_event call
915 if (FromTask->isFulfilled())
916 TsanHappensAfter(FromTask->GetTaskPtr());
917 // Included tasks are executed sequentially, no need to track
918 // synchronization
919 if (!FromTask->isIncluded()) {
920 // Task will finish before a barrier in the surrounding parallel region
921 // ...
922 ParallelData *PData = FromTask->Team;
923 TsanHappensBefore(PData->GetBarrierPtr(FromTask->BarrierIndex));
924
925 // ... and before an eventual taskwait by the parent thread.
926 TsanHappensBefore(FromTask->Parent->GetTaskwaitPtr());
927
928 if (FromTask->TaskGroup != nullptr) {
929 // This task is part of a taskgroup, so it will finish before the
930 // corresponding taskgroup_end.
931 TsanHappensBefore(FromTask->TaskGroup->GetPtr());
932 }
933 }
934 // release dependencies
935 releaseDependencies(FromTask);
936}
937
938static void suspendTask(TaskData *FromTask) {
939 if (!FromTask)
940 return;
941 // Task may be resumed at a later point in time.
942 TsanHappensBefore(FromTask->GetTaskPtr());
943}
944
945static void switchTasks(TaskData *FromTask, TaskData *ToTask) {
946 // Legacy handling for missing reduction callback
947 if (hasReductionCallback < ompt_set_always) {
948 if (FromTask && FromTask->InBarrier) {
949 // We want to ignore writes in the runtime code during barriers,
950 // but not when executing tasks with user code!
952 }
953 if (ToTask && ToTask->InBarrier) {
954 // We want to ignore writes in the runtime code during barriers,
955 // but not when executing tasks with user code!
957 }
958 }
959 //// Not yet used
960 // if (FromTask)
961 // FromTask->deactivate();
962 // if (ToTask)
963 // ToTask->activate();
964}
965
966static void endTask(TaskData *FromTask) {
967 if (!FromTask)
968 return;
969}
970
971static void startTask(TaskData *ToTask) {
972 if (!ToTask)
973 return;
974 // Handle dependencies on first execution of the task
975 if (ToTask->execution == 0) {
976 ToTask->execution++;
977 acquireDependencies(ToTask);
978 }
979 // 1. Task will begin execution after it has been created.
980 // 2. Task will resume after it has been switched away.
981 TsanHappensAfter(ToTask->GetTaskPtr());
982}
983
984static void ompt_tsan_task_schedule(ompt_data_t *first_task_data,
985 ompt_task_status_t prior_task_status,
986 ompt_data_t *second_task_data) {
987
988 //
989 // The necessary action depends on prior_task_status:
990 //
991 // ompt_task_early_fulfill = 5,
992 // -> ignored
993 //
994 // ompt_task_late_fulfill = 6,
995 // -> first completed, first freed, second ignored
996 //
997 // ompt_task_complete = 1,
998 // ompt_task_cancel = 3,
999 // -> first completed, first freed, second starts
1000 //
1001 // ompt_taskwait_complete = 8,
1002 // -> first starts, first completes, first freed, second ignored
1003 //
1004 // ompt_task_detach = 4,
1005 // ompt_task_yield = 2,
1006 // ompt_task_switch = 7
1007 // -> first suspended, second starts
1008 //
1009
1010 TaskData *FromTask = ToTaskData(first_task_data);
1011 TaskData *ToTask = ToTaskData(second_task_data);
1012
1013 switch (prior_task_status) {
1014 case ompt_task_early_fulfill:
1015 TsanHappensBefore(FromTask->GetTaskPtr());
1016 FromTask->setFulfilled();
1017 return;
1018 case ompt_task_late_fulfill:
1019 TsanHappensAfter(FromTask->GetTaskPtr());
1020 completeTask(FromTask);
1021 freeTask(FromTask);
1022 return;
1023 case ompt_taskwait_complete:
1024 acquireDependencies(FromTask);
1025 freeTask(FromTask);
1026 return;
1027 case ompt_task_complete:
1028 completeTask(FromTask);
1029 endTask(FromTask);
1030 switchTasks(FromTask, ToTask);
1031 freeTask(FromTask);
1032 return;
1033 case ompt_task_cancel:
1034 completeTask(FromTask);
1035 endTask(FromTask);
1036 switchTasks(FromTask, ToTask);
1037 freeTask(FromTask);
1038 startTask(ToTask);
1039 return;
1040 case ompt_task_detach:
1041 endTask(FromTask);
1042 suspendTask(FromTask);
1043 switchTasks(FromTask, ToTask);
1044 startTask(ToTask);
1045 return;
1046 case ompt_task_yield:
1047 suspendTask(FromTask);
1048 switchTasks(FromTask, ToTask);
1049 startTask(ToTask);
1050 return;
1051 case ompt_task_switch:
1052 suspendTask(FromTask);
1053 switchTasks(FromTask, ToTask);
1054 startTask(ToTask);
1055 return;
1056 }
1057}
1058
1059static void ompt_tsan_dependences(ompt_data_t *task_data,
1060 const ompt_dependence_t *deps, int ndeps) {
1061 if (ndeps > 0) {
1062 // Copy the data to use it in task_switch and task_end.
1063 TaskData *Data = ToTaskData(task_data);
1064 if (!Data->Parent) {
1065 // Return since doacross dependences are not supported yet.
1066 return;
1067 }
1068 if (!Data->Parent->DependencyMap)
1069 Data->Parent->DependencyMap =
1070 new std::unordered_map<void *, DependencyData *>();
1071 Data->Dependencies =
1072 (TaskDependency *)malloc(sizeof(TaskDependency) * ndeps);
1073 Data->DependencyCount = ndeps;
1074 for (int i = 0, d = 0; i < ndeps; i++, d++) {
1075 if (deps[i].dependence_type == ompt_dependence_type_out_all_memory ||
1076 deps[i].dependence_type == ompt_dependence_type_inout_all_memory) {
1077 Data->setAllMemoryDep();
1078 Data->DependencyCount--;
1079 if (!archer_flags->all_memory) {
1080 printf("The application uses omp_all_memory, but Archer was\n"
1081 "started to not consider omp_all_memory. This can lead\n"
1082 "to false data race alerts.\n"
1083 "Include all_memory=1 in ARCHER_OPTIONS to consider\n"
1084 "omp_all_memory from the beginning.\n");
1085 archer_flags->all_memory = 1;
1086 }
1087 d--;
1088 continue;
1089 }
1090 auto ret = Data->Parent->DependencyMap->insert(
1091 std::make_pair(deps[i].variable.ptr, nullptr));
1092 if (ret.second) {
1093 ret.first->second = DependencyData::New();
1094 }
1095 new ((void *)(Data->Dependencies + d))
1096 TaskDependency(ret.first->second, deps[i].dependence_type);
1097 }
1098
1099 // This callback is executed before this task is first started.
1100 TsanHappensBefore(Data->GetTaskPtr());
1101 }
1102}
1103
1104/// OMPT event callbacks for handling locking.
1105static void ompt_tsan_mutex_acquired(ompt_mutex_t kind, ompt_wait_id_t wait_id,
1106 const void *codeptr_ra) {
1107
1108 // Acquire our own lock to make sure that
1109 // 1. the previous release has finished.
1110 // 2. the next acquire doesn't start before we have finished our release.
1111 LocksMutex.lock();
1112 std::mutex &Lock = Locks[wait_id];
1113 LocksMutex.unlock();
1114
1115 Lock.lock();
1116 TsanHappensAfter(&Lock);
1117}
1118
1119static void ompt_tsan_mutex_released(ompt_mutex_t kind, ompt_wait_id_t wait_id,
1120 const void *codeptr_ra) {
1121 LocksMutex.lock();
1122 std::mutex &Lock = Locks[wait_id];
1123 LocksMutex.unlock();
1124 TsanHappensBefore(&Lock);
1125
1126 Lock.unlock();
1127}
1128
1129// callback , signature , variable to store result , required support level
1130#define SET_OPTIONAL_CALLBACK_T(event, type, result, level) \
1131 do { \
1132 ompt_callback_##type##_t tsan_##event = &ompt_tsan_##event; \
1133 result = ompt_set_callback(ompt_callback_##event, \
1134 (ompt_callback_t)tsan_##event); \
1135 if (result < level) \
1136 printf("Registered callback '" #event "' is not supported at " #level \
1137 " (%i)\n", \
1138 result); \
1139 } while (0)
1140
1141#define SET_CALLBACK_T(event, type) \
1142 do { \
1143 int res; \
1144 SET_OPTIONAL_CALLBACK_T(event, type, res, ompt_set_always); \
1145 } while (0)
1146
1147#define SET_CALLBACK(event) SET_CALLBACK_T(event, event)
1148
1149#define findTsanFunction(f, fSig) \
1150 do { \
1151 void *fp = dlsym(RTLD_DEFAULT, #f); \
1152 if (fp) \
1153 f = fSig fp; \
1154 else \
1155 printf("Unable to find TSan function " #f ".\n"); \
1156 } while (0)
1157
1158#define findTsanFunctionSilent(f, fSig) f = fSig dlsym(RTLD_DEFAULT, #f)
1159
1160static int ompt_tsan_initialize(ompt_function_lookup_t lookup, int device_num,
1161 ompt_data_t *tool_data) {
1162 const char *options = getenv("TSAN_OPTIONS");
1163 TsanFlags tsan_flags(options);
1164
1165 ompt_set_callback_t ompt_set_callback =
1166 (ompt_set_callback_t)lookup("ompt_set_callback");
1167 if (ompt_set_callback == NULL) {
1168 std::cerr << "Could not set callback, exiting..." << std::endl;
1169 std::exit(1);
1170 }
1172 (ompt_get_parallel_info_t)lookup("ompt_get_parallel_info");
1173
1174 if (ompt_get_parallel_info == NULL) {
1175 fprintf(stderr, "Could not get inquiry function 'ompt_get_parallel_info', "
1176 "exiting...\n");
1177 exit(1);
1178 }
1179
1180 findTsanFunction(AnnotateHappensAfter,
1181 (void (*)(const char *, int, const volatile void *)));
1182 findTsanFunction(AnnotateHappensBefore,
1183 (void (*)(const char *, int, const volatile void *)));
1184 findTsanFunction(AnnotateIgnoreWritesBegin, (void (*)(const char *, int)));
1185 findTsanFunction(AnnotateIgnoreWritesEnd, (void (*)(const char *, int)));
1187 AnnotateNewMemory,
1188 (void (*)(const char *, int, const volatile void *, size_t)));
1189 findTsanFunction(__tsan_func_entry, (void (*)(const void *)));
1190 findTsanFunction(__tsan_func_exit, (void (*)(void)));
1191
1192 SET_CALLBACK(thread_begin);
1193 SET_CALLBACK(thread_end);
1194 SET_CALLBACK(parallel_begin);
1195 SET_CALLBACK(implicit_task);
1196 SET_CALLBACK(sync_region);
1197 SET_CALLBACK(parallel_end);
1198
1199 SET_CALLBACK(task_create);
1200 SET_CALLBACK(task_schedule);
1201 SET_CALLBACK(dependences);
1202
1203 SET_CALLBACK_T(mutex_acquired, mutex);
1204 SET_CALLBACK_T(mutex_released, mutex);
1205 SET_OPTIONAL_CALLBACK_T(reduction, sync_region, hasReductionCallback,
1206 ompt_set_never);
1207
1208 if (!tsan_flags.ignore_noninstrumented_modules)
1209 fprintf(stderr,
1210 "Warning: please export "
1211 "TSAN_OPTIONS='ignore_noninstrumented_modules=1' "
1212 "to avoid false positive reports from the OpenMP runtime!\n");
1213 if (archer_flags->ignore_serial)
1215
1216 return 1; // success
1217}
1218
1219static void ompt_tsan_finalize(ompt_data_t *tool_data) {
1220 if (archer_flags->ignore_serial)
1222 if (archer_flags->print_max_rss) {
1223 struct rusage end;
1224 getrusage(RUSAGE_SELF, &end);
1225 printf("MAX RSS[KiB] during execution: %ld\n", end.ru_maxrss);
1226 }
1227
1228 if (archer_flags)
1229 delete archer_flags;
1230}
1231
1232extern "C" ompt_start_tool_result_t *
1233ompt_start_tool(unsigned int omp_version, const char *runtime_version) {
1234 const char *options = getenv("ARCHER_OPTIONS");
1235 archer_flags = new ArcherFlags(options);
1236 if (!archer_flags->enabled) {
1237 if (archer_flags->verbose)
1238 std::cout << "Archer disabled, stopping operation" << std::endl;
1239 delete archer_flags;
1240 return NULL;
1241 }
1242
1243 pagesize = getpagesize();
1244
1247
1248 // The OMPT start-up code uses dlopen with RTLD_LAZY. Therefore, we cannot
1249 // rely on dlopen to fail if TSan is missing, but would get a runtime error
1250 // for the first TSan call. We use __tsan_init to detect whether
1251 // an implementation of the Annotation interface is available in the
1252 // execution or disable the tool (by returning NULL).
1253
1254 void (*__tsan_init)(void) = nullptr;
1255
1256 findTsanFunctionSilent(__tsan_init, (void (*)(void)));
1257 if (!__tsan_init) // if we are not running on TSAN, give a different
1258 // tool the chance to be loaded
1259 {
1260 if (archer_flags->verbose)
1261 std::cout << "Archer detected OpenMP application without TSan; "
1262 "stopping operation"
1263 << std::endl;
1264 delete archer_flags;
1265 return NULL;
1266 }
1267
1268 if (archer_flags->verbose)
1269 std::cout << "Archer detected OpenMP application with TSan, supplying "
1270 "OpenMP synchronization semantics"
1271 << std::endl;
1272 return &ompt_start_tool_result;
1273}
static ompt_set_callback_t ompt_set_callback
Definition callback.h:157
static ompt_get_parallel_info_t ompt_get_parallel_info
Definition callback.h:163
void
Definition ittnotify.h:3324
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type size_t void * data
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp end
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id parent
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type type
static kmp_bootstrap_lock_t lock
Definition kmp_i18n.cpp:57
#define i
Definition kmp_stub.cpp:87
struct ompt_start_tool_result_t ompt_start_tool_result_t
unsigned * Index(unsigned *p, unsigned i, unsigned j, unsigned bound2)
return ret
static ompt_start_tool_result_t * ompt_start_tool_result
#define ompt_start_tool
static void __ompt_tsan_func(Args...)
#define TsanHappensBefore(cv)
#define TsanFuncEntry(pc)
static void ompt_tsan_parallel_begin(ompt_data_t *parent_task_data, const ompt_frame_t *parent_task_frame, ompt_data_t *parallel_data, uint32_t requested_team_size, int flag, const void *codeptr_ra)
OMPT event callbacks for handling parallel regions.
static TaskData * ToTaskData(ompt_data_t *task_data)
#define findTsanFunctionSilent(f, fSig)
static uint64_t my_next_id()
#define DECLARE_TSAN_FUNCTION(name,...)
static void ompt_tsan_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)
static void freeTask(TaskData *task)
static void switchTasks(TaskData *FromTask, TaskData *ToTask)
#define TsanHappensAfter(cv)
static void endTask(TaskData *FromTask)
static ArcherFlags * archer_flags
static void ompt_tsan_dependences(ompt_data_t *task_data, const ompt_dependence_t *deps, int ndeps)
static void ompt_tsan_thread_end(ompt_data_t *thread_data)
#define SET_CALLBACK_T(event, type)
static void ompt_tsan_task_create(ompt_data_t *parent_task_data, const ompt_frame_t *parent_frame, ompt_data_t *new_task_data, int type, int has_dependences, const void *codeptr_ra)
OMPT event callbacks for handling tasks.
#define TsanFuncExit()
#define TsanIgnoreWritesEnd()
static int pagesize
static std::unordered_map< ompt_wait_id_t, std::mutex > Locks
Store a mutex for each wait_id to resolve race condition with callbacks.
static void ompt_tsan_task_schedule(ompt_data_t *first_task_data, ompt_task_status_t prior_task_status, ompt_data_t *second_task_data)
static void acquireDependencies(TaskData *task)
#define SET_CALLBACK(event)
static void completeTask(TaskData *FromTask)
static void ompt_tsan_mutex_acquired(ompt_mutex_t kind, ompt_wait_id_t wait_id, const void *codeptr_ra)
OMPT event callbacks for handling locking.
static void suspendTask(TaskData *FromTask)
static std::mutex LocksMutex
#define SET_OPTIONAL_CALLBACK_T(event, type, result, level)
static void ompt_tsan_parallel_end(ompt_data_t *parallel_data, ompt_data_t *task_data, int flag, const void *codeptr_ra)
static void ompt_tsan_mutex_released(ompt_mutex_t kind, ompt_wait_id_t wait_id, const void *codeptr_ra)
static int ompt_tsan_initialize(ompt_function_lookup_t lookup, int device_num, ompt_data_t *tool_data)
#define TsanNewMemory(addr, size)
#define KMP_FALLTHROUGH()
Definition ompt-tsan.cpp:50
static void ompt_tsan_finalize(ompt_data_t *tool_data)
static int hasReductionCallback
Definition ompt-tsan.cpp:53
static void ompt_tsan_implicit_task(ompt_scope_endpoint_t endpoint, ompt_data_t *parallel_data, ompt_data_t *task_data, unsigned int team_size, unsigned int thread_num, int type)
#define findTsanFunction(f, fSig)
static void ompt_tsan_reduction(ompt_sync_region_t kind, ompt_scope_endpoint_t endpoint, ompt_data_t *parallel_data, ompt_data_t *task_data, const void *codeptr_ra)
#define TsanIgnoreWritesBegin()
static void ompt_tsan_thread_begin(ompt_thread_t thread_type, ompt_data_t *thread_data)
static void startTask(TaskData *ToTask)
char ompt_tsan_clockid
static void releaseDependencies(TaskData *task)
volatile int flag
__attribute__((noinline))