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