1/*
2 * ompt-general.cpp -- OMPT implementation of interface functions
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 license information.
9// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10//
11//===----------------------------------------------------------------------===//
12
13/*****************************************************************************
14 * system include files
15 ****************************************************************************/
16
17#include <assert.h>
18
19#include <stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#if KMP_OS_UNIX
24#include <dlfcn.h>
25#endif
26
27/*****************************************************************************
28 * ompt include files
29 ****************************************************************************/
30
31#include "ompt-specific.cpp"
32
33/*****************************************************************************
34 * macros
35 ****************************************************************************/
36
37#define ompt_get_callback_success 1
38#define ompt_get_callback_failure 0
39
40#define no_tool_present 0
41
42#define OMPT_API_ROUTINE static
43
44#ifndef OMPT_STR_MATCH
45#define OMPT_STR_MATCH(haystack, needle) (!strcasecmp(haystack, needle))
46#endif
47
48/*****************************************************************************
49 * types
50 ****************************************************************************/
51
52typedef struct {
53  const char *state_name;
54  ompt_state_t state_id;
55} ompt_state_info_t;
56
57typedef struct {
58  const char *name;
59  kmp_mutex_impl_t id;
60} kmp_mutex_impl_info_t;
61
62enum tool_setting_e {
63  omp_tool_error,
64  omp_tool_unset,
65  omp_tool_disabled,
66  omp_tool_enabled
67};
68
69/*****************************************************************************
70 * global variables
71 ****************************************************************************/
72
73ompt_callbacks_active_t ompt_enabled;
74
75ompt_state_info_t ompt_state_info[] = {
76#define ompt_state_macro(state, code) {#state, state},
77    FOREACH_OMPT_STATE(ompt_state_macro)
78#undef ompt_state_macro
79};
80
81kmp_mutex_impl_info_t kmp_mutex_impl_info[] = {
82#define kmp_mutex_impl_macro(name, id) {#name, name},
83    FOREACH_KMP_MUTEX_IMPL(kmp_mutex_impl_macro)
84#undef kmp_mutex_impl_macro
85};
86
87ompt_callbacks_internal_t ompt_callbacks;
88
89static ompt_start_tool_result_t *ompt_start_tool_result = NULL;
90
91/*****************************************************************************
92 * forward declarations
93 ****************************************************************************/
94
95static ompt_interface_fn_t ompt_fn_lookup(const char *s);
96
97OMPT_API_ROUTINE ompt_data_t *ompt_get_thread_data(void);
98
99/*****************************************************************************
100 * initialization and finalization (private operations)
101 ****************************************************************************/
102
103typedef ompt_start_tool_result_t *(*ompt_start_tool_t)(unsigned int,
104                                                       const char *);
105
106#if KMP_OS_DARWIN
107
108// While Darwin supports weak symbols, the library that wishes to provide a new
109// implementation has to link against this runtime which defeats the purpose
110// of having tools that are agnostic of the underlying runtime implementation.
111//
112// Fortunately, the linker includes all symbols of an executable in the global
113// symbol table by default so dlsym() even finds static implementations of
114// ompt_start_tool. For this to work on Linux, -Wl,--export-dynamic needs to be
115// passed when building the application which we don't want to rely on.
116
117static ompt_start_tool_result_t *ompt_tool_darwin(unsigned int omp_version,
118                                                  const char *runtime_version) {
119  ompt_start_tool_result_t *ret = NULL;
120  // Search symbol in the current address space.
121  ompt_start_tool_t start_tool =
122      (ompt_start_tool_t)dlsym(RTLD_DEFAULT, "ompt_start_tool");
123  if (start_tool) {
124    ret = start_tool(omp_version, runtime_version);
125  }
126  return ret;
127}
128
129#elif OMPT_HAVE_WEAK_ATTRIBUTE
130
131// On Unix-like systems that support weak symbols the following implementation
132// of ompt_start_tool() will be used in case no tool-supplied implementation of
133// this function is present in the address space of a process.
134
135_OMP_EXTERN OMPT_WEAK_ATTRIBUTE ompt_start_tool_result_t *
136ompt_start_tool(unsigned int omp_version, const char *runtime_version) {
137  ompt_start_tool_result_t *ret = NULL;
138  // Search next symbol in the current address space. This can happen if the
139  // runtime library is linked before the tool. Since glibc 2.2 strong symbols
140  // don't override weak symbols that have been found before unless the user
141  // sets the environment variable LD_DYNAMIC_WEAK.
142  ompt_start_tool_t next_tool =
143      (ompt_start_tool_t)dlsym(RTLD_NEXT, "ompt_start_tool");
144  if (next_tool) {
145    ret = next_tool(omp_version, runtime_version);
146  }
147  return ret;
148}
149
150#elif OMPT_HAVE_PSAPI
151
152// On Windows, the ompt_tool_windows function is used to find the
153// ompt_start_tool symbol across all modules loaded by a process. If
154// ompt_start_tool is found, ompt_start_tool's return value is used to
155// initialize the tool. Otherwise, NULL is returned and OMPT won't be enabled.
156
157#include <psapi.h>
158#pragma comment(lib, "psapi.lib")
159
160// The number of loaded modules to start enumeration with EnumProcessModules()
161#define NUM_MODULES 128
162
163static ompt_start_tool_result_t *
164ompt_tool_windows(unsigned int omp_version, const char *runtime_version) {
165  int i;
166  DWORD needed, new_size;
167  HMODULE *modules;
168  HANDLE process = GetCurrentProcess();
169  modules = (HMODULE *)malloc(NUM_MODULES * sizeof(HMODULE));
170  ompt_start_tool_t ompt_tool_p = NULL;
171
172#if OMPT_DEBUG
173  printf("ompt_tool_windows(): looking for ompt_start_tool\n");
174#endif
175  if (!EnumProcessModules(process, modules, NUM_MODULES * sizeof(HMODULE),
176                          &needed)) {
177    // Regardless of the error reason use the stub initialization function
178    free(modules);
179    return NULL;
180  }
181  // Check if NUM_MODULES is enough to list all modules
182  new_size = needed / sizeof(HMODULE);
183  if (new_size > NUM_MODULES) {
184#if OMPT_DEBUG
185    printf("ompt_tool_windows(): resize buffer to %d bytes\n", needed);
186#endif
187    modules = (HMODULE *)realloc(modules, needed);
188    // If resizing failed use the stub function.
189    if (!EnumProcessModules(process, modules, needed, &needed)) {
190      free(modules);
191      return NULL;
192    }
193  }
194  for (i = 0; i < new_size; ++i) {
195    (FARPROC &)ompt_tool_p = GetProcAddress(modules[i], "ompt_start_tool");
196    if (ompt_tool_p) {
197#if OMPT_DEBUG
198      TCHAR modName[MAX_PATH];
199      if (GetModuleFileName(modules[i], modName, MAX_PATH))
200        printf("ompt_tool_windows(): ompt_start_tool found in module %s\n",
201               modName);
202#endif
203      free(modules);
204      return (*ompt_tool_p)(omp_version, runtime_version);
205    }
206#if OMPT_DEBUG
207    else {
208      TCHAR modName[MAX_PATH];
209      if (GetModuleFileName(modules[i], modName, MAX_PATH))
210        printf("ompt_tool_windows(): ompt_start_tool not found in module %s\n",
211               modName);
212    }
213#endif
214  }
215  free(modules);
216  return NULL;
217}
218#else
219#error Activation of OMPT is not supported on this platform.
220#endif
221
222static ompt_start_tool_result_t *
223ompt_try_start_tool(unsigned int omp_version, const char *runtime_version) {
224  ompt_start_tool_result_t *ret = NULL;
225  ompt_start_tool_t start_tool = NULL;
226#if KMP_OS_WINDOWS
227  // Cannot use colon to describe a list of absolute paths on Windows
228  const char *sep = ";";
229#else
230  const char *sep = ":";
231#endif
232
233#if KMP_OS_DARWIN
234  // Try in the current address space
235  ret = ompt_tool_darwin(omp_version, runtime_version);
236#elif OMPT_HAVE_WEAK_ATTRIBUTE
237  ret = ompt_start_tool(omp_version, runtime_version);
238#elif OMPT_HAVE_PSAPI
239  ret = ompt_tool_windows(omp_version, runtime_version);
240#else
241#error Activation of OMPT is not supported on this platform.
242#endif
243  if (ret)
244    return ret;
245
246  // Try tool-libraries-var ICV
247  const char *tool_libs = getenv("OMP_TOOL_LIBRARIES");
248  if (tool_libs) {
249    char *libs = __kmp_str_format("%s", tool_libs);
250    char *buf;
251    char *fname = __kmp_str_token(libs, sep, &buf);
252    while (fname) {
253#if KMP_OS_UNIX
254      void *h = dlopen(fname, RTLD_LAZY);
255      if (h) {
256        start_tool = (ompt_start_tool_t)dlsym(h, "ompt_start_tool");
257#elif KMP_OS_WINDOWS
258      HMODULE h = LoadLibrary(fname);
259      if (h) {
260        start_tool = (ompt_start_tool_t)GetProcAddress(h, "ompt_start_tool");
261#else
262#error Activation of OMPT is not supported on this platform.
263#endif
264        if (start_tool && (ret = (*start_tool)(omp_version, runtime_version)))
265          break;
266      }
267      fname = __kmp_str_token(NULL, sep, &buf);
268    }
269    __kmp_str_free(&libs);
270  }
271  if (ret)
272    return ret;
273
274#if KMP_OS_UNIX
275  { // Non-standard: load archer tool if application is built with TSan
276    const char *fname = "libarcher.so";
277    void *h = dlopen(fname, RTLD_LAZY);
278    if (h) {
279      start_tool = (ompt_start_tool_t)dlsym(h, "ompt_start_tool");
280      if (start_tool)
281        ret = (*start_tool)(omp_version, runtime_version);
282      if (ret)
283        return ret;
284    }
285  }
286#endif
287  return ret;
288}
289
290void ompt_pre_init() {
291  //--------------------------------------------------
292  // Execute the pre-initialization logic only once.
293  //--------------------------------------------------
294  static int ompt_pre_initialized = 0;
295
296  if (ompt_pre_initialized)
297    return;
298
299  ompt_pre_initialized = 1;
300
301  //--------------------------------------------------
302  // Use a tool iff a tool is enabled and available.
303  //--------------------------------------------------
304  const char *ompt_env_var = getenv("OMP_TOOL");
305  tool_setting_e tool_setting = omp_tool_error;
306
307  if (!ompt_env_var || !strcmp(ompt_env_var, ""))
308    tool_setting = omp_tool_unset;
309  else if (OMPT_STR_MATCH(ompt_env_var, "disabled"))
310    tool_setting = omp_tool_disabled;
311  else if (OMPT_STR_MATCH(ompt_env_var, "enabled"))
312    tool_setting = omp_tool_enabled;
313
314#if OMPT_DEBUG
315  printf("ompt_pre_init(): tool_setting = %d\n", tool_setting);
316#endif
317  switch (tool_setting) {
318  case omp_tool_disabled:
319    break;
320
321  case omp_tool_unset:
322  case omp_tool_enabled:
323
324    //--------------------------------------------------
325    // Load tool iff specified in environment variable
326    //--------------------------------------------------
327    ompt_start_tool_result =
328        ompt_try_start_tool(__kmp_openmp_version, ompt_get_runtime_version());
329
330    memset(&ompt_enabled, 0, sizeof(ompt_enabled));
331    break;
332
333  case omp_tool_error:
334    fprintf(stderr, "Warning: OMP_TOOL has invalid value \"%s\".\n"
335                    "  legal values are (NULL,\"\",\"disabled\","
336                    "\"enabled\").\n",
337            ompt_env_var);
338    break;
339  }
340#if OMPT_DEBUG
341  printf("ompt_pre_init(): ompt_enabled = %d\n", ompt_enabled);
342#endif
343}
344
345extern "C" int omp_get_initial_device(void);
346
347void ompt_post_init() {
348  //--------------------------------------------------
349  // Execute the post-initialization logic only once.
350  //--------------------------------------------------
351  static int ompt_post_initialized = 0;
352
353  if (ompt_post_initialized)
354    return;
355
356  ompt_post_initialized = 1;
357
358  //--------------------------------------------------
359  // Initialize the tool if so indicated.
360  //--------------------------------------------------
361  if (ompt_start_tool_result) {
362    ompt_enabled.enabled = !!ompt_start_tool_result->initialize(
363        ompt_fn_lookup, omp_get_initial_device(), &(ompt_start_tool_result->tool_data));
364
365    if (!ompt_enabled.enabled) {
366      // tool not enabled, zero out the bitmap, and done
367      memset(&ompt_enabled, 0, sizeof(ompt_enabled));
368      return;
369    }
370
371    kmp_info_t *root_thread = ompt_get_thread();
372
373    ompt_set_thread_state(root_thread, ompt_state_overhead);
374
375    if (ompt_enabled.ompt_callback_thread_begin) {
376      ompt_callbacks.ompt_callback(ompt_callback_thread_begin)(
377          ompt_thread_initial, __ompt_get_thread_data_internal());
378    }
379    ompt_data_t *task_data;
380    ompt_data_t *parallel_data;
381    __ompt_get_task_info_internal(0, NULL, &task_data, NULL, &parallel_data, NULL);
382    if (ompt_enabled.ompt_callback_implicit_task) {
383      ompt_callbacks.ompt_callback(ompt_callback_implicit_task)(
384          ompt_scope_begin, parallel_data, task_data, 1, 1, ompt_task_initial);
385    }
386
387    ompt_set_thread_state(root_thread, ompt_state_work_serial);
388  }
389}
390
391void ompt_fini() {
392  if (ompt_enabled.enabled) {
393    ompt_start_tool_result->finalize(&(ompt_start_tool_result->tool_data));
394  }
395
396  memset(&ompt_enabled, 0, sizeof(ompt_enabled));
397}
398
399/*****************************************************************************
400 * interface operations
401 ****************************************************************************/
402
403/*****************************************************************************
404 * state
405 ****************************************************************************/
406
407OMPT_API_ROUTINE int ompt_enumerate_states(int current_state, int *next_state,
408                                           const char **next_state_name) {
409  const static int len = sizeof(ompt_state_info) / sizeof(ompt_state_info_t);
410  int i = 0;
411
412  for (i = 0; i < len - 1; i++) {
413    if (ompt_state_info[i].state_id == current_state) {
414      *next_state = ompt_state_info[i + 1].state_id;
415      *next_state_name = ompt_state_info[i + 1].state_name;
416      return 1;
417    }
418  }
419
420  return 0;
421}
422
423OMPT_API_ROUTINE int ompt_enumerate_mutex_impls(int current_impl,
424                                                int *next_impl,
425                                                const char **next_impl_name) {
426  const static int len =
427      sizeof(kmp_mutex_impl_info) / sizeof(kmp_mutex_impl_info_t);
428  int i = 0;
429  for (i = 0; i < len - 1; i++) {
430    if (kmp_mutex_impl_info[i].id != current_impl)
431      continue;
432    *next_impl = kmp_mutex_impl_info[i + 1].id;
433    *next_impl_name = kmp_mutex_impl_info[i + 1].name;
434    return 1;
435  }
436  return 0;
437}
438
439/*****************************************************************************
440 * callbacks
441 ****************************************************************************/
442
443OMPT_API_ROUTINE ompt_set_result_t ompt_set_callback(ompt_callbacks_t which,
444                                       ompt_callback_t callback) {
445  switch (which) {
446
447#define ompt_event_macro(event_name, callback_type, event_id)                  \
448  case event_name:                                                             \
449    ompt_callbacks.ompt_callback(event_name) = (callback_type)callback;        \
450    ompt_enabled.event_name = (callback != 0);                                 \
451    if (callback)                                                              \
452      return ompt_event_implementation_status(event_name);                     \
453    else                                                                       \
454      return ompt_set_always;
455
456    FOREACH_OMPT_EVENT(ompt_event_macro)
457
458#undef ompt_event_macro
459
460  default:
461    return ompt_set_error;
462  }
463}
464
465OMPT_API_ROUTINE int ompt_get_callback(ompt_callbacks_t which,
466                                       ompt_callback_t *callback) {
467  if (!ompt_enabled.enabled)
468    return ompt_get_callback_failure;
469
470  switch (which) {
471
472#define ompt_event_macro(event_name, callback_type, event_id)                  \
473  case event_name: {                                                           \
474    ompt_callback_t mycb =                                                     \
475        (ompt_callback_t)ompt_callbacks.ompt_callback(event_name);             \
476    if (ompt_enabled.event_name && mycb) {                                     \
477      *callback = mycb;                                                        \
478      return ompt_get_callback_success;                                        \
479    }                                                                          \
480    return ompt_get_callback_failure;                                          \
481  }
482
483    FOREACH_OMPT_EVENT(ompt_event_macro)
484
485#undef ompt_event_macro
486
487  default:
488    return ompt_get_callback_failure;
489  }
490}
491
492/*****************************************************************************
493 * parallel regions
494 ****************************************************************************/
495
496OMPT_API_ROUTINE int ompt_get_parallel_info(int ancestor_level,
497                                            ompt_data_t **parallel_data,
498                                            int *team_size) {
499  if (!ompt_enabled.enabled)
500    return 0;
501  return __ompt_get_parallel_info_internal(ancestor_level, parallel_data,
502                                           team_size);
503}
504
505OMPT_API_ROUTINE int ompt_get_state(ompt_wait_id_t *wait_id) {
506  if (!ompt_enabled.enabled)
507    return ompt_state_work_serial;
508  int thread_state = __ompt_get_state_internal(wait_id);
509
510  if (thread_state == ompt_state_undefined) {
511    thread_state = ompt_state_work_serial;
512  }
513
514  return thread_state;
515}
516
517/*****************************************************************************
518 * tasks
519 ****************************************************************************/
520
521OMPT_API_ROUTINE ompt_data_t *ompt_get_thread_data(void) {
522  if (!ompt_enabled.enabled)
523    return NULL;
524  return __ompt_get_thread_data_internal();
525}
526
527OMPT_API_ROUTINE int ompt_get_task_info(int ancestor_level, int *type,
528                                        ompt_data_t **task_data,
529                                        ompt_frame_t **task_frame,
530                                        ompt_data_t **parallel_data,
531                                        int *thread_num) {
532  if (!ompt_enabled.enabled)
533    return 0;
534  return __ompt_get_task_info_internal(ancestor_level, type, task_data,
535                                       task_frame, parallel_data, thread_num);
536}
537
538OMPT_API_ROUTINE int ompt_get_task_memory(void **addr, size_t *size,
539                                          int block) {
540  return __ompt_get_task_memory_internal(addr, size, block);
541}
542
543/*****************************************************************************
544 * num_procs
545 ****************************************************************************/
546
547OMPT_API_ROUTINE int ompt_get_num_procs(void) {
548  // copied from kmp_ftn_entry.h (but modified: OMPT can only be called when
549  // runtime is initialized)
550  return __kmp_avail_proc;
551}
552
553/*****************************************************************************
554 * places
555 ****************************************************************************/
556
557OMPT_API_ROUTINE int ompt_get_num_places(void) {
558// copied from kmp_ftn_entry.h (but modified)
559#if !KMP_AFFINITY_SUPPORTED
560  return 0;
561#else
562  if (!KMP_AFFINITY_CAPABLE())
563    return 0;
564  return __kmp_affinity_num_masks;
565#endif
566}
567
568OMPT_API_ROUTINE int ompt_get_place_proc_ids(int place_num, int ids_size,
569                                             int *ids) {
570// copied from kmp_ftn_entry.h (but modified)
571#if !KMP_AFFINITY_SUPPORTED
572  return 0;
573#else
574  int i, count;
575  int tmp_ids[ids_size];
576  if (!KMP_AFFINITY_CAPABLE())
577    return 0;
578  if (place_num < 0 || place_num >= (int)__kmp_affinity_num_masks)
579    return 0;
580  /* TODO: Is this safe for asynchronous call from signal handler during runtime
581   * shutdown? */
582  kmp_affin_mask_t *mask = KMP_CPU_INDEX(__kmp_affinity_masks, place_num);
583  count = 0;
584  KMP_CPU_SET_ITERATE(i, mask) {
585    if ((!KMP_CPU_ISSET(i, __kmp_affin_fullMask)) ||
586        (!KMP_CPU_ISSET(i, mask))) {
587      continue;
588    }
589    if (count < ids_size)
590      tmp_ids[count] = i;
591    count++;
592  }
593  if (ids_size >= count) {
594    for (i = 0; i < count; i++) {
595      ids[i] = tmp_ids[i];
596    }
597  }
598  return count;
599#endif
600}
601
602OMPT_API_ROUTINE int ompt_get_place_num(void) {
603// copied from kmp_ftn_entry.h (but modified)
604#if !KMP_AFFINITY_SUPPORTED
605  return -1;
606#else
607  if (!ompt_enabled.enabled || __kmp_get_gtid() < 0)
608    return -1;
609
610  int gtid;
611  kmp_info_t *thread;
612  if (!KMP_AFFINITY_CAPABLE())
613    return -1;
614  gtid = __kmp_entry_gtid();
615  thread = __kmp_thread_from_gtid(gtid);
616  if (thread == NULL || thread->th.th_current_place < 0)
617    return -1;
618  return thread->th.th_current_place;
619#endif
620}
621
622OMPT_API_ROUTINE int ompt_get_partition_place_nums(int place_nums_size,
623                                                   int *place_nums) {
624// copied from kmp_ftn_entry.h (but modified)
625#if !KMP_AFFINITY_SUPPORTED
626  return 0;
627#else
628  if (!ompt_enabled.enabled || __kmp_get_gtid() < 0)
629    return 0;
630
631  int i, gtid, place_num, first_place, last_place, start, end;
632  kmp_info_t *thread;
633  if (!KMP_AFFINITY_CAPABLE())
634    return 0;
635  gtid = __kmp_entry_gtid();
636  thread = __kmp_thread_from_gtid(gtid);
637  if (thread == NULL)
638    return 0;
639  first_place = thread->th.th_first_place;
640  last_place = thread->th.th_last_place;
641  if (first_place < 0 || last_place < 0)
642    return 0;
643  if (first_place <= last_place) {
644    start = first_place;
645    end = last_place;
646  } else {
647    start = last_place;
648    end = first_place;
649  }
650  if (end - start <= place_nums_size)
651    for (i = 0, place_num = start; place_num <= end; ++place_num, ++i) {
652      place_nums[i] = place_num;
653    }
654  return end - start + 1;
655#endif
656}
657
658/*****************************************************************************
659 * places
660 ****************************************************************************/
661
662OMPT_API_ROUTINE int ompt_get_proc_id(void) {
663  if (!ompt_enabled.enabled || __kmp_get_gtid() < 0)
664    return -1;
665#if KMP_OS_LINUX
666  return sched_getcpu();
667#elif KMP_OS_WINDOWS
668  PROCESSOR_NUMBER pn;
669  GetCurrentProcessorNumberEx(&pn);
670  return 64 * pn.Group + pn.Number;
671#else
672  return -1;
673#endif
674}
675
676/*****************************************************************************
677 * compatability
678 ****************************************************************************/
679
680/*
681 * Currently unused function
682OMPT_API_ROUTINE int ompt_get_ompt_version() { return OMPT_VERSION; }
683*/
684
685/*****************************************************************************
686* application-facing API
687 ****************************************************************************/
688
689/*----------------------------------------------------------------------------
690 | control
691 ---------------------------------------------------------------------------*/
692
693int __kmp_control_tool(uint64_t command, uint64_t modifier, void *arg) {
694
695  if (ompt_enabled.enabled) {
696    if (ompt_enabled.ompt_callback_control_tool) {
697      return ompt_callbacks.ompt_callback(ompt_callback_control_tool)(
698          command, modifier, arg, OMPT_LOAD_RETURN_ADDRESS(__kmp_entry_gtid()));
699    } else {
700      return -1;
701    }
702  } else {
703    return -2;
704  }
705}
706
707/*****************************************************************************
708 * misc
709 ****************************************************************************/
710
711OMPT_API_ROUTINE uint64_t ompt_get_unique_id(void) {
712  return __ompt_get_unique_id_internal();
713}
714
715OMPT_API_ROUTINE void ompt_finalize_tool(void) { __kmp_internal_end_atexit(); }
716
717/*****************************************************************************
718 * Target
719 ****************************************************************************/
720
721OMPT_API_ROUTINE int ompt_get_target_info(uint64_t *device_num,
722                                          ompt_id_t *target_id,
723                                          ompt_id_t *host_op_id) {
724  return 0; // thread is not in a target region
725}
726
727OMPT_API_ROUTINE int ompt_get_num_devices(void) {
728  return 1; // only one device (the current device) is available
729}
730
731/*****************************************************************************
732 * API inquiry for tool
733 ****************************************************************************/
734
735static ompt_interface_fn_t ompt_fn_lookup(const char *s) {
736
737#define ompt_interface_fn(fn)                                                  \
738  fn##_t fn##_f = fn;                                                          \
739  if (strcmp(s, #fn) == 0)                                                     \
740    return (ompt_interface_fn_t)fn##_f;
741
742  FOREACH_OMPT_INQUIRY_FN(ompt_interface_fn)
743
744  return (ompt_interface_fn_t)0;
745}
746