505 lines · cpp
1//===- OmptTester.cpp - ompTest OMPT tool implementation --------*- 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/// This file represents the core implementation file for the ompTest library.11/// It provides the actual OMPT tool implementation: registers callbacks, etc.12/// OMPT callbacks are passed to their corresponding handler, which in turn13/// notifies all registered asserters.14///15//===----------------------------------------------------------------------===//16 17#include "OmptTester.h"18 19#include <atomic>20#include <cassert>21#include <cstdlib>22#include <cstring>23 24using namespace omptest;25 26// Callback handler, which receives and relays OMPT callbacks27extern OmptCallbackHandler *Handler;28 29// EventListener, which actually prints the OMPT events30static OmptEventReporter *EventReporter;31 32// From openmp/runtime/test/ompt/callback.h33#define register_ompt_callback_t(name, type) \34 do { \35 type f_##name = &on_##name; \36 if (ompt_set_callback(name, (ompt_callback_t)f_##name) == ompt_set_never) \37 printf("0: Could not register callback '" #name "'\n"); \38 } while (0)39 40#define register_ompt_callback(name) register_ompt_callback_t(name, name##_t)41 42#define OMPT_BUFFER_REQUEST_SIZE 25643 44#ifdef OPENMP_LIBOMPTEST_BUILD_STANDALONE45std::vector<std::pair<std::string, TestSuite>> TestRegistrar::Tests;46#endif47 48static std::atomic<ompt_id_t> NextOpId{0x8000000000000001};49static bool UseEMICallbacks = false;50static bool UseTracing = false;51static bool RunAsTestSuite = false;52static bool ColoredLog = false;53 54// OMPT entry point handles55static ompt_set_trace_ompt_t ompt_set_trace_ompt = 0;56static ompt_start_trace_t ompt_start_trace = 0;57static ompt_flush_trace_t ompt_flush_trace = 0;58static ompt_stop_trace_t ompt_stop_trace = 0;59static ompt_get_record_ompt_t ompt_get_record_ompt = 0;60static ompt_advance_buffer_cursor_t ompt_advance_buffer_cursor = 0;61static ompt_get_record_type_t ompt_get_record_type_fn = 0;62 63// OMPT device side tracing: Currently traced devices64typedef std::unordered_set<ompt_device_t *> OmptDeviceSetTy;65typedef std::unique_ptr<OmptDeviceSetTy> OmptDeviceSetPtrTy;66static OmptDeviceSetPtrTy TracedDevices;67 68// OMPT callbacks69 70// Trace record callbacks71static void on_ompt_callback_buffer_request(int device_num,72 ompt_buffer_t **buffer,73 size_t *bytes) {74 *bytes = OMPT_BUFFER_REQUEST_SIZE;75 *buffer = malloc(*bytes);76 OmptCallbackHandler::get().handleBufferRequest(device_num, buffer, bytes);77}78 79// Note: This callback must handle a null begin cursor. Currently,80// ompt_get_record_ompt, print_record_ompt, and81// ompt_advance_buffer_cursor handle a null cursor.82static void on_ompt_callback_buffer_complete(83 int device_num, ompt_buffer_t *buffer,84 size_t bytes, /* bytes returned in this callback */85 ompt_buffer_cursor_t begin, int buffer_owned) {86 OmptCallbackHandler::get().handleBufferComplete(device_num, buffer, bytes,87 begin, buffer_owned);88 89 int Status = 1;90 ompt_buffer_cursor_t CurrentPos = begin;91 while (Status) {92 ompt_record_ompt_t *Record = ompt_get_record_ompt(buffer, CurrentPos);93 if (ompt_get_record_type_fn(buffer, CurrentPos) != ompt_record_ompt) {94 printf("Warning: received non-ompt type buffer object\n");95 }96 // TODO: Sometimes it may happen that the retrieved record may be null?!97 // Only handle non-null records98 if (Record != nullptr)99 OmptCallbackHandler::get().handleBufferRecord(Record);100 Status = ompt_advance_buffer_cursor(/*device=*/NULL, buffer, bytes,101 CurrentPos, &CurrentPos);102 }103 if (buffer_owned) {104 OmptCallbackHandler::get().handleBufferRecordDeallocation(buffer);105 free(buffer);106 }107}108 109static ompt_set_result_t set_trace_ompt(ompt_device_t *Device) {110 if (!ompt_set_trace_ompt)111 return ompt_set_error;112 113 if (UseEMICallbacks) {114 ompt_set_trace_ompt(Device, /*enable=*/1,115 /*etype=*/ompt_callback_target_emi);116 ompt_set_trace_ompt(Device, /*enable=*/1,117 /*etype=*/ompt_callback_target_data_op_emi);118 ompt_set_trace_ompt(Device, /*enable=*/1,119 /*etype=*/ompt_callback_target_submit_emi);120 } else {121 ompt_set_trace_ompt(Device, /*enable=*/1, /*etype=*/ompt_callback_target);122 ompt_set_trace_ompt(Device, /*enable=*/1,123 /*etype=*/ompt_callback_target_data_op);124 ompt_set_trace_ompt(Device, /*enable=*/1,125 /*etype=*/ompt_callback_target_submit);126 }127 128 return ompt_set_always;129}130 131/////// HOST-RELATED //////132 133static void on_ompt_callback_thread_begin(ompt_thread_t thread_type,134 ompt_data_t *thread_data) {135 OmptCallbackHandler::get().handleThreadBegin(thread_type, thread_data);136}137 138static void on_ompt_callback_thread_end(ompt_data_t *thread_data) {139 OmptCallbackHandler::get().handleThreadEnd(thread_data);140}141 142static void on_ompt_callback_parallel_begin(143 ompt_data_t *encountering_task_data,144 const ompt_frame_t *encountering_task_frame, ompt_data_t *parallel_data,145 unsigned int requested_parallelism, int flags, const void *codeptr_ra) {146 OmptCallbackHandler::get().handleParallelBegin(147 encountering_task_data, encountering_task_frame, parallel_data,148 requested_parallelism, flags, codeptr_ra);149}150 151static void on_ompt_callback_parallel_end(ompt_data_t *parallel_data,152 ompt_data_t *encountering_task_data,153 int flags, const void *codeptr_ra) {154 OmptCallbackHandler::get().handleParallelEnd(155 parallel_data, encountering_task_data, flags, codeptr_ra);156}157 158static void159on_ompt_callback_task_create(ompt_data_t *encountering_task_data,160 const ompt_frame_t *encountering_task_frame,161 ompt_data_t *new_task_data, int flags,162 int has_dependences, const void *codeptr_ra) {163 OmptCallbackHandler::get().handleTaskCreate(164 encountering_task_data, encountering_task_frame, new_task_data, flags,165 has_dependences, codeptr_ra);166}167 168static void on_ompt_callback_task_schedule(ompt_data_t *prior_task_data,169 ompt_task_status_t prior_task_status,170 ompt_data_t *next_task_data) {171 OmptCallbackHandler::get().handleTaskSchedule(172 prior_task_data, prior_task_status, next_task_data);173}174 175static void on_ompt_callback_implicit_task(ompt_scope_endpoint_t endpoint,176 ompt_data_t *parallel_data,177 ompt_data_t *task_data,178 unsigned int actual_parallelism,179 unsigned int index, int flags) {180 OmptCallbackHandler::get().handleImplicitTask(181 endpoint, parallel_data, task_data, actual_parallelism, index, flags);182}183 184// Callbacks as of Table 19.4, which are not considered required for a minimal185// conforming OMPT implementation.186static void on_ompt_callback_work(ompt_work_t work_type,187 ompt_scope_endpoint_t endpoint,188 ompt_data_t *parallel_data,189 ompt_data_t *task_data, uint64_t count,190 const void *codeptr_ra) {191 OmptCallbackHandler::get().handleWork(work_type, endpoint, parallel_data,192 task_data, count, codeptr_ra);193}194 195static void on_ompt_callback_dispatch(ompt_data_t *parallel_data,196 ompt_data_t *task_data,197 ompt_dispatch_t kind,198 ompt_data_t instance) {199 OmptCallbackHandler::get().handleDispatch(parallel_data, task_data, kind,200 instance);201}202 203static void on_ompt_callback_sync_region(ompt_sync_region_t kind,204 ompt_scope_endpoint_t endpoint,205 ompt_data_t *parallel_data,206 ompt_data_t *task_data,207 const void *codeptr_ra) {208 OmptCallbackHandler::get().handleSyncRegion(kind, endpoint, parallel_data,209 task_data, codeptr_ra);210}211 212/////// DEVICE-RELATED //////213 214// Synchronous callbacks215static void on_ompt_callback_device_initialize(int device_num, const char *type,216 ompt_device_t *device,217 ompt_function_lookup_t lookup,218 const char *documentation) {219 OmptCallbackHandler::get().handleDeviceInitialize(device_num, type, device,220 lookup, documentation);221 if (!UseTracing)222 return;223 224 if (!lookup) {225 printf("Trace collection disabled on device %d\n", device_num);226 return;227 }228 229 ompt_set_trace_ompt = (ompt_set_trace_ompt_t)lookup("ompt_set_trace_ompt");230 ompt_start_trace = (ompt_start_trace_t)lookup("ompt_start_trace");231 ompt_flush_trace = (ompt_flush_trace_t)lookup("ompt_flush_trace");232 ompt_stop_trace = (ompt_stop_trace_t)lookup("ompt_stop_trace");233 ompt_get_record_ompt = (ompt_get_record_ompt_t)lookup("ompt_get_record_ompt");234 ompt_advance_buffer_cursor =235 (ompt_advance_buffer_cursor_t)lookup("ompt_advance_buffer_cursor");236 237 ompt_get_record_type_fn =238 (ompt_get_record_type_t)lookup("ompt_get_record_type");239 if (!ompt_get_record_type_fn) {240 printf("Warning: No function ompt_get_record_type found in device "241 "callbacks\n");242 }243 244 static bool IsDeviceMapInitialized = false;245 if (!IsDeviceMapInitialized) {246 TracedDevices = std::make_unique<OmptDeviceSetTy>();247 IsDeviceMapInitialized = true;248 }249 250 set_trace_ompt(device);251 252 // In many scenarios, this is a good place to start the253 // trace. If start_trace is called from the main program before this254 // callback is dispatched, the start_trace handle will be null. This255 // is because this device_init callback is invoked during the first256 // target construct implementation.257 258 start_trace(device);259}260 261static void on_ompt_callback_device_finalize(int device_num) {262 OmptCallbackHandler::get().handleDeviceFinalize(device_num);263}264 265static void on_ompt_callback_device_load(int device_num, const char *filename,266 int64_t offset_in_file,267 void *vma_in_file, size_t bytes,268 void *host_addr, void *device_addr,269 uint64_t module_id) {270 OmptCallbackHandler::get().handleDeviceLoad(271 device_num, filename, offset_in_file, vma_in_file, bytes, host_addr,272 device_addr, module_id);273}274 275static void on_ompt_callback_device_unload(int device_num, uint64_t module_id) {276 OmptCallbackHandler::get().handleDeviceUnload(device_num, module_id);277}278 279static void on_ompt_callback_target_data_op(280 ompt_id_t target_id, ompt_id_t host_op_id, ompt_target_data_op_t optype,281 void *src_addr, int src_device_num, void *dest_addr, int dest_device_num,282 size_t bytes, const void *codeptr_ra) {283 OmptCallbackHandler::get().handleTargetDataOp(284 target_id, host_op_id, optype, src_addr, src_device_num, dest_addr,285 dest_device_num, bytes, codeptr_ra);286}287 288static void on_ompt_callback_target(ompt_target_t kind,289 ompt_scope_endpoint_t endpoint,290 int device_num, ompt_data_t *task_data,291 ompt_id_t target_id,292 const void *codeptr_ra) {293 OmptCallbackHandler::get().handleTarget(kind, endpoint, device_num, task_data,294 target_id, codeptr_ra);295}296 297static void on_ompt_callback_target_submit(ompt_id_t target_id,298 ompt_id_t host_op_id,299 unsigned int requested_num_teams) {300 OmptCallbackHandler::get().handleTargetSubmit(target_id, host_op_id,301 requested_num_teams);302}303 304static void on_ompt_callback_target_data_op_emi(305 ompt_scope_endpoint_t endpoint, ompt_data_t *target_task_data,306 ompt_data_t *target_data, ompt_id_t *host_op_id,307 ompt_target_data_op_t optype, void *src_addr, int src_device_num,308 void *dest_addr, int dest_device_num, size_t bytes,309 const void *codeptr_ra) {310 assert(codeptr_ra != 0 && "Unexpected null codeptr");311 // Both src and dest must not be null312 // However, for omp_target_alloc only the END call holds a value for one of313 // the two entries314 if (optype != ompt_target_data_alloc)315 assert((src_addr != 0 || dest_addr != 0) && "Both src and dest addr null");316 if (endpoint == ompt_scope_begin)317 *host_op_id = NextOpId.fetch_add(1, std::memory_order_relaxed);318 OmptCallbackHandler::get().handleTargetDataOpEmi(319 endpoint, target_task_data, target_data, host_op_id, optype, src_addr,320 src_device_num, dest_addr, dest_device_num, bytes, codeptr_ra);321}322 323static void on_ompt_callback_target_emi(ompt_target_t kind,324 ompt_scope_endpoint_t endpoint,325 int device_num, ompt_data_t *task_data,326 ompt_data_t *target_task_data,327 ompt_data_t *target_data,328 const void *codeptr_ra) {329 assert(codeptr_ra != 0 && "Unexpected null codeptr");330 if (endpoint == ompt_scope_begin)331 target_data->value = NextOpId.fetch_add(1, std::memory_order_relaxed);332 OmptCallbackHandler::get().handleTargetEmi(kind, endpoint, device_num,333 task_data, target_task_data,334 target_data, codeptr_ra);335}336 337static void on_ompt_callback_target_submit_emi(338 ompt_scope_endpoint_t endpoint, ompt_data_t *target_data,339 ompt_id_t *host_op_id, unsigned int requested_num_teams) {340 OmptCallbackHandler::get().handleTargetSubmitEmi(341 endpoint, target_data, host_op_id, requested_num_teams);342}343 344static void on_ompt_callback_target_map(ompt_id_t target_id,345 unsigned int nitems, void **host_addr,346 void **device_addr, size_t *bytes,347 unsigned int *mapping_flags,348 const void *codeptr_ra) {349 assert(0 && "Target map callback is unimplemented");350}351 352static void on_ompt_callback_target_map_emi(ompt_data_t *target_data,353 unsigned int nitems,354 void **host_addr,355 void **device_addr, size_t *bytes,356 unsigned int *mapping_flags,357 const void *codeptr_ra) {358 assert(0 && "Target map emi callback is unimplemented");359}360 361/// Load the value of a given boolean environmental variable.362bool getBoolEnvironmentVariable(const char *VariableName) {363 if (VariableName == nullptr)364 return false;365 if (const char *EnvValue = std::getenv(VariableName)) {366 std::string S{EnvValue};367 for (auto &C : S)368 C = (char)std::tolower(C);369 if (S == "1" || S == "on" || S == "true" || S == "yes")370 return true;371 }372 return false;373}374 375/// Called by the OMP runtime to initialize the OMPT376int ompt_initialize(ompt_function_lookup_t lookup, int initial_device_num,377 ompt_data_t *tool_data) {378 ompt_set_callback_t ompt_set_callback = nullptr;379 ompt_set_callback = (ompt_set_callback_t)lookup("ompt_set_callback");380 if (!ompt_set_callback)381 return 0; // failure382 383 UseEMICallbacks = getBoolEnvironmentVariable("OMPTEST_USE_OMPT_EMI");384 UseTracing = getBoolEnvironmentVariable("OMPTEST_USE_OMPT_TRACING");385 RunAsTestSuite = getBoolEnvironmentVariable("OMPTEST_RUN_AS_TESTSUITE");386 ColoredLog = getBoolEnvironmentVariable("OMPTEST_LOG_COLORED");387 388 register_ompt_callback(ompt_callback_thread_begin);389 register_ompt_callback(ompt_callback_thread_end);390 register_ompt_callback(ompt_callback_parallel_begin);391 register_ompt_callback(ompt_callback_parallel_end);392 register_ompt_callback(ompt_callback_work);393 register_ompt_callback(ompt_callback_dispatch);394 register_ompt_callback(ompt_callback_task_create);395 // register_ompt_callback(ompt_callback_dependences);396 // register_ompt_callback(ompt_callback_task_dependence);397 register_ompt_callback(ompt_callback_task_schedule);398 register_ompt_callback(ompt_callback_implicit_task);399 // register_ompt_callback(ompt_callback_masked);400 register_ompt_callback(ompt_callback_sync_region);401 // register_ompt_callback(ompt_callback_mutex_acquire);402 // register_ompt_callback(ompt_callback_mutex);403 // register_ompt_callback(ompt_callback_nestLock);404 // register_ompt_callback(ompt_callback_flush);405 // register_ompt_callback(ompt_callback_cancel);406 register_ompt_callback(ompt_callback_device_initialize);407 register_ompt_callback(ompt_callback_device_finalize);408 register_ompt_callback(ompt_callback_device_load);409 register_ompt_callback(ompt_callback_device_unload);410 411 if (UseEMICallbacks) {412 register_ompt_callback(ompt_callback_target_emi);413 register_ompt_callback(ompt_callback_target_submit_emi);414 register_ompt_callback(ompt_callback_target_data_op_emi);415 register_ompt_callback(ompt_callback_target_map_emi);416 } else {417 register_ompt_callback(ompt_callback_target);418 register_ompt_callback(ompt_callback_target_submit);419 register_ompt_callback(ompt_callback_target_data_op);420 register_ompt_callback(ompt_callback_target_map);421 }422 423 // Construct & subscribe the reporter, so it gets notified of events424 EventReporter = new OmptEventReporter();425 OmptCallbackHandler::get().subscribe(EventReporter);426 427 if (RunAsTestSuite)428 EventReporter->setActive(false);429 430 return 1; // success431}432 433void ompt_finalize(ompt_data_t *tool_data) {434 assert(Handler && "Callback handler should be present at this point");435 assert(EventReporter && "EventReporter should be present at this point");436 delete Handler;437 delete EventReporter;438}439 440#ifdef __cplusplus441extern "C" {442#endif443/// Called from the OMP Runtime to start / initialize the tool444ompt_start_tool_result_t *ompt_start_tool(unsigned int omp_version,445 const char *runtime_version) {446 static ompt_start_tool_result_t ompt_start_tool_result = {447 &ompt_initialize, &ompt_finalize, {0}};448 return &ompt_start_tool_result;449}450 451int start_trace(ompt_device_t *Device) {452 if (!ompt_start_trace)453 return 0;454 455 // Start tracing this device (add to set)456 assert(TracedDevices->find(Device) == TracedDevices->end() &&457 "Device already present in the map");458 TracedDevices->insert(Device);459 460 return ompt_start_trace(Device, &on_ompt_callback_buffer_request,461 &on_ompt_callback_buffer_complete);462}463 464int flush_trace(ompt_device_t *Device) {465 if (!ompt_flush_trace)466 return 0;467 return ompt_flush_trace(Device);468}469 470int flush_traced_devices() {471 if (!ompt_flush_trace || TracedDevices == nullptr)472 return 0;473 474 size_t NumFlushedDevices = 0;475 for (auto Device : *TracedDevices)476 if (ompt_flush_trace(Device) == 1)477 ++NumFlushedDevices;478 479 // Provide time to process triggered assert events480 std::this_thread::sleep_for(std::chrono::milliseconds(1));481 482 return (NumFlushedDevices == TracedDevices->size());483}484 485int stop_trace(ompt_device_t *Device) {486 if (!ompt_stop_trace)487 return 0;488 489 // Stop tracing this device (erase from set)490 assert(TracedDevices->find(Device) != TracedDevices->end() &&491 "Device not present in the map");492 TracedDevices->erase(Device);493 494 return ompt_stop_trace(Device);495}496 497// This is primarily used to stop unwanted prints from happening.498void libomptest_global_eventreporter_set_active(bool State) {499 assert(EventReporter && "EventReporter should be present at this point");500 EventReporter->setActive(State);501}502#ifdef __cplusplus503}504#endif505