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