Home | History | Annotate | Line # | Download | only in libgomp
      1      1.1  mrg /* OpenACC Profiling Interface
      2      1.1  mrg 
      3  1.1.1.2  mrg    Copyright (C) 2019-2022 Free Software Foundation, Inc.
      4      1.1  mrg 
      5      1.1  mrg    Contributed by Mentor, a Siemens Business.
      6      1.1  mrg 
      7      1.1  mrg    This file is part of the GNU Offloading and Multi Processing Library
      8      1.1  mrg    (libgomp).
      9      1.1  mrg 
     10      1.1  mrg    Libgomp is free software; you can redistribute it and/or modify it
     11      1.1  mrg    under the terms of the GNU General Public License as published by
     12      1.1  mrg    the Free Software Foundation; either version 3, or (at your option)
     13      1.1  mrg    any later version.
     14      1.1  mrg 
     15      1.1  mrg    Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
     16      1.1  mrg    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     17      1.1  mrg    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     18      1.1  mrg    more details.
     19      1.1  mrg 
     20      1.1  mrg    Under Section 7 of GPL version 3, you are granted additional
     21      1.1  mrg    permissions described in the GCC Runtime Library Exception, version
     22      1.1  mrg    3.1, as published by the Free Software Foundation.
     23      1.1  mrg 
     24      1.1  mrg    You should have received a copy of the GNU General Public License and
     25      1.1  mrg    a copy of the GCC Runtime Library Exception along with this program;
     26      1.1  mrg    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     27      1.1  mrg    <http://www.gnu.org/licenses/>.  */
     28      1.1  mrg 
     29      1.1  mrg #define _GNU_SOURCE
     30      1.1  mrg #include "libgomp.h"
     31      1.1  mrg #include "oacc-int.h"
     32      1.1  mrg #include "secure_getenv.h"
     33      1.1  mrg #include "acc_prof.h"
     34      1.1  mrg #include <assert.h>
     35      1.1  mrg #ifdef HAVE_STRING_H
     36      1.1  mrg # include <string.h>
     37      1.1  mrg #endif
     38      1.1  mrg #ifdef PLUGIN_SUPPORT
     39      1.1  mrg # include <dlfcn.h>
     40      1.1  mrg #endif
     41      1.1  mrg 
     42      1.1  mrg #define STATIC_ASSERT(expr) _Static_assert (expr, "!(" #expr ")")
     43      1.1  mrg 
     44      1.1  mrg /* Statically assert that the layout of the common fields in the
     45      1.1  mrg    'acc_event_info' variants matches.  */
     46      1.1  mrg /* 'event_type' */
     47      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, event_type)
     48      1.1  mrg 	       == offsetof (acc_event_info, data_event.event_type));
     49      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.event_type)
     50      1.1  mrg 	       == offsetof (acc_event_info, launch_event.event_type));
     51      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.event_type)
     52      1.1  mrg 	       == offsetof (acc_event_info, other_event.event_type));
     53      1.1  mrg /* 'valid_bytes' */
     54      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.valid_bytes)
     55      1.1  mrg 	       == offsetof (acc_event_info, launch_event.valid_bytes));
     56      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.valid_bytes)
     57      1.1  mrg 	       == offsetof (acc_event_info, other_event.valid_bytes));
     58      1.1  mrg /* 'parent_construct' */
     59      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.parent_construct)
     60      1.1  mrg 	       == offsetof (acc_event_info, launch_event.parent_construct));
     61      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.parent_construct)
     62      1.1  mrg 	       == offsetof (acc_event_info, other_event.parent_construct));
     63      1.1  mrg /* 'implicit' */
     64      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.implicit)
     65      1.1  mrg 	       == offsetof (acc_event_info, launch_event.implicit));
     66      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.implicit)
     67      1.1  mrg 	       == offsetof (acc_event_info, other_event.implicit));
     68      1.1  mrg /* 'tool_info' */
     69      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.tool_info)
     70      1.1  mrg 	       == offsetof (acc_event_info, launch_event.tool_info));
     71      1.1  mrg STATIC_ASSERT (offsetof (acc_event_info, data_event.tool_info)
     72      1.1  mrg 	       == offsetof (acc_event_info, other_event.tool_info));
     73      1.1  mrg 
     74      1.1  mrg struct goacc_prof_callback_entry
     75      1.1  mrg {
     76      1.1  mrg   acc_prof_callback cb;
     77      1.1  mrg   int ref;
     78      1.1  mrg   bool enabled;
     79      1.1  mrg   struct goacc_prof_callback_entry *next;
     80      1.1  mrg };
     81      1.1  mrg 
     82      1.1  mrg /* Use a separate flag to minimize run-time performance impact for the (very
     83      1.1  mrg    common) case that profiling is not enabled.
     84      1.1  mrg 
     85      1.1  mrg    Once enabled, we're not going to disable this anymore, anywhere.  We
     86      1.1  mrg    probably could, by adding appropriate logic to 'acc_prof_register',
     87      1.1  mrg    'acc_prof_unregister'.  */
     88      1.1  mrg bool goacc_prof_enabled = false;
     89      1.1  mrg 
     90      1.1  mrg /* Global state for registered callbacks.
     91      1.1  mrg    'goacc_prof_callbacks_enabled[acc_ev_none]' acts as a global toggle.  */
     92      1.1  mrg static bool goacc_prof_callbacks_enabled[acc_ev_last];
     93      1.1  mrg static struct goacc_prof_callback_entry *goacc_prof_callback_entries[acc_ev_last];
     94      1.1  mrg /* Lock used to protect access to 'goacc_prof_callbacks_enabled', and
     95      1.1  mrg    'goacc_prof_callback_entries'.  */
     96      1.1  mrg static gomp_mutex_t goacc_prof_lock;
     97      1.1  mrg 
     98      1.1  mrg void
     99      1.1  mrg goacc_profiling_initialize (void)
    100      1.1  mrg {
    101      1.1  mrg   gomp_mutex_init (&goacc_prof_lock);
    102      1.1  mrg 
    103      1.1  mrg   /* Initially, all callbacks for all events are enabled.  */
    104      1.1  mrg   for (int i = 0; i < acc_ev_last; ++i)
    105      1.1  mrg     goacc_prof_callbacks_enabled[i] = true;
    106      1.1  mrg 
    107      1.1  mrg 
    108      1.1  mrg #ifdef PLUGIN_SUPPORT
    109      1.1  mrg   char *acc_proflibs = secure_getenv ("ACC_PROFLIB");
    110      1.1  mrg   while (acc_proflibs != NULL && acc_proflibs[0] != '\0')
    111      1.1  mrg     {
    112      1.1  mrg       char *acc_proflibs_sep = strchr (acc_proflibs, ';');
    113      1.1  mrg       char *acc_proflib;
    114      1.1  mrg       if (acc_proflibs_sep == acc_proflibs)
    115      1.1  mrg 	{
    116      1.1  mrg 	  /* Stray ';' separator: make sure we don't 'dlopen' the main
    117      1.1  mrg 	     program.  */
    118      1.1  mrg 	  acc_proflib = NULL;
    119      1.1  mrg 	}
    120      1.1  mrg       else
    121      1.1  mrg 	{
    122      1.1  mrg 	  if (acc_proflibs_sep != NULL)
    123      1.1  mrg 	    {
    124      1.1  mrg 	      /* Single out the first library.  */
    125      1.1  mrg 	      acc_proflib = gomp_malloc (acc_proflibs_sep - acc_proflibs + 1);
    126      1.1  mrg 	      memcpy (acc_proflib, acc_proflibs,
    127      1.1  mrg 		      acc_proflibs_sep - acc_proflibs);
    128      1.1  mrg 	      acc_proflib[acc_proflibs_sep - acc_proflibs] = '\0';
    129      1.1  mrg 	    }
    130      1.1  mrg 	  else
    131      1.1  mrg 	    {
    132      1.1  mrg 	      /* No ';' separator, so only one library.  */
    133      1.1  mrg 	      acc_proflib = acc_proflibs;
    134      1.1  mrg 	    }
    135      1.1  mrg 
    136      1.1  mrg 	  gomp_debug (0, "%s: dlopen (\"%s\")\n", __FUNCTION__, acc_proflib);
    137      1.1  mrg 	  void *dl_handle = dlopen (acc_proflib, RTLD_LAZY);
    138      1.1  mrg 	  if (dl_handle != NULL)
    139      1.1  mrg 	    {
    140      1.1  mrg 	      typeof (&acc_register_library) a_r_l
    141      1.1  mrg 		= dlsym (dl_handle, "acc_register_library");
    142      1.1  mrg 	      if (a_r_l == NULL)
    143      1.1  mrg 		goto dl_fail;
    144      1.1  mrg 	      gomp_debug (0, "  %s: calling %s:acc_register_library\n",
    145      1.1  mrg 			  __FUNCTION__, acc_proflib);
    146      1.1  mrg 	      a_r_l (acc_prof_register, acc_prof_unregister,
    147      1.1  mrg 		     acc_prof_lookup);
    148      1.1  mrg 	    }
    149      1.1  mrg 	  else
    150      1.1  mrg 	    {
    151      1.1  mrg 	    dl_fail:
    152      1.1  mrg 	      gomp_error ("while loading ACC_PROFLIB \"%s\": %s",
    153      1.1  mrg 			  acc_proflib, dlerror ());
    154      1.1  mrg 	      if (dl_handle != NULL)
    155      1.1  mrg 		{
    156      1.1  mrg 		  int err = dlclose (dl_handle);
    157      1.1  mrg 		  dl_handle = NULL;
    158      1.1  mrg 		  if (err != 0)
    159      1.1  mrg 		    goto dl_fail;
    160      1.1  mrg 		}
    161      1.1  mrg 	    }
    162      1.1  mrg 	}
    163      1.1  mrg 
    164      1.1  mrg       if (acc_proflib != acc_proflibs)
    165      1.1  mrg 	{
    166      1.1  mrg 	  free (acc_proflib);
    167      1.1  mrg 
    168      1.1  mrg 	  acc_proflibs = acc_proflibs_sep + 1;
    169      1.1  mrg 	}
    170      1.1  mrg       else
    171      1.1  mrg 	acc_proflibs = NULL;
    172      1.1  mrg     }
    173      1.1  mrg #endif /* PLUGIN_SUPPORT */
    174      1.1  mrg }
    175      1.1  mrg 
    176      1.1  mrg void
    177      1.1  mrg acc_prof_register (acc_event_t ev, acc_prof_callback cb, acc_register_t reg)
    178      1.1  mrg {
    179      1.1  mrg   gomp_debug (0, "%s: ev=%d, cb=%p, reg=%d\n",
    180      1.1  mrg 	      __FUNCTION__, (int) ev, (void *) cb, (int) reg);
    181      1.1  mrg 
    182      1.1  mrg 
    183      1.1  mrg   /* For any events to be dispatched, the user first has to register a
    184      1.1  mrg      callback, which makes this here a good place for enabling the whole
    185      1.1  mrg      machinery.  */
    186      1.1  mrg   if (!GOACC_PROF_ENABLED)
    187      1.1  mrg     __atomic_store_n (&goacc_prof_enabled, true, MEMMODEL_RELEASE);
    188      1.1  mrg 
    189      1.1  mrg 
    190      1.1  mrg   enum
    191      1.1  mrg   {
    192      1.1  mrg     EVENT_KIND_BOGUS,
    193      1.1  mrg     EVENT_KIND_NORMAL,
    194      1.1  mrg     /* As end events invoke callbacks in the reverse order, we register these
    195      1.1  mrg        in the reverse order here.  */
    196      1.1  mrg     EVENT_KIND_END,
    197      1.1  mrg   } event_kind = EVENT_KIND_BOGUS;
    198      1.1  mrg   switch (ev)
    199      1.1  mrg     {
    200      1.1  mrg     case acc_ev_none:
    201      1.1  mrg     case acc_ev_device_init_start:
    202      1.1  mrg     case acc_ev_device_shutdown_start:
    203      1.1  mrg     case acc_ev_runtime_shutdown:
    204      1.1  mrg     case acc_ev_create:
    205      1.1  mrg     case acc_ev_delete:
    206      1.1  mrg     case acc_ev_alloc:
    207      1.1  mrg     case acc_ev_free:
    208      1.1  mrg     case acc_ev_enter_data_start:
    209      1.1  mrg     case acc_ev_exit_data_start:
    210      1.1  mrg     case acc_ev_update_start:
    211      1.1  mrg     case acc_ev_compute_construct_start:
    212      1.1  mrg     case acc_ev_enqueue_launch_start:
    213      1.1  mrg     case acc_ev_enqueue_upload_start:
    214      1.1  mrg     case acc_ev_enqueue_download_start:
    215      1.1  mrg     case acc_ev_wait_start:
    216      1.1  mrg       event_kind = EVENT_KIND_NORMAL;
    217      1.1  mrg       break;
    218      1.1  mrg     case acc_ev_device_init_end:
    219      1.1  mrg     case acc_ev_device_shutdown_end:
    220      1.1  mrg     case acc_ev_enter_data_end:
    221      1.1  mrg     case acc_ev_exit_data_end:
    222      1.1  mrg     case acc_ev_update_end:
    223      1.1  mrg     case acc_ev_compute_construct_end:
    224      1.1  mrg     case acc_ev_enqueue_launch_end:
    225      1.1  mrg     case acc_ev_enqueue_upload_end:
    226      1.1  mrg     case acc_ev_enqueue_download_end:
    227      1.1  mrg     case acc_ev_wait_end:
    228      1.1  mrg       event_kind = EVENT_KIND_END;
    229      1.1  mrg       break;
    230      1.1  mrg     case acc_ev_last:
    231      1.1  mrg       break;
    232      1.1  mrg     }
    233      1.1  mrg   if (event_kind == EVENT_KIND_BOGUS)
    234      1.1  mrg     {
    235      1.1  mrg       /* Silently ignore.  */
    236      1.1  mrg       gomp_debug (0, "  ignoring request for bogus 'acc_event_t'\n");
    237      1.1  mrg       return;
    238      1.1  mrg     }
    239      1.1  mrg 
    240      1.1  mrg   bool bogus = true;
    241      1.1  mrg   switch (reg)
    242      1.1  mrg     {
    243      1.1  mrg     case acc_reg:
    244      1.1  mrg     case acc_toggle:
    245      1.1  mrg     case acc_toggle_per_thread:
    246      1.1  mrg       bogus = false;
    247      1.1  mrg       break;
    248      1.1  mrg     }
    249      1.1  mrg   if (bogus)
    250      1.1  mrg     {
    251      1.1  mrg       /* Silently ignore.  */
    252      1.1  mrg       gomp_debug (0, "  ignoring request with bogus 'acc_register_t'\n");
    253      1.1  mrg       return;
    254      1.1  mrg     }
    255      1.1  mrg 
    256      1.1  mrg   /* Special cases.  */
    257      1.1  mrg   if (reg == acc_toggle)
    258      1.1  mrg     {
    259      1.1  mrg       if (cb == NULL)
    260      1.1  mrg 	{
    261      1.1  mrg 	  gomp_debug (0, "  globally enabling callbacks\n");
    262      1.1  mrg 	  gomp_mutex_lock (&goacc_prof_lock);
    263      1.1  mrg 	  /* For 'acc_ev_none', this acts as a global toggle.  */
    264      1.1  mrg 	  goacc_prof_callbacks_enabled[ev] = true;
    265      1.1  mrg 	  gomp_mutex_unlock (&goacc_prof_lock);
    266      1.1  mrg 	  return;
    267      1.1  mrg 	}
    268      1.1  mrg       else if (ev == acc_ev_none && cb != NULL)
    269      1.1  mrg 	{
    270      1.1  mrg 	  gomp_debug (0, "  ignoring request\n");
    271      1.1  mrg 	  return;
    272      1.1  mrg 	}
    273      1.1  mrg     }
    274      1.1  mrg   else if (reg == acc_toggle_per_thread)
    275      1.1  mrg     {
    276      1.1  mrg       if (ev == acc_ev_none && cb == NULL)
    277      1.1  mrg 	{
    278      1.1  mrg 	  gomp_debug (0, "  thread: enabling callbacks\n");
    279      1.1  mrg 	  goacc_lazy_initialize ();
    280      1.1  mrg 	  struct goacc_thread *thr = goacc_thread ();
    281      1.1  mrg 	  thr->prof_callbacks_enabled = true;
    282      1.1  mrg 	  return;
    283      1.1  mrg 	}
    284      1.1  mrg       /* Silently ignore.  */
    285      1.1  mrg       gomp_debug (0, "  ignoring bogus request\n");
    286      1.1  mrg       return;
    287      1.1  mrg     }
    288      1.1  mrg 
    289      1.1  mrg   gomp_mutex_lock (&goacc_prof_lock);
    290      1.1  mrg 
    291      1.1  mrg   struct goacc_prof_callback_entry *it, *it_p;
    292      1.1  mrg   it = goacc_prof_callback_entries[ev];
    293      1.1  mrg   it_p = NULL;
    294      1.1  mrg   while (it)
    295      1.1  mrg     {
    296      1.1  mrg       if (it->cb == cb)
    297      1.1  mrg 	break;
    298      1.1  mrg       it_p = it;
    299      1.1  mrg       it = it->next;
    300      1.1  mrg     }
    301      1.1  mrg 
    302      1.1  mrg   switch (reg)
    303      1.1  mrg     {
    304      1.1  mrg     case acc_reg:
    305      1.1  mrg       /* If we already have this callback registered, just increment its
    306      1.1  mrg 	 reference count.  */
    307      1.1  mrg       if (it != NULL)
    308      1.1  mrg 	{
    309      1.1  mrg 	  it->ref++;
    310      1.1  mrg 	  gomp_debug (0, "  already registered;"
    311      1.1  mrg 		      " incrementing reference count to: %d\n", it->ref);
    312      1.1  mrg 	}
    313      1.1  mrg       else
    314      1.1  mrg 	{
    315      1.1  mrg 	  struct goacc_prof_callback_entry *e
    316      1.1  mrg 	    = gomp_malloc (sizeof (struct goacc_prof_callback_entry));
    317      1.1  mrg 	  e->cb = cb;
    318      1.1  mrg 	  e->ref = 1;
    319      1.1  mrg 	  e->enabled = true;
    320      1.1  mrg 	  bool prepend = (event_kind == EVENT_KIND_END);
    321      1.1  mrg 	  /* If we don't have any callback registered yet, also use the
    322      1.1  mrg 	     'prepend' code path.  */
    323      1.1  mrg 	  if (it_p == NULL)
    324      1.1  mrg 	    prepend = true;
    325      1.1  mrg 	  if (prepend)
    326      1.1  mrg 	    {
    327      1.1  mrg 	      gomp_debug (0, "  prepending\n");
    328      1.1  mrg 	      e->next = goacc_prof_callback_entries[ev];
    329      1.1  mrg 	      goacc_prof_callback_entries[ev] = e;
    330      1.1  mrg 	    }
    331      1.1  mrg 	  else
    332      1.1  mrg 	    {
    333      1.1  mrg 	      gomp_debug (0, "  appending\n");
    334      1.1  mrg 	      e->next = NULL;
    335      1.1  mrg 	      it_p->next = e;
    336      1.1  mrg 	    }
    337      1.1  mrg 	}
    338      1.1  mrg       break;
    339      1.1  mrg 
    340      1.1  mrg     case acc_toggle:
    341      1.1  mrg       if (it == NULL)
    342      1.1  mrg 	{
    343      1.1  mrg 	  gomp_debug (0, "  ignoring request: is not registered\n");
    344      1.1  mrg 	  break;
    345      1.1  mrg 	}
    346      1.1  mrg       else
    347      1.1  mrg 	{
    348      1.1  mrg 	  gomp_debug (0, "  enabling\n");
    349      1.1  mrg 	  it->enabled = true;
    350      1.1  mrg 	}
    351      1.1  mrg       break;
    352      1.1  mrg 
    353      1.1  mrg     case acc_toggle_per_thread:
    354      1.1  mrg       __builtin_unreachable ();
    355      1.1  mrg     }
    356      1.1  mrg 
    357      1.1  mrg   gomp_mutex_unlock (&goacc_prof_lock);
    358      1.1  mrg }
    359      1.1  mrg 
    360      1.1  mrg void
    361      1.1  mrg acc_prof_unregister (acc_event_t ev, acc_prof_callback cb, acc_register_t reg)
    362      1.1  mrg {
    363      1.1  mrg   gomp_debug (0, "%s: ev=%d, cb=%p, reg=%d\n",
    364      1.1  mrg 	      __FUNCTION__, (int) ev, (void *) cb, (int) reg);
    365      1.1  mrg 
    366      1.1  mrg   /* If profiling is not enabled, there cannot be anything to unregister.  */
    367      1.1  mrg   if (!GOACC_PROF_ENABLED)
    368      1.1  mrg     return;
    369      1.1  mrg 
    370      1.1  mrg   if (ev < acc_ev_none
    371      1.1  mrg       || ev >= acc_ev_last)
    372      1.1  mrg     {
    373      1.1  mrg       /* Silently ignore.  */
    374      1.1  mrg       gomp_debug (0, "  ignoring request for bogus 'acc_event_t'\n");
    375      1.1  mrg       return;
    376      1.1  mrg     }
    377      1.1  mrg 
    378      1.1  mrg   bool bogus = true;
    379      1.1  mrg   switch (reg)
    380      1.1  mrg     {
    381      1.1  mrg     case acc_reg:
    382      1.1  mrg     case acc_toggle:
    383      1.1  mrg     case acc_toggle_per_thread:
    384      1.1  mrg       bogus = false;
    385      1.1  mrg       break;
    386      1.1  mrg     }
    387      1.1  mrg   if (bogus)
    388      1.1  mrg     {
    389      1.1  mrg       /* Silently ignore.  */
    390      1.1  mrg       gomp_debug (0, "  ignoring request with bogus 'acc_register_t'\n");
    391      1.1  mrg       return;
    392      1.1  mrg     }
    393      1.1  mrg 
    394      1.1  mrg   /* Special cases.  */
    395      1.1  mrg   if (reg == acc_toggle)
    396      1.1  mrg     {
    397      1.1  mrg       if (cb == NULL)
    398      1.1  mrg 	{
    399      1.1  mrg 	  gomp_debug (0, "  globally disabling callbacks\n");
    400      1.1  mrg 	  gomp_mutex_lock (&goacc_prof_lock);
    401      1.1  mrg 	  /* For 'acc_ev_none', this acts as a global toggle.  */
    402      1.1  mrg 	  goacc_prof_callbacks_enabled[ev] = false;
    403      1.1  mrg 	  gomp_mutex_unlock (&goacc_prof_lock);
    404      1.1  mrg 	  return;
    405      1.1  mrg 	}
    406      1.1  mrg       else if (ev == acc_ev_none && cb != NULL)
    407      1.1  mrg 	{
    408      1.1  mrg 	  gomp_debug (0, "  ignoring request\n");
    409      1.1  mrg 	  return;
    410      1.1  mrg 	}
    411      1.1  mrg     }
    412      1.1  mrg   else if (reg == acc_toggle_per_thread)
    413      1.1  mrg     {
    414      1.1  mrg       if (ev == acc_ev_none && cb == NULL)
    415      1.1  mrg 	{
    416      1.1  mrg 	  gomp_debug (0, "  thread: disabling callbacks\n");
    417      1.1  mrg 	  goacc_lazy_initialize ();
    418      1.1  mrg 	  struct goacc_thread *thr = goacc_thread ();
    419      1.1  mrg 	  thr->prof_callbacks_enabled = false;
    420      1.1  mrg 	  return;
    421      1.1  mrg 	}
    422      1.1  mrg       /* Silently ignore.  */
    423      1.1  mrg       gomp_debug (0, "  ignoring bogus request\n");
    424      1.1  mrg       return;
    425      1.1  mrg     }
    426      1.1  mrg 
    427      1.1  mrg   gomp_mutex_lock (&goacc_prof_lock);
    428      1.1  mrg 
    429      1.1  mrg   struct goacc_prof_callback_entry *it, *it_p;
    430      1.1  mrg   it = goacc_prof_callback_entries[ev];
    431      1.1  mrg   it_p = NULL;
    432      1.1  mrg   while (it)
    433      1.1  mrg     {
    434      1.1  mrg       if (it->cb == cb)
    435      1.1  mrg 	break;
    436      1.1  mrg       it_p = it;
    437      1.1  mrg       it = it->next;
    438      1.1  mrg     }
    439      1.1  mrg 
    440      1.1  mrg   switch (reg)
    441      1.1  mrg     {
    442      1.1  mrg     case acc_reg:
    443      1.1  mrg       if (it == NULL)
    444      1.1  mrg 	{
    445      1.1  mrg 	  /* Silently ignore.  */
    446      1.1  mrg 	  gomp_debug (0, "  ignoring bogus request: is not registered\n");
    447      1.1  mrg 	  break;
    448      1.1  mrg 	}
    449      1.1  mrg       it->ref--;
    450      1.1  mrg       gomp_debug (0, "  decrementing reference count to: %d\n", it->ref);
    451      1.1  mrg       if (it->ref == 0)
    452      1.1  mrg 	{
    453      1.1  mrg 	  if (it_p == NULL)
    454      1.1  mrg 	    goacc_prof_callback_entries[ev] = it->next;
    455      1.1  mrg 	  else
    456      1.1  mrg 	    it_p->next = it->next;
    457      1.1  mrg 	  free (it);
    458      1.1  mrg 	}
    459      1.1  mrg       break;
    460      1.1  mrg 
    461      1.1  mrg     case acc_toggle:
    462      1.1  mrg       if (it == NULL)
    463      1.1  mrg 	{
    464      1.1  mrg 	  gomp_debug (0, "  ignoring request: is not registered\n");
    465      1.1  mrg 	  break;
    466      1.1  mrg 	}
    467      1.1  mrg       else
    468      1.1  mrg 	{
    469      1.1  mrg 	  gomp_debug (0, "  disabling\n");
    470      1.1  mrg 	  it->enabled = false;
    471      1.1  mrg 	}
    472      1.1  mrg       break;
    473      1.1  mrg 
    474      1.1  mrg     case acc_toggle_per_thread:
    475      1.1  mrg       __builtin_unreachable ();
    476      1.1  mrg     }
    477      1.1  mrg 
    478      1.1  mrg   gomp_mutex_unlock (&goacc_prof_lock);
    479      1.1  mrg }
    480      1.1  mrg 
    481      1.1  mrg acc_query_fn
    482      1.1  mrg acc_prof_lookup (const char *name)
    483      1.1  mrg {
    484      1.1  mrg   gomp_debug (0, "%s (%s)\n",
    485      1.1  mrg 	      __FUNCTION__, name ?: "NULL");
    486      1.1  mrg 
    487      1.1  mrg   return NULL;
    488      1.1  mrg }
    489      1.1  mrg 
    490      1.1  mrg void
    491      1.1  mrg acc_register_library (acc_prof_reg reg, acc_prof_reg unreg,
    492      1.1  mrg 		      acc_prof_lookup_func lookup)
    493      1.1  mrg {
    494      1.1  mrg   gomp_fatal ("TODO");
    495      1.1  mrg }
    496      1.1  mrg 
    497      1.1  mrg /* Prepare to dispatch events?  */
    498      1.1  mrg 
    499      1.1  mrg bool
    500      1.1  mrg _goacc_profiling_dispatch_p (bool check_not_nested_p)
    501      1.1  mrg {
    502      1.1  mrg   gomp_debug (0, "%s\n", __FUNCTION__);
    503      1.1  mrg 
    504      1.1  mrg   bool ret;
    505      1.1  mrg 
    506      1.1  mrg   struct goacc_thread *thr = goacc_thread ();
    507      1.1  mrg   if (__builtin_expect (thr == NULL, false))
    508      1.1  mrg     {
    509      1.1  mrg       /* If we don't have any per-thread state yet, that means that per-thread
    510      1.1  mrg 	 callback dispatch has not been explicitly disabled (which only a call
    511      1.1  mrg 	 to 'acc_prof_unregister' with 'acc_toggle_per_thread' would do, and
    512      1.1  mrg 	 that would have allocated per-thread state via
    513      1.1  mrg 	 'goacc_lazy_initialize'); initially, all callbacks for all events are
    514      1.1  mrg 	 enabled.  */
    515      1.1  mrg       gomp_debug (0, "  %s: don't have any per-thread state yet\n", __FUNCTION__);
    516      1.1  mrg     }
    517      1.1  mrg   else
    518      1.1  mrg     {
    519      1.1  mrg       if (check_not_nested_p)
    520      1.1  mrg 	{
    521      1.1  mrg 	  /* No nesting.  */
    522      1.1  mrg 	  assert (thr->prof_info == NULL);
    523      1.1  mrg 	  assert (thr->api_info == NULL);
    524      1.1  mrg 	}
    525      1.1  mrg 
    526      1.1  mrg       if (__builtin_expect (!thr->prof_callbacks_enabled, true))
    527      1.1  mrg 	{
    528      1.1  mrg 	  gomp_debug (0, "  %s: disabled for this thread\n", __FUNCTION__);
    529      1.1  mrg 	  ret = false;
    530      1.1  mrg 	  goto out;
    531      1.1  mrg 	}
    532      1.1  mrg     }
    533      1.1  mrg 
    534      1.1  mrg   gomp_mutex_lock (&goacc_prof_lock);
    535      1.1  mrg 
    536      1.1  mrg   /* 'goacc_prof_callbacks_enabled[acc_ev_none]' acts as a global toggle.  */
    537      1.1  mrg   if (__builtin_expect (!goacc_prof_callbacks_enabled[acc_ev_none], true))
    538      1.1  mrg     {
    539      1.1  mrg       gomp_debug (0, "  %s: disabled globally\n", __FUNCTION__);
    540      1.1  mrg       ret = false;
    541      1.1  mrg       goto out_unlock;
    542      1.1  mrg     }
    543      1.1  mrg   else
    544      1.1  mrg     ret = true;
    545      1.1  mrg 
    546      1.1  mrg  out_unlock:
    547      1.1  mrg   gomp_mutex_unlock (&goacc_prof_lock);
    548      1.1  mrg 
    549      1.1  mrg  out:
    550      1.1  mrg   return ret;
    551      1.1  mrg }
    552      1.1  mrg 
    553      1.1  mrg /* Set up to dispatch events?  */
    554      1.1  mrg 
    555      1.1  mrg bool
    556      1.1  mrg _goacc_profiling_setup_p (struct goacc_thread *thr,
    557      1.1  mrg 			  acc_prof_info *prof_info, acc_api_info *api_info)
    558      1.1  mrg {
    559      1.1  mrg   gomp_debug (0, "%s (%p)\n", __FUNCTION__, thr);
    560      1.1  mrg 
    561      1.1  mrg   /* If we don't have any per-thread state yet, we can't register 'prof_info'
    562      1.1  mrg      and 'api_info'.  */
    563      1.1  mrg   if (__builtin_expect (thr == NULL, false))
    564      1.1  mrg     {
    565      1.1  mrg       gomp_debug (0, "Can't dispatch OpenACC Profiling Interface events for"
    566      1.1  mrg 		  " the current call, construct, or directive\n");
    567      1.1  mrg       return false;
    568      1.1  mrg     }
    569      1.1  mrg 
    570      1.1  mrg   if (thr->prof_info != NULL)
    571      1.1  mrg     {
    572      1.1  mrg       /* Profiling has already been set up for an outer construct.  In this
    573      1.1  mrg 	 case, we continue to use the existing information, and thus return
    574      1.1  mrg 	 'false' here.
    575      1.1  mrg 
    576      1.1  mrg 	 This can happen, for example, for an 'enter data' directive, which
    577      1.1  mrg 	 sets up profiling, then calls into 'acc_copyin', which should not
    578      1.1  mrg 	 again set up profiling, should not overwrite the existing
    579      1.1  mrg 	 information.  */
    580      1.1  mrg       return false;
    581      1.1  mrg     }
    582      1.1  mrg 
    583      1.1  mrg   thr->prof_info = prof_info;
    584      1.1  mrg   thr->api_info = api_info;
    585      1.1  mrg 
    586      1.1  mrg   /* Fill in some defaults.  */
    587      1.1  mrg 
    588      1.1  mrg   prof_info->event_type = -1; /* Must be set later.  */
    589      1.1  mrg   prof_info->valid_bytes = _ACC_PROF_INFO_VALID_BYTES;
    590      1.1  mrg   prof_info->version = _ACC_PROF_INFO_VERSION;
    591      1.1  mrg   if (thr->dev)
    592      1.1  mrg     {
    593      1.1  mrg       prof_info->device_type = acc_device_type (thr->dev->type);
    594      1.1  mrg       prof_info->device_number = thr->dev->target_id;
    595      1.1  mrg     }
    596      1.1  mrg   else
    597      1.1  mrg     {
    598      1.1  mrg       prof_info->device_type = -1;
    599      1.1  mrg       prof_info->device_number = -1;
    600      1.1  mrg     }
    601      1.1  mrg   prof_info->thread_id = -1;
    602      1.1  mrg   prof_info->async = acc_async_sync;
    603      1.1  mrg   prof_info->async_queue = prof_info->async;
    604      1.1  mrg   prof_info->src_file = NULL;
    605      1.1  mrg   prof_info->func_name = NULL;
    606      1.1  mrg   prof_info->line_no = -1;
    607      1.1  mrg   prof_info->end_line_no = -1;
    608      1.1  mrg   prof_info->func_line_no = -1;
    609      1.1  mrg   prof_info->func_end_line_no = -1;
    610      1.1  mrg 
    611      1.1  mrg   api_info->device_api = acc_device_api_none;
    612      1.1  mrg   api_info->valid_bytes = _ACC_API_INFO_VALID_BYTES;
    613      1.1  mrg   api_info->device_type = prof_info->device_type;
    614      1.1  mrg   api_info->vendor = -1;
    615      1.1  mrg   api_info->device_handle = NULL;
    616      1.1  mrg   api_info->context_handle = NULL;
    617      1.1  mrg   api_info->async_handle = NULL;
    618      1.1  mrg 
    619      1.1  mrg   return true;
    620      1.1  mrg }
    621      1.1  mrg 
    622      1.1  mrg /* Dispatch events.
    623      1.1  mrg 
    624      1.1  mrg    This must only be called if 'GOACC_PROFILING_DISPATCH_P' or
    625      1.1  mrg    'GOACC_PROFILING_SETUP_P' returned a true result.  */
    626      1.1  mrg 
    627      1.1  mrg void
    628      1.1  mrg goacc_profiling_dispatch (acc_prof_info *prof_info, acc_event_info *event_info,
    629      1.1  mrg 			  acc_api_info *apt_info)
    630      1.1  mrg {
    631      1.1  mrg   acc_event_t event_type = event_info->event_type;
    632      1.1  mrg   gomp_debug (0, "%s: event_type=%d\n", __FUNCTION__, (int) event_type);
    633      1.1  mrg   assert (event_type > acc_ev_none
    634      1.1  mrg 	  && event_type < acc_ev_last);
    635      1.1  mrg 
    636      1.1  mrg   gomp_mutex_lock (&goacc_prof_lock);
    637      1.1  mrg 
    638      1.1  mrg   if (!goacc_prof_callbacks_enabled[event_type])
    639      1.1  mrg     {
    640      1.1  mrg       gomp_debug (0, "  disabled for this event type\n");
    641      1.1  mrg 
    642      1.1  mrg       goto out_unlock;
    643      1.1  mrg     }
    644      1.1  mrg 
    645      1.1  mrg   for (struct goacc_prof_callback_entry *e
    646      1.1  mrg 	 = goacc_prof_callback_entries[event_type];
    647      1.1  mrg        e != NULL;
    648      1.1  mrg        e = e->next)
    649      1.1  mrg     {
    650      1.1  mrg       if (!e->enabled)
    651      1.1  mrg 	{
    652      1.1  mrg 	  gomp_debug (0, "  disabled for callback %p\n", e->cb);
    653      1.1  mrg 	  continue;
    654      1.1  mrg 	}
    655      1.1  mrg 
    656      1.1  mrg       gomp_debug (0, "  calling callback %p\n", e->cb);
    657      1.1  mrg       e->cb (prof_info, event_info, apt_info);
    658      1.1  mrg     }
    659      1.1  mrg 
    660      1.1  mrg  out_unlock:
    661      1.1  mrg   gomp_mutex_unlock (&goacc_prof_lock);
    662      1.1  mrg }
    663