#!/usr/bin/env python

"""Check Chapel runtime for 'inappropriate' function calls

Check the chapel runtime for calls to the system allocator, exit routines, or
direct calls to atomic_*_fence routines.

Generally speaking, unless the memory layer hasn't been initialized, or you
need memory that isn't registered, the runtime should use the chapel allocator,
not the system one. This ensures that we don't mismatch calls between our
allocator and the system one, and ensures we don't use the system one for no
reason as that can hurt performance (slower allocator, mem not registered)

The only file that's allowed to directly call the system allocator is the sys
alloc wrapper (chpl-mem-sys.h)

Also checks for calls to exit. We want to use chpl_exit_* instead so there's a
common place to break.

We have chpl_ wrappers for the atomic_*_fence() calls and we want to use those
for portability.
"""

import os
import sys
import look_for_calls

chplenv_dir = os.path.join(os.path.dirname(__file__), '..', 'chplenv')
sys.path.insert(0, os.path.abspath(chplenv_dir))

from chpl_home_utils import get_chpl_home


def main():
    """Check runtime for "bad function calls"""
    chpl_home = get_chpl_home()
    runtime_dir = os.path.join(chpl_home, 'runtime')

    # Check runtime (except chpl-mem-sys.h) for alloc calls
    alloc_exclude_paths = ['chpl-mem-sys.h']
    alloc_funcs = look_for_calls.get_alloc_funcs()
    ret = look_for_calls.check_for_calls(alloc_funcs, runtime_dir, alloc_exclude_paths)

    # Check runtime (except cstdlib chpl-atomics.h) for atomic_*_fence calls
    cstdlib_header = os.path.join('atomics', 'cstdlib','chpl-atomics.h')
    atomic_exclude_paths = [cstdlib_header]
    atomic_funcs = ['atomic_thread_fence', 'atomic_signal_fence']
    ret = ret or look_for_calls.check_for_calls(atomic_funcs, runtime_dir, atomic_exclude_paths)

    # Check runtime for exit calls
    exit_exclude_paths = ['error.h', 'chplexit.c', 'chplexit.h']
    exit_funcs = look_for_calls.get_exit_funcs()
    ret = ret or look_for_calls.check_for_calls(exit_funcs, runtime_dir, exit_exclude_paths)
    return ret

if __name__ == "__main__":
    sys.exit(main())
