# vim: set ft=c:

MPI_T_category_changed:
    .desc: Get the timestamp indicating the last change to the categories
/*
    Notes:
    If two subsequent calls to this routine return the same timestamp, it is guaranteed that
    the category information has not changed between the two calls. If the timestamp retrieved
    from the second call is higher, then some categories have been added or expanded.
*/
{
    *update_number = cat_stamp;
}

MPI_T_category_get_categories:
    .desc: Get sub-categories in a category
{ -- error_check -- indices
    if (len != 0) {
        MPIT_ERRTEST_ARGNULL(indices);
    }
}
{ -- early_return --
    if (len == 0) {
        goto fn_exit;
    }
}

MPI_T_category_get_cvars:
    .desc: Get control variables in a category
{ -- error_check -- indices
    if (len != 0) {
        MPIT_ERRTEST_ARGNULL(indices);
    }
}
{ -- early_return --
    if (len == 0) {
        goto fn_exit;
    }
}

MPI_T_category_get_index:
    .desc: Get the index of a category
    .error: MPI_T_ERR_INVALID_NAME
{
    name2index_hash_t *hash_entry;

    /* Do hash lookup by the name */
    HASH_FIND_STR(cat_hash, name, hash_entry);
    if (hash_entry != NULL) {
        *cat_index = hash_entry->idx;
    } else {
        mpi_errno = MPI_T_ERR_INVALID_NAME;
        goto fn_fail;
    }
}

MPI_T_category_get_info:
    .desc: Get the information about a category
    .skip: validate-ANY
    .error: MPI_T_ERR_INVALID_INDEX
{ -- error_check --
    MPIT_ERRTEST_CAT_INDEX(cat_index);
}
{
    cat_table_entry_t *cat;
    cat = (cat_table_entry_t *) utarray_eltptr(cat_table, cat_index);
    MPIR_T_strncpy(name, cat->name, name_len);
    MPIR_T_strncpy(desc, cat->desc, desc_len);

    if (num_cvars != NULL)
        *num_cvars = utarray_len(cat->cvar_indices);

    if (num_pvars != NULL)
        *num_pvars = utarray_len(cat->pvar_indices);

    if (num_categories != NULL)
        *num_categories = utarray_len(cat->subcat_indices);
}

MPI_T_category_get_num:
    .desc: Get the number of categories
{
    *num_cat = utarray_len(cat_table);
}

MPI_T_category_get_pvars:
    .desc: Get performance variables in a category
    .error: MPI_T_ERR_INVALID
{ -- error_check -- indices
    if (len != 0) {
        MPIT_ERRTEST_ARGNULL(indices);
    }
}
{ -- early_return --
    if (len == 0) {
        goto fn_exit;
    }
}

MPI_T_cvar_get_index:
    .desc: Get the index of a control variable
    .error: MPI_T_ERR_INVALID_NAME
{
    name2index_hash_t *hash_entry;

    /* Do hash lookup by the name */
    HASH_FIND_STR(cvar_hash, name, hash_entry);
    if (hash_entry != NULL) {
        *cvar_index = hash_entry->idx;
    } else {
        mpi_errno = MPI_T_ERR_INVALID_NAME;
        goto fn_fail;
    }
}

MPI_T_cvar_get_info:
    .desc: Get the information about a control variable
    .skip: validate-ANY
    .error: MPI_T_ERR_INVALID_INDEX
{ -- error_check --
    MPIT_ERRTEST_CVAR_INDEX(cvar_index);
}
{
    cvar_table_entry_t *cvar;
    cvar = (cvar_table_entry_t *) utarray_eltptr(cvar_table, cvar_index);

    MPIR_T_strncpy(name, cvar->name, name_len);
    MPIR_T_strncpy(desc, cvar->desc, desc_len);

    if (verbosity != NULL)
        *verbosity = cvar->verbosity;

    if (datatype != NULL)
        *datatype = cvar->datatype;

    if (enumtype != NULL)
        *enumtype = cvar->enumtype;

    if (bind!= NULL)
        *bind= cvar->bind;

    if (scope != NULL)
        *scope = cvar->scope;
}

MPI_T_cvar_get_num:
    .desc: Get the number of control variables
{
    *num_cvar = utarray_len(cvar_table);
}

MPI_T_cvar_handle_alloc:
    .desc: Allocate a handle for a control variable
    .skip: validate-TOOL_MPI_OBJ
    .error: MPI_T_ERR_OUT_OF_HANDLES

MPI_T_cvar_handle_free:
    .desc: Free an existing handle for a control variable
{
    MPIR_T_cvar_handle_t *hnd;
    hnd = *handle;
    MPL_free(hnd);
    *handle = MPI_T_CVAR_HANDLE_NULL;
}

MPI_T_cvar_read:
    .desc: Read the value of a control variable

MPI_T_cvar_write:
    .desc: Write a control variable
    .error: MPI_T_ERR_CVAR_SET_NEVER, MPI_T_ERR_CVAR_SET_NOT_NOW
{ -- error_check --
    MPIR_T_cvar_handle_t *hnd = handle;

    if (hnd->scope == MPI_T_SCOPE_CONSTANT) {
        mpi_errno = MPI_T_ERR_CVAR_SET_NEVER;
        goto fn_fail;
    } else if (hnd->scope == MPI_T_SCOPE_READONLY) {
        mpi_errno = MPI_T_ERR_CVAR_SET_NOT_NOW;
        goto fn_fail;
    }
}

MPI_T_enum_get_info:
    .desc: Get the information about an enumeration
    .skip: validate-STRING, validate-STRING_LENGTH
{
    *num = utarray_len(enumtype->items);
    MPIR_T_strncpy(name, enumtype->name, name_len);
}

MPI_T_enum_get_item:
    .desc: Get the information about an item in an enumeration
{
    enum_item_t *item;
    item = (enum_item_t *) utarray_eltptr(enumtype->items, indx);
    *value = item->value;
    MPIR_T_strncpy(name, item->name, name_len);
}

MPI_T_finalize:
    .desc: Finalize the MPI tool information interface
    .skip: global_cs
    .seealso: MPI_T_init_thread
    .error: MPI_T_ERR_NOT_INITIALIZED
/*
    Notes:
    This routine may be called as often as the corresponding MPI_T_init_thread() routine
    up to the current point of execution. Calling it more times returns a corresponding
    error code. As long as the number of calls to MPI_T_finalize() is smaller than the
    number of calls to MPI_T_init_thread() up to the current point of execution, the MPI
    tool information interface remains initialized and calls to its routines are permissible.
    Further, additional calls to MPI_T_init_thread() after one or more calls to MPI_T_finalize()
    are permissible. Once MPI_T_finalize() is called the same number of times as the routine
    MPI_T_init_thread() up to the current point of execution, the MPI tool information
    interface is no longer initialized. The interface can be reinitialized by subsequent calls
    to MPI_T_init_thread().
    
    At the end of the program execution, unless MPI_Abort() is called, an application must
    have called MPI_T_init_thread() and MPI_T_finalize() an equal number of times.
*/
{
    --MPIR_T_init_balance;
    if (MPIR_T_init_balance < 0) {
        mpi_errno = MPI_T_ERR_NOT_INITIALIZED;
        goto fn_fail;
    }

    if (MPIR_T_init_balance == 0) {
        MPIR_T_THREAD_CS_FINALIZE();
        MPIR_T_env_finalize();
    }
}

MPI_T_init_thread:
    .desc: Initialize the MPI_T execution environment
    .skip: global_cs, initcheck
    .seealso: MPI_T_finalize
/*
    Notes:
      The valid values for the level of thread support are:
    + MPI_THREAD_SINGLE - Only one thread will execute.
    . MPI_THREAD_FUNNELED - The process may be multi-threaded, but only the main
      thread will make MPI_T calls (all MPI_T calls are funneled to the
      main thread).
    . MPI_THREAD_SERIALIZED - The process may be multi-threaded, and multiple
      threads may make MPI_T calls, but only one at a time: MPI_T calls are not
      made concurrently from two distinct threads (all MPI_T calls are serialized).
    - MPI_THREAD_MULTIPLE - Multiple threads may call MPI_T, with no restrictions.
*/
{ -- error_check -- required
    CHECKENUM: required, thread_level, MPI_THREAD_SINGLE MPI_THREAD_FUNNELED MPI_THREAD_SERIALIZED MPI_THREAD_MULTIPLE
}
{
#if defined MPICH_IS_THREADED
    MPIR_T_is_threaded = (required == MPI_THREAD_MULTIPLE);
#endif /* MPICH_IS_THREADED */

    if (provided != NULL) {
        /* This must be min(required,MPICH_THREAD_LEVEL) if runtime
         * control of thread level is available */
        *provided = (MPICH_THREAD_LEVEL < required) ? MPICH_THREAD_LEVEL : required;
    }

    ++MPIR_T_init_balance;
    if (MPIR_T_init_balance == 1) {
        MPIR_T_THREAD_CS_INIT();
        mpi_errno = MPIR_T_env_init();
        if (mpi_errno) {
            goto fn_fail;
        }
    }
}

MPI_T_pvar_get_index:
    .desc: Get the index of a performance variable
    .error: MPI_T_ERR_INVALID_NAME
{
    int seq = var_class - MPIR_T_PVAR_CLASS_FIRST;
    name2index_hash_t *hash_entry;

    /* Do hash lookup by the name */
    HASH_FIND_STR(pvar_hashs[seq], name, hash_entry);
    if (hash_entry != NULL) {
        *pvar_index = hash_entry->idx;
    } else {
        mpi_errno = MPI_T_ERR_INVALID_NAME;
        goto fn_fail;
    }
}

MPI_T_pvar_get_info:
    .desc: Get the information about a performance variable
    .skip: validate-ANY
    .error: MPI_T_ERR_INVALID_INDEX
{ -- error_check --
    MPIT_ERRTEST_CVAR_INDEX(pvar_index);
}
{
    pvar_table_entry_t *entry;
    entry = (pvar_table_entry_t *) utarray_eltptr(pvar_table, pvar_index);
    if (!entry->active) {
        mpi_errno = MPI_T_ERR_INVALID_INDEX;
        goto fn_fail;
    }

    pvar_table_entry_t *info;
    info = (pvar_table_entry_t *) utarray_eltptr(pvar_table, pvar_index);

    MPIR_T_strncpy(name, info->name, name_len);
    MPIR_T_strncpy(desc, info->desc, desc_len);

    if (verbosity != NULL)
        *verbosity = info->verbosity;

    if (var_class != NULL)
        *var_class = info->varclass;

    if (datatype != NULL)
        *datatype = info->datatype;

    if (enumtype != NULL)
        *enumtype = info->enumtype;

    if (bind != NULL)
        *bind = info->bind;

    if (readonly != NULL)
        *readonly = info->flags & MPIR_T_PVAR_FLAG_READONLY;

    if (continuous != NULL)
        *continuous = info->flags & MPIR_T_PVAR_FLAG_CONTINUOUS;

    if (atomic != NULL)
        *atomic = info->flags & MPIR_T_PVAR_FLAG_ATOMIC;
}

MPI_T_pvar_get_num:
    .desc: Get the number of performance variables
{
    *num_pvar = utarray_len(pvar_table);
}

MPI_T_pvar_handle_alloc:
    .desc: Allocate a handle for a performance variable
    .skip: validate-TOOL_MPI_OBJ
    .error: MPI_T_ERR_INVALID_INDEX, MPI_T_ERR_OUT_OF_HANDLES
{ -- error_check -- pvar_index
    pvar_table_entry_t *entry;
    entry = (pvar_table_entry_t *) utarray_eltptr(pvar_table, pvar_index);
    if (!entry->active) {
        mpi_errno = MPI_T_ERR_INVALID_INDEX;
        goto fn_fail;
    }
}

MPI_T_pvar_handle_free:
    .desc: Free an existing handle for a performance variable

MPI_T_pvar_read:
    .desc: Read the value of a performance variable
/*
    Notes:
    The MPI_T_pvar_read() call queries the value of the performance variable with the
    handle "handle" in the session identified by the parameter session and stores the result
    in the buffer identified by the parameter buf. The user is responsible to ensure that the
    buffer is of the appropriate size to hold the entire value of the performance variable
    (based on the datatype and count returned by the corresponding previous calls to
    MPI_T_pvar_get_info() and MPI_T_pvar_handle_alloc(), respectively).
    
    The constant MPI_T_PVAR_ALL_HANDLES cannot be used as an argument for the function
    MPI_T_pvar_read().
*/

MPI_T_pvar_readreset:
    .desc: Read the value of a performance variable and then reset it
    .error: MPI_T_ERR_INVALID_HANDLE, MPI_T_ERR_PVAR_NO_ATOMIC
{ -- error_check --
     if (handle == MPI_T_PVAR_ALL_HANDLES || session != handle->session
	|| !MPIR_T_pvar_is_oncestarted(handle)) {
	mpi_errno = MPI_T_ERR_INVALID_HANDLE;
	goto fn_fail;
    }

    if (!MPIR_T_pvar_is_atomic(handle)) {
	mpi_errno = MPI_T_ERR_PVAR_NO_ATOMIC;
	goto fn_fail;
    }
}

MPI_T_pvar_reset:
    .desc: Reset a performance variable
    .error: MPI_T_ERR_INVALID_HANDLE, MPI_T_ERR_PVAR_NO_WRITE
/*
    Notes:
    The MPI_T_pvar_reset() call sets the performance variable with the handle identified
    by the parameter handle to its starting value. If it is not possible
    to change the variable, the function returns MPI_T_ERR_PVAR_NO_WRITE.
    If the constant MPI_T_PVAR_ALL_HANDLES is passed in handle, the MPI implementation
    attempts to reset all variables within the session identified by the parameter session for
    which handles have been allocated. In this case, the routine returns MPI_SUCCESS if all
    variables are reset successfully, otherwise MPI_T_ERR_PVAR_NO_WRITE is returned. Readonly
    variables are ignored when MPI_T_PVAR_ALL_HANDLES is specified.
*/
{
    MPIR_T_pvar_handle_t *hnd;
    /* If handle is MPI_T_PVAR_ALL_HANDLES, dispatch the call.
     * Otherwise, do correctness check, then go to impl.
     */
    if (handle == MPI_T_PVAR_ALL_HANDLES) {
        DL_FOREACH(session->hlist, hnd) {
            if (!MPIR_T_pvar_is_readonly(hnd)) {
                mpi_errno = MPIR_T_pvar_reset_impl(session, hnd);
                if (mpi_errno != MPI_SUCCESS)
                    goto fn_fail;
            }
        }
    } else {
        if (handle->session != session) {
            mpi_errno = MPI_T_ERR_INVALID_HANDLE;
            goto fn_fail;
        }

        if (MPIR_T_pvar_is_readonly(handle)) {
            mpi_errno = MPI_T_ERR_PVAR_NO_WRITE;
            goto fn_fail;
        }

        mpi_errno = MPIR_T_pvar_reset_impl(session, handle);
        if (mpi_errno != MPI_SUCCESS)
            goto fn_fail;
    }
}

MPI_T_pvar_session_create:
    .desc: Create a new session for accessing performance variables
    .error: MPI_T_ERR_OUT_OF_SESSIONS

MPI_T_pvar_session_free:
    .desc: Free an existing performance variable session
/*
    Notes:
    Calls to the MPI tool information interface can no longer be made
    within the context of a session after it is freed. On a successful
    return, MPI sets the session identifier to MPI_T_PVAR_SESSION_NULL.
*/

MPI_T_pvar_start:
    .desc: Start a performance variable
    .error: MPI_T_ERR_INVALID_HANDLE, MPI_T_ERR_PVAR_NO_STARTSTOP
/*
    Notes:
    If the constant MPI_T_PVAR_ALL_HANDLES is passed in handle, the MPI implementation
    attempts to start all variables within the session identified by the parameter session for
    which handles have been allocated. In this case, the routine returns MPI_SUCCESS if all
    variables are started successfully, otherwise MPI_T_ERR_PVAR_NO_STARTSTOP is returned.
    Continuous variables and variables that are already started are ignored when
    MPI_T_PVAR_ALL_HANDLES is specified.
*/
{
    if (handle == MPI_T_PVAR_ALL_HANDLES) {
        MPIR_T_pvar_handle_t *hnd;
        DL_FOREACH(session->hlist, hnd) {
            if (!MPIR_T_pvar_is_continuous(hnd) && !MPIR_T_pvar_is_started(hnd))
                mpi_errno = MPIR_T_pvar_start_impl(session, hnd);
        }
    } else {
        if (handle->session != session) {
            mpi_errno = MPI_T_ERR_INVALID_HANDLE;
            goto fn_fail;
        }
        if (MPIR_T_pvar_is_continuous(handle)) {
            mpi_errno = MPI_T_ERR_PVAR_NO_STARTSTOP;
            goto fn_fail;
        }

        if (!MPIR_T_pvar_is_started(handle)) {
            mpi_errno = MPIR_T_pvar_start_impl(session, handle);
            if (mpi_errno != MPI_SUCCESS)
                goto fn_fail;
        }
    }
}

MPI_T_pvar_stop:
    .desc: Stop a performance variable
    .error: MPI_T_ERR_INVALID_HANDLE, MPI_T_ERR_PVAR_NO_STARTSTOP
/*
    Notes:
    This functions stops the performance variable with the handle identified by the parameter
    handle in the session identified by the parameter session.
    
    If the constant MPI_T_PVAR_ALL_HANDLES is passed in handle, the MPI implementation
    attempts to stop all variables within the session identified by the parameter session for
    which handles have been allocated. In this case, the routine returns MPI_SUCCESS if all
    variables are stopped successfully, otherwise MPI_T_ERR_PVAR_NO_STARTSTOP is returned.
    Continuous variables and variables that are already stopped are ignored when
    MPI_T_PVAR_ALL_HANDLES is specified.
*/
{
    /* If handle is MPI_T_PVAR_ALL_HANDLES, dispatch the call.
     * Otherwise, do correctness check, then go to impl.
     */
    if (handle == MPI_T_PVAR_ALL_HANDLES) {
        MPIR_T_pvar_handle_t *hnd;
        DL_FOREACH(session->hlist, hnd) {
            if (!MPIR_T_pvar_is_continuous(hnd) && MPIR_T_pvar_is_started(hnd)) {
                mpi_errno = MPIR_T_pvar_stop_impl(session, hnd);
                if (mpi_errno != MPI_SUCCESS)
                    goto fn_fail;
            }
        }
    } else {
        if (handle->session != session) {
            mpi_errno = MPI_T_ERR_INVALID_HANDLE;
            goto fn_fail;
        }
        if (MPIR_T_pvar_is_continuous(handle)) {
            mpi_errno = MPI_T_ERR_PVAR_NO_STARTSTOP;
            goto fn_fail;
        }

        if (MPIR_T_pvar_is_started(handle)) {
            mpi_errno = MPIR_T_pvar_stop_impl(session, handle);
            if (mpi_errno != MPI_SUCCESS)
                goto fn_fail;
        }
    }
}

MPI_T_pvar_write:
    .desc: Write a performance variable
    .error: MPI_T_ERR_PVAR_NO_WRITE
/*
    Notes:
    The MPI_T_pvar_write() call attempts to write the value of the performance variable
    with the handle identified by the parameter handle in the session identified by the parameter
    session. The value to be written is passed in the buffer identified by the parameter buf. The
    user must ensure that the buffer is of the appropriate size to hold the entire value of the
    performance variable (based on the datatype and count returned by the corresponding previous
    calls to MPI_T_pvar_get_info() and MPI_T_pvar_handle_alloc(), respectively).
    
    The constant MPI_T_PVAR_ALL_HANDLES cannot be used as an argument for the function
    MPI_T_pvar_write().
*/
{ -- error_check --
    if (MPIR_T_pvar_is_readonly(handle)) {
        mpi_errno = MPI_T_ERR_PVAR_NO_WRITE;
        goto fn_fail;
    }
}

MPI_T_category_get_num_events:
    .desc: Returns the number of event types contained in the queried category.
    .error: MPI_T_ERR_INVALID_INDEX

MPI_T_category_get_events:
    .desc: Query which event types are contained in a particular category.
    .error: MPI_T_ERR_INVALID_INDEX

MPI_T_source_get_num:
    .desc: Query the number of event sources

MPI_T_source_get_info:
    .desc: Returns additional information on the source identified by the source_index argument
    .skip: validate-INFO, validate-SOURCE_INDEX
    .error: MPI_T_ERR_INVALID_INDEX

MPI_T_source_get_timestamp:
    .desc: Returns a current timestamp from the source identified by the source_index argument
    .skip: validate-SOURCE_INDEX
    .error: MPI_T_ERR_INVALID_INDEX, MPI_T_ERR_NOT_SUPPORTED

MPI_T_event_get_num:
    .desc: Query the number of event types

MPI_T_event_get_info:
    .desc: Returns additional information about a specific event type
    .skip: validate-STRING, validate-DATATYPE, validate-DISPLACEMENT_NNI, validate-ARRAY_LENGTH_NNI, validate-INFO, validate-EVENT_INDEX
    .error: MPI_T_ERR_INVALID_INDEX

MPI_T_event_get_index:
    .desc: Returns the index of an event type identified by a known event type name
    .error: MPI_T_ERR_INVALID_NAME

MPI_T_event_handle_alloc:
    .desc: Creates a registration handle for the event type identified by event_index
    .skip: validate-TOOL_MPI_OBJ, validate-EVENT_INDEX
    .error: MPI_T_ERR_INVALID_INDEX

MPI_T_event_handle_set_info:
    .desc: Updates the hints of the event-registration handle associated with event_registration

MPI_T_event_handle_get_info:
    .desc: Returns a new info object containing the hints of the event-registration handle associated with event_registration

MPI_T_event_register_callback:
    .desc: Associates a user-defined function with an allocated event-registration handle
    .skip: validate-CALLBACK_SAFETY, validate-EVENT_CB_FUNCTION

MPI_T_event_callback_set_info:
    .desc: Updates the hints of the callback function registered for the callback safety level specified by cb_safety of the event-registration handle associated with event_registration
    .skip: validate-CALLBACK_SAFETY

MPI_T_event_callback_get_info:
    .desc: Returns a new info object containing the hints of the callback function registered for the callback safety level specified by cb_safety of the event-registration handle associated with event_registration.
    .skip: validate-CALLBACK_SAFETY

MPI_T_event_handle_free:
    .desc: Initiates deallocation of the event-registration handle specified by event_registration
    .skip: validate-EVENT_FREE_CB_FUNCTION

MPI_T_event_set_dropped_handler:
    .desc: Registers a function to be called when event information is dropped for the registration handle specified in event_registration
    .skip: validate-EVENT_DROP_CB_FUNCTION

MPI_T_event_read:
    .desc: Copy one element of event data to a user-specified buffer

MPI_T_event_copy:
    .desc: Copy event data as a whole into a user-specified buffer

MPI_T_event_get_timestamp:
    .desc: Returns the timestamp of when the event was initially observed by the implementation

MPI_T_event_get_source:
    .desc: Returns the index of the source of the event instance
