#!/usr/bin/sh

##
## This file is part of the Score-P software (http://www.score-p.org)
##
## Copyright (c) 2017, 2022, 2025,
## Technische Universitaet Dresden, Germany
##
## Copyright (c) 2024,
## Forschungszentrum Juelich GmbH, Germany
##
## This software may be modified and distributed under the terms of
## a BSD-style license.  See the COPYING file in the package base
## directory for details.
##

set -e

argv0=${0##*/}

short_usage()
{
    printf "Usage: ${argv0} <scorep-config flags> [--] <application> [<resultdir>]\n"
    printf "This script is EXPERIMENTAL.\n"
    printf "Try '%s --help' for more information.\n" "$argv0"
}

die()
{
    (
        printf "%s: " "$argv0"
        printf "$@"
        printf "\n"
        short_usage
    ) >&2
    exit 1
}

warn()
{
    (
        printf "%s: warning: " "$argv0"
        printf "$@"
        printf "\n"
    ) >&2
}

usage()
{
    if test -t 1
    then
        ${PAGER-$(type less >/dev/null 2>&1 && echo less || echo cat)}
    else
        cat
    fi <<EOH
Usage
=====

    ${argv0} <scorep-config flags> [--] <application> [<resultdir>]
    ${argv0} --clean                    <preload-file>

Options
-------

  - \`--help\`            This help text
  - \`--verbose\`         Runs in verbose mode
  - \`--value-only\`      Outputs only the value of \`LD_PRELOAD\` to standard out.
  - \`--\`                Stop parsing arguments for \`scorep-config\`.

See \`scorep-config --help\` for all options. Though not all are supported for
preloading.

Description
===========

\`scorep-preload-init\` is a script that prepares a measurement to be executed
on an uninstrumented application with the help of the \`LD_PRELOAD\` mechanism.

If the login and compute environments differ, this preparation step must be
executed on the login resources, <resultdir> must be available on the compute
resources, and the value of \`LD_PRELOAD\` must only affect applications running
on the compute resources.

This script is EXPERIMENTAL.

Report bugs to <support@score-p.org>
EOH
}

: ${SCOREP_BINDIR:="/usr/lib64/mpich/bin"}
: ${NM:="/usr/bin/nm -B"}
: ${EGREP:="/usr/bin/grep -E"}
: ${INSTALL:="/usr/bin/install -c"}

MPI_SUPPORT=false
MPI_SUPPORT=true

SHMEM_SUPPORT=false
#SHMEM_SUPPORT=true

OMPT_SUPPORT=false
#OMPT_SUPPORT=true
#DEFAULT_OMPT_SUPPORT=false
DEFAULT_OMPT_SUPPORT=true

PTHREAD_SUPPORT=false
PTHREAD_SUPPORT=true

SCOREP_CONFIG="${SCOREP_BINDIR}/scorep-config --nouser --nocompiler --noopenacc --nocuda"

CLEAN=false
VALUE_ONLY=false
MPP=detect
THREAD=none
THREAD=pthread
IO=
IO=posix

# check for arguments
while test $# -gt 0
do
    case ${1} in
    (--help)
        usage
        exit 0
    ;;
    (--verbose)
        V=1
    ;;
    (--clean)
        CLEAN=true
    ;;
    (--value-only)
        VALUE_ONLY=true
    ;;
    (--mpp=*)
        MPP=${1#--mpp=}
    ;;
    (--io=*)
        IO=${1#--io=}
    ;;
    (--thread)
        THREAD=pthread
    ;;
    (--thread=*)
        THREAD=${1#--thread=}
    ;;
    (--thread=omp:ompt)
        if ! $OMPT_SUPPORT
        then
            die "option not supported by Score-P: '%s'" "${1}"
        fi
        SCOREP_CONFIG="${SCOREP_CONFIG} ${1}"
    ;;
    (--thread=omp)
        if ! $OMPT_SUPPORT || ! $DEFAULT_OMPT_SUPPORT
        then
            warn "option not suitable for uninstrumented applications: '%s'" "${1}"
        fi
        SCOREP_CONFIG="${SCOREP_CONFIG} ${1}"
    ;;
    (--compiler|--user|--pomp)
        warn "option not suitable for uninstrumented applications: '%s'" "${1}"
        SCOREP_CONFIG="${SCOREP_CONFIG} ${1}"
    ;;
    (--*)
        # pass arguments to scorep-config
        SCOREP_CONFIG="${SCOREP_CONFIG} ${1}"
    ;;
    (--)
        shift
        break
    ;;
    (*)
        break
    ;;
    esac
    shift
done

__v_libtool_=--silent
__v_libtool_1=
eval "_V_libtool=\${__v_libtool_${V}}"

libtool_clean()
{
    ${SCOREP_LIBTOOL} ${_V_libtool} --mode=uninstall rm -f "${1}"
}

if ${CLEAN}
then
    if test $# -ne 1
    then
        die "missing clean argument"
    fi
    SCOREP_LIBTOOL=$(${SCOREP_CONFIG} --libtool)
    libtool_clean "${1}"
    rm -f "${1%*.la}.clean"
    exit
fi

if test $# -lt 1
then
    usage
    exit 1
fi

if test $# -gt 2
then
    die "too many arguments"
fi

BINARY=${1-}
if test x"${BINARY:+set}" = x"set"
then
    BINARY_TAG=$(basename "${BINARY}")
    RESULTDIR=$(cd ${2-$(dirname "${BINARY}")} && pwd)/.scorep_preload #")
else
    BINARY_TAG=unknown_$$
    RESULTDIR=$(cd ${2-${PWD}} && pwd)/.scorep_preload
fi

SCOREP_TMP="$(mktemp -d -t scorep_preload.XXXXXXXXXX)"
cleanup()
{
    rm -rf "${SCOREP_TMP}"
}
${V:+: }trap cleanup EXIT

# auto detect used multi-process paradigm
if test ${MPP} = detect
then
    # MPI is still the default for scorep-config
    MPP=none

    if test x"${BINARY:+set}" = x"set" && test -x ${BINARY}
    then
        ${NM} "${BINARY}" 2>/dev/null >"${SCOREP_TMP}/nm.out"

        if ${EGREP} -l ' [UTD] (MPI|mpi)_' "${SCOREP_TMP}/nm.out" >/dev/null 2>&1
        then
            if ! ${MPI_SUPPORT}
            then
                die "detected MPI application but this Score-P installation does not support MPI"
            fi
            MPP=mpi
        fi

        if ${EGREP} -l ' [UTD] (shmem_|my_pe|_my_pe|num_pes|_num_pes|start_pes|shmalloc|shfree|shmemalign|shrealloc)' "${SCOREP_TMP}/nm.out" >/dev/null 2>&1
        then
            if ! ${SHMEM_SUPPORT}
            then
                die "detected SHMEM application but this Score-P installation does not support SHMEM"
            fi
            MPP=shmem
        fi
    fi
fi
SCOREP_CONFIG="${SCOREP_CONFIG}${MPP:+ --mpp=${MPP}}"

SCOREP_CONFIG_PREFIX=
if [ "${MPP}" != none ]; then
    SCOREP_CONFIG_PREFIX=${MPP}
fi
SCOREP_CC=$(${SCOREP_CONFIG} --${SCOREP_CONFIG_PREFIX}cc)
SCOREP_LIBTOOL=$(${SCOREP_CONFIG} --${SCOREP_CONFIG_PREFIX}libtool)

case "${THREAD},${OMPT_SUPPORT},${DEFAULT_OMPT_SUPPORT},${PTHREAD_SUPPORT}" in
(none,*,*,*) : pass ;;

(omp,true,true,*|omp:ompt,true,*,*) : pass ;;
(omp,false,*,*|omp:ompt,false,*,*)
    die "OpenMP requested but this Score-P installation does not support it"
;;

(pthread,*,*,true) : pass ;;
(pthread,*,*,false)
    die "Pthread requested but this Score-P installation does not support it"
;;

(*)
    die "option unknown or not suitable for uninstrumented applications: '--thread=%s'" "${THREAD}"
;;
esac
SCOREP_CONFIG="${SCOREP_CONFIG}${THREAD:+ --thread=${THREAD}}"

SCOREP_CONFIG="${SCOREP_CONFIG}${IO:+  --io=${IO}}"

if test -e "${RESULTDIR}/${BINARY_TAG}.la"
then
    libtool_clean "${RESULTDIR}/${BINARY_TAG}.la"
fi

# build subsystems list
${SCOREP_CONFIG} --adapter-init >"${SCOREP_TMP}/scorep_preload.c"

${SCOREP_LIBTOOL} ${_V_libtool} --mode=compile --tag=CC \
    ${SCOREP_CC} \
    -shared \
    -c \
    -o "${SCOREP_TMP}/scorep_preload.lo" "${SCOREP_TMP}/scorep_preload.c"

${SCOREP_LIBTOOL} ${_V_libtool} --mode=link --tag=CC \
    ${SCOREP_CC} \
    -avoid-version \
    -module \
    -shared \
    -o ${SCOREP_TMP}/${BINARY_TAG}.la \
    `${SCOREP_CONFIG} --ldflags` \
    ${SCOREP_TMP}/scorep_preload.lo \
    `${SCOREP_CONFIG} --mgmt-libs` \
    -rpath "${RESULTDIR}"

mkdir -p "${RESULTDIR}"
${SCOREP_LIBTOOL} ${_V_libtool} --mode=install \
    ${INSTALL} "${SCOREP_TMP}/${BINARY_TAG}.la" \
    "${RESULTDIR}/${BINARY_TAG}.la"

libscorep_preload=${RESULTDIR}/$(
    . "${RESULTDIR}/${BINARY_TAG}.la"
    echo "${library_names##* }"
)

# we still need to preload all event adapters, so that library interposition works
LD_PRELOAD_VALUE="${libscorep_preload}"
# --preload-libs gives us full paths to the shared objects
for event_lib in `${SCOREP_CONFIG} --preload-libs`
do
    LD_PRELOAD_VALUE="${LD_PRELOAD_VALUE}:${event_lib}"
done
LD_PRELOAD_VALUE="${LD_PRELOAD_VALUE}${LD_PRELOAD:+:${LD_PRELOAD}}"

printf "${SCOREP_BINDIR}/${argv0} --clean \"${RESULTDIR}/${BINARY_TAG}.la\"\n" >"${RESULTDIR}/${BINARY_TAG}.clean"

if ${VALUE_ONLY}
then
    printf "%s\n" "${LD_PRELOAD_VALUE}"
else
    cat <<EOA
The value for LD_PRELOAD is:

    LD_PRELOAD=${LD_PRELOAD_VALUE}

Set this variable when starting your application. This might be needed
to be done when launching your application or setting it before executing
the application in the job script.

To remove this preload result run:

    $SHELL "${RESULTDIR}/${BINARY_TAG}.clean"

This script is EXPERIMENTAL.
EOA
fi
