Python 3.11 support + update RedHat CPack (#254)

* Fixes for Python 3.11

* Add python 3.11 to scripts

- also tweak to to{upper,lower} bash functions

* Fix PAPI RPM packaging in RedHat

- fix error from #!/usr/bin/python in papi_hl_output_writer.py
  - requires either python2 or python3 instead of python

* cpack updates

- only generate STGZ for RedHat
- support `--generators` arg in build-release.sh
- support 7z, zip, and other zip generators
- fix build-release.sh with `--mpi`
- support setting CONDA_ROOT

* Support rhel/fedora/centos in omnitrace-install.py

* RedHat status badge

* Fix support for Python 3.11 + tweak ubuntu ci

- Remove installing clang and mpich in Ubuntu CI container
- Fallback on conda-forge for Python 3.11
- Enable entrypoint-rhel.sh for RHEL CI
- Pull latest container by default

* Update ElfUtils and PAPI builds

- quieter build output
- disable-nls for ElfUtils
- use -s flag for make

* Development Guide Docs

[ROCm/rocprofiler-systems commit: 83f9ed8696]
Bu işleme şunda yer alıyor:
Jonathan R. Madsen
2023-03-08 00:19:29 -06:00
işlemeyi yapan: GitHub
ebeveyn 446fd36a93
işleme e70d684c98
22 değiştirilmiş dosya ile 570 ekleme ve 79 silme
+19 -4
Dosyayı Görüntüle
@@ -161,6 +161,21 @@ jobs:
run: |
echo "CI_SCRIPT_ARGS=--rocm +python" >> $GITHUB_ENV
- name: Configure Ubuntu Generators
if: ${{ matrix.os-distro == 'ubuntu' }}
run: |
echo "CI_GENERATOR_ARGS=--generators STGZ DEB" >> $GITHUB_ENV
- name: Configure OpenSUSE Generators
if: ${{ matrix.os-distro == 'opensuse' }}
run: |
echo "CI_GENERATOR_ARGS=--generators STGZ RPM" >> $GITHUB_ENV
- name: Configure RedHat Generators
if: ${{ matrix.os-distro == 'rhel' }}
run: |
echo "CI_GENERATOR_ARGS=--generators STGZ" >> $GITHUB_ENV
- name: Build Base Container
timeout-minutes: 30
run: |
@@ -172,7 +187,7 @@ jobs:
timeout-minutes: 150
run: |
pushd docker
./build-docker-release.sh --distro ${{ matrix.os-distro }} --versions ${{ matrix.os-version }} --rocm-versions ${{ matrix.rocm-version }} -- ${CI_SCRIPT_ARGS}
./build-docker-release.sh --distro ${{ matrix.os-distro }} --versions ${{ matrix.os-version }} --rocm-versions ${{ matrix.rocm-version }} -- ${CI_SCRIPT_ARGS} ${CI_GENERATOR_ARGS}
popd
- name: List Files
@@ -199,7 +214,7 @@ jobs:
- name: RPM Artifacts
timeout-minutes: 10
if: ${{ matrix.os-distro != 'ubuntu' }}
if: ${{ matrix.os-distro == 'opensuse' }}
uses: actions/upload-artifact@v3
with:
name: omnitrace-rpm-${{ matrix.os-distro }}-${{ matrix.os-version }}-rocm-${{ matrix.rocm-version }}-installer
@@ -252,7 +267,7 @@ jobs:
- name: Test RPM Install
timeout-minutes: 20
if: ${{ matrix.os-distro != 'ubuntu' }}
if: ${{ matrix.os-distro == 'opensuse' }}
run: |
set -v
for i in omnitrace-*.rpm
@@ -262,7 +277,7 @@ jobs:
- name: Upload RPM Release Assets
uses: softprops/action-gh-release@v1
if: matrix.os-distro != 'ubuntu' && startsWith(github.ref, 'refs/tags/') && github.repository == 'AMDResearch/omnitrace'
if: matrix.os-distro == 'opensuse' && startsWith(github.ref, 'refs/tags/') && github.repository == 'AMDResearch/omnitrace'
with:
fail_on_unmatched_files: True
files: |
+1
Dosyayı Görüntüle
@@ -4,6 +4,7 @@
[![Ubuntu 20.04 with GCC, ROCm, and MPI](https://github.com/AMDResearch/omnitrace/actions/workflows/ubuntu-focal.yml/badge.svg)](https://github.com/AMDResearch/omnitrace/actions/workflows/ubuntu-focal.yml)
[![Ubuntu 22.04 (GCC, Python, ROCm)](https://github.com/AMDResearch/omnitrace/actions/workflows/ubuntu-jammy.yml/badge.svg)](https://github.com/AMDResearch/omnitrace/actions/workflows/ubuntu-jammy.yml)
[![OpenSUSE 15.x with GCC](https://github.com/AMDResearch/omnitrace/actions/workflows/opensuse.yml/badge.svg)](https://github.com/AMDResearch/omnitrace/actions/workflows/opensuse.yml)
[![RedHat Linux (GCC, Python, ROCm)](https://github.com/AMDResearch/omnitrace/actions/workflows/redhat.yml/badge.svg)](https://github.com/AMDResearch/omnitrace/actions/workflows/redhat.yml)
[![Installer Packaging (CPack)](https://github.com/AMDResearch/omnitrace/actions/workflows/cpack.yml/badge.svg)](https://github.com/AMDResearch/omnitrace/actions/workflows/cpack.yml)
[![Documentation](https://github.com/AMDResearch/omnitrace/actions/workflows/docs.yml/badge.svg)](https://github.com/AMDResearch/omnitrace/actions/workflows/docs.yml)
+4 -4
Dosyayı Görüntüle
@@ -109,16 +109,16 @@ externalproject_add(
CFLAGS=-fPIC\ -O3\ -Wno-error=null-dereference CXX=${ElfUtils_CXX_COMPILER}
CXXFLAGS=-fPIC\ -O3\ -Wno-error=null-dereference
[=[LDFLAGS=-Wl,-rpath='$$ORIGIN']=] <SOURCE_DIR>/configure --enable-install-elfh
--prefix=${_eu_root} --disable-libdebuginfod --disable-debuginfod
--enable-thread-safety
BUILD_COMMAND ${MAKE_COMMAND} install
--prefix=${_eu_root} --disable-libdebuginfod --disable-debuginfod --disable-nls
--enable-thread-safety --enable-silent-rules
BUILD_COMMAND ${MAKE_COMMAND} install -s
BUILD_BYPRODUCTS "${_eu_build_byproducts}"
INSTALL_COMMAND "")
# target for re-executing the installation
add_custom_target(
omnitrace-elfutils-install
COMMAND ${MAKE_COMMAND} install
COMMAND ${MAKE_COMMAND} install -s
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/external/elfutils/src/ElfUtils-External
COMMENT "Installing ElfUtils...")
+22 -5
Dosyayı Görüntüle
@@ -206,13 +206,13 @@ externalproject_add(
PATCH_COMMAND
${CMAKE_COMMAND} -E env CC=${PAPI_C_COMPILER}
CFLAGS=-fPIC\ -O3\ -Wno-stringop-truncation LIBS=-lrt LDFLAGS=-lrt
${OMNITRACE_PAPI_EXTRA_ENV} <SOURCE_DIR>/configure
${OMNITRACE_PAPI_EXTRA_ENV} <SOURCE_DIR>/configure --quiet
--prefix=${OMNITRACE_PAPI_INSTALL_DIR} --with-static-lib=yes --with-shared-lib=no
--with-perf-events --with-tests=no --with-components=${_OMNITRACE_PAPI_COMPONENTS}
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CFLAGS=-fPIC\ -O3\ -Wno-stringop-truncation
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} static install
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} static install -s
BUILD_COMMAND ${CMAKE_COMMAND} -E env CFLAGS=-fPIC\ -O3\ -Wno-stringop-truncation
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} utils install-utils
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} utils install-utils -s
INSTALL_COMMAND ""
BUILD_BYPRODUCTS "${_OMNITRACE_PAPI_BUILD_BYPRODUCTS}")
@@ -220,9 +220,9 @@ externalproject_add(
add_custom_target(
omnitrace-papi-install
COMMAND ${CMAKE_COMMAND} -E env CFLAGS=-fPIC\ -O3\ -Wno-stringop-truncation
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} static install
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} static install -s
COMMAND ${CMAKE_COMMAND} -E env CFLAGS=-fPIC\ -O3\ -Wno-stringop-truncation
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} utils install-utils
${OMNITRACE_PAPI_EXTRA_ENV} ${MAKE_EXECUTABLE} utils install-utils -s
WORKING_DIRECTORY ${OMNITRACE_PAPI_SOURCE_DIR}/src
COMMENT "Installing PAPI...")
@@ -288,7 +288,24 @@ foreach(
papi_native_avail
papi_version
papi_xml_event_info)
string(REPLACE "_" "-" _UTIL_EXE_INSTALL_NAME "omnitrace-${_UTIL_EXE}")
# RPM installer on RedHat/RockyLinux throws error that #!/usr/bin/python should either
# be #!/usr/bin/python2 or #!/usr/bin/python3
if(_UTIL_EXE STREQUAL "papi_hl_output_writer.py")
file(
READ
"${PROJECT_BINARY_DIR}/external/papi/source/src/high-level/scripts/${_UTIL_EXE}"
_HL_OUTPUT_WRITER)
string(REPLACE "#!/usr/bin/python\n" "#!/usr/bin/python3\n" _HL_OUTPUT_WRITER
"${_HL_OUTPUT_WRITER}")
file(MAKE_DIRECTORY "${OMNITRACE_PAPI_INSTALL_DIR}/bin")
file(WRITE "${OMNITRACE_PAPI_INSTALL_DIR}/bin/${_UTIL_EXE}3"
"${_HL_OUTPUT_WRITER}")
set(_UTIL_EXE "${_UTIL_EXE}3")
endif()
install(
PROGRAMS ${OMNITRACE_PAPI_INSTALL_DIR}/bin/${_UTIL_EXE}
DESTINATION ${CMAKE_INSTALL_BINDIR}
+9 -5
Dosyayı Görüntüle
@@ -55,7 +55,6 @@ def get_rocm_version(rocm_hint):
def get_os_info(os_distrib, os_version):
_os_info = {}
with open("/etc/os-release", "r") as f:
for line in [_v.strip() for _v in f.readlines()]:
@@ -67,10 +66,18 @@ def get_os_info(os_distrib, os_version):
os_distrib = "ubuntu"
elif "suse" in _os_info["ID_LIKE"]:
os_distrib = "opensuse"
elif "rhel" in _os_info["ID_LIKE"]:
os_distrib = "rhel"
elif "fedora" in _os_info["ID_LIKE"]:
os_distrib = "rhel"
elif "centos" in _os_info["ID_LIKE"]:
os_distrib = "rhel"
else:
raise RuntimeError(
"Unknown ID_LIKE value in /etc/os-release: {}".format(_os_info["ID_LIKE"])
)
elif os_distrib == "fedora" or os_distrib == "centos":
os_distrib = "rhel"
if os_version is None:
@@ -84,7 +91,6 @@ def get_os_info(os_distrib, os_version):
def print_log(*args, **kwargs):
sys.stdout.flush()
sys.stderr.flush()
sys.stderr.write("### ")
@@ -94,14 +100,12 @@ def print_log(*args, **kwargs):
def run(*args, **kwargs):
print_log("Executing: {}\n".format(" ".join(*args)))
sp.run(*args, **kwargs, check=True)
sys.stderr.write("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
@@ -131,7 +135,7 @@ if __name__ == "__main__":
help="Target OS distribution",
type=str,
default=None,
choices=("auto", "ubuntu", "opensuse"),
choices=("auto", "ubuntu", "opensuse", "rhel", "centos", "fedora"),
)
parser.add_argument(
"-v", "--os-version", help="Target OS version", type=str, default=None
+3 -2
Dosyayı Görüntüle
@@ -31,14 +31,15 @@ RUN if [ "${ROCM_VERSION}" != "0.0" ]; then \
zypper clean --all; \
fi
ARG PYTHON_VERSIONS="6 7 8 9 10"
ARG PYTHON_VERSIONS="6 7 8 9 10 11"
RUN wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh && \
bash miniconda.sh -b -p /opt/conda && \
export PATH="/opt/conda/bin:${PATH}" && \
conda config --set always_yes yes --set changeps1 no && \
conda update -c defaults -n base conda && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults python=3.${i} pip; done && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults -c conda-forge python=3.${i} pip; done && \
for i in ${PYTHON_VERSIONS}; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done && \
conda clean -a -y && \
conda init
+3 -3
Dosyayı Görüntüle
@@ -33,16 +33,16 @@ RUN cd /tmp/dyninst && \
shopt -s dotglob extglob && \
rm -rf *
ARG PYTHON_VERSIONS="6 7 8 9 10"
ARG PYTHON_VERSIONS="6 7 8 9 10 11"
RUN wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh && \
bash miniconda.sh -b -p /opt/conda && \
export PATH="/opt/conda/bin:${PATH}" && \
conda config --set always_yes yes --set changeps1 no && \
conda update -c defaults -n base conda && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults python=3.${i} pip numpy; done && \
conda clean -a -y && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults -c conda-forge python=3.${i} pip numpy; done && \
for i in ${PYTHON_VERSIONS}; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done && \
conda clean -a -y && \
cd /tmp && \
shopt -s dotglob extglob && \
rm -rf *
+5 -2
Dosyayı Görüntüle
@@ -34,14 +34,15 @@ RUN if [ "${ROCM_VERSION}" != "0.0" ]; then \
yum clean all; \
fi
ARG PYTHON_VERSIONS="6 7 8 9 10"
ARG PYTHON_VERSIONS="6 7 8 9 10 11"
RUN wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh && \
bash miniconda.sh -b -p /opt/conda && \
export PATH="/opt/conda/bin:${PATH}" && \
conda config --set always_yes yes --set changeps1 no && \
conda update -c defaults -n base conda && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults python=3.${i} pip; done && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults -c conda-forge python=3.${i} pip; done && \
for i in ${PYTHON_VERSIONS}; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done && \
conda clean -a -y && \
conda init
@@ -50,3 +51,5 @@ RUN if [ "${ROCM_VERSION}" != "0.0" ]; then ln -sf /opt/rocm-${ROCM_VERSION}* /o
WORKDIR /home
ENV LC_ALL C.UTF-8
SHELL [ "/bin/bash", "--login", "-c" ]
COPY ./entrypoint-rhel.sh /docker-entrypoint.sh
ENTRYPOINT [ "/docker-entrypoint.sh" ]
+3 -3
Dosyayı Görüntüle
@@ -33,16 +33,16 @@ RUN cd /tmp/dyninst && \
shopt -s dotglob extglob && \
rm -rf *
ARG PYTHON_VERSIONS="6 7 8 9 10"
ARG PYTHON_VERSIONS="6 7 8 9 10 11"
RUN wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh && \
bash miniconda.sh -b -p /opt/conda && \
export PATH="/opt/conda/bin:${PATH}" && \
conda config --set always_yes yes --set changeps1 no && \
conda update -c defaults -n base conda && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults python=3.${i} pip numpy; done && \
conda clean -a -y && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults -c conda-forge python=3.${i} pip numpy; done && \
for i in ${PYTHON_VERSIONS}; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done && \
conda clean -a -y && \
cd /tmp && \
shopt -s dotglob extglob && \
rm -rf *
+3 -2
Dosyayı Görüntüle
@@ -18,7 +18,7 @@ ARG EXTRA_PACKAGES=""
ARG ROCM_REPO_VERSION="debian"
ARG ROCM_VERSION="0.0"
ARG ROCM_REPO_DIST="ubuntu"
ARG PYTHON_VERSIONS="6 7 8 9 10"
ARG PYTHON_VERSIONS="6 7 8 9 10 11"
ENV PATH ${HOME}/.local/bin:${PATH}
RUN apt-get update && \
@@ -48,7 +48,8 @@ RUN wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -
export PATH="/opt/conda/bin:${PATH}" && \
conda config --set always_yes yes --set changeps1 no && \
conda update -c defaults -n base conda && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults python=3.${i} pip; done && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults -c conda-forge python=3.${i} pip; done && \
for i in ${PYTHON_VERSIONS}; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done && \
conda clean -a -y && \
conda init
+4 -4
Dosyayı Görüntüle
@@ -16,7 +16,7 @@ ARG EXTRA_PACKAGES=""
ARG ELFUTILS_DOWNLOAD_VERSION="0.186"
ARG BOOST_DOWNLOAD_VERSION="1.79.0"
ARG NJOBS="12"
ARG PYTHON_VERSIONS="6 7 8 9 10"
ARG PYTHON_VERSIONS="6 7 8 9 10 11"
ENV PATH /usr/local/bin:${PATH}
ENV LIBRARY_PATH /usr/local/lib:/usr/local/lib64:${LIBRARY_PATH}
@@ -27,7 +27,7 @@ COPY ./dyninst-source /tmp/dyninst
RUN apt-get update && \
apt-get dist-upgrade -y && \
apt-get install -y autoconf autotools-dev bash-completion build-essential bzip2 clang cmake curl environment-modules git-core gnupg2 gzip libiberty-dev libmpich-dev libpapi-dev libpfm4-dev libtool locales lsb-release m4 mpich python3-pip unzip wget zip zlib1g-dev && \
apt-get install -y autoconf autotools-dev bash-completion build-essential bzip2 cmake curl environment-modules git-core gnupg2 gzip libiberty-dev libpapi-dev libpfm4-dev libtool locales lsb-release m4 python3-pip unzip wget zip zlib1g-dev && \
python3 -m pip install 'cmake==3.18.4' && \
apt-get autoclean && \
locale -a && \
@@ -40,9 +40,9 @@ RUN apt-get update && \
export PATH="/opt/conda/bin:${PATH}" && \
conda config --set always_yes yes --set changeps1 no && \
conda update -c defaults -n base conda && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults python=3.${i} pip numpy; done && \
conda clean -a -y && \
for i in ${PYTHON_VERSIONS}; do conda create -n py3.${i} -c defaults -c conda-forge python=3.${i} pip numpy; done && \
for i in ${PYTHON_VERSIONS}; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done && \
conda clean -a -y && \
cd /tmp && \
shopt -s dotglob extglob && \
rm -rf *
+17 -2
Dosyayı Görüntüle
@@ -8,7 +8,9 @@ set -e
: ${NJOBS=$(nproc)}
: ${ELFUTILS_VERSION:=0.186}
: ${BOOST_VERSION:=1.79.0}
: ${PYTHON_VERSIONS:="6 7 8 9 10 11"}
: ${PUSH:=0}
: ${PULL:=--pull}
verbose-run()
{
@@ -18,12 +20,12 @@ verbose-run()
tolower()
{
echo "$@" | awk -F '\|~\|' '{print tolower($1)}';
echo "$@" | awk -F '\\|~\\|' '{print tolower($1)}';
}
toupper()
{
echo "$@" | awk -F '\|~\|' '{print toupper($1)}';
echo "$@" | awk -F '\\|~\\|' '{print toupper($1)}';
}
usage()
@@ -32,11 +34,13 @@ usage()
echo "Options:"
print_option "help -h" "" "This message"
print_option "push" "" "Push the container to DockerHub when completed"
print_option "no-pull" "" "Do not pull down most recent base container"
echo ""
print_default_option() { printf " --%-20s %-24s %s (default: %s)\n" "${1}" "${2}" "${3}" "$(tolower ${4})"; }
print_default_option distro "[ubuntu|opensuse|rhel]" "OS distribution" "${DISTRO}"
print_default_option versions "[VERSION] [VERSION...]" "Ubuntu, OpenSUSE, or RHEL release" "${VERSIONS}"
print_default_option python-versions "[VERSION] [VERSION...]" "Python 3 minor releases" "${PYTHON_VERSIONS}"
print_default_option "jobs -j" "[N]" "parallel build jobs" "${NJOBS}"
print_default_option elfutils-version "[0.183..0.186]" "ElfUtils version" "${ELFUTILS_VERSION}"
print_default_option boost-version "[1.67.0..1.79.0]" "Boost version" "${BOOST_VERSION}"
@@ -75,6 +79,11 @@ do
VERSIONS=${1}
last() { VERSIONS="${VERSIONS} ${1}"; }
;;
"--python-versions")
shift
PYTHON_VERSIONS=${1}
last() { PYTHON_VERSIONS="${PYTHON_VERSIONS} ${1}"; }
;;
--jobs|-j)
shift
NJOBS=${1}
@@ -99,6 +108,10 @@ do
PUSH=1
reset-last
;;
"--no-pull")
PULL=""
reset-last
;;
--*)
reset-last
last ${1}
@@ -137,11 +150,13 @@ fi
for VERSION in ${VERSIONS}
do
verbose-run docker build . \
${PULL} \
-f ${DOCKER_FILE} \
--tag ${USER}/omnitrace:ci-base-${DISTRO}-${VERSION} \
--build-arg DISTRO=${DISTRO_IMAGE} \
--build-arg VERSION=${VERSION} \
--build-arg NJOBS=${NJOBS} \
--build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\" \
--build-arg ELFUTILS_DOWNLOAD_VERSION=${ELFUTILS_VERSION} \
--build-arg BOOST_DOWNLOAD_VERSION=${BOOST_VERSION}
done
+1 -1
Dosyayı Görüntüle
@@ -101,7 +101,7 @@ reset-last
: ${VERSIONS:=20.04 18.04}
: ${ROCM_VERSIONS:=5.0 4.5 4.3}
: ${MPI:=0}
: ${PYTHON_VERSIONS:="6 7 8 9 10"}
: ${PYTHON_VERSIONS:="6 7 8 9 10 11"}
: ${RETRY:=3}
n=0
+13 -6
Dosyayı Görüntüle
@@ -4,21 +4,22 @@
: ${ROCM_VERSIONS:="5.0"}
: ${DISTRO:=ubuntu}
: ${VERSIONS:=20.04}
: ${PYTHON_VERSIONS:="6 7 8 9 10"}
: ${PYTHON_VERSIONS:="6 7 8 9 10 11"}
: ${BUILD_CI:=""}
: ${PUSH:=0}
: ${PULL:=--pull}
: ${RETRY:=3}
set -e
tolower()
{
echo "$@" | awk -F '\|~\|' '{print tolower($1)}';
echo "$@" | awk -F '\\|~\\|' '{print tolower($1)}';
}
toupper()
{
echo "$@" | awk -F '\|~\|' '{print toupper($1)}';
echo "$@" | awk -F '\\|~\\|' '{print toupper($1)}';
}
usage()
@@ -26,6 +27,7 @@ usage()
print_option() { printf " --%-20s %-24s %s\n" "${1}" "${2}" "${3}"; }
echo "Options:"
print_option "help -h" "" "This message"
print_option "no-pull" "" "Do not pull down most recent base container"
echo ""
print_default_option() { printf " --%-20s %-24s %s (default: %s)\n" "${1}" "${2}" "${3}" "$(tolower ${4})"; }
@@ -118,6 +120,11 @@ do
;;
--push)
PUSH=1
reset-last
;;
--no-pull)
PULL=""
reset-last
;;
--retry|-r)
shift
@@ -184,7 +191,7 @@ do
*)
;;
esac
verbose-build docker build . -f ${DOCKER_FILE} --tag ${CONTAINER} --build-arg DISTRO=${DISTRO} --build-arg VERSION=${VERSION} --build-arg ROCM_VERSION=${ROCM_VERSION} --build-arg ROCM_REPO_VERSION=${ROCM_REPO_VERSION} --build-arg ROCM_REPO_DIST=${ROCM_REPO_DIST} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\"
verbose-build docker build . ${PULL} -f ${DOCKER_FILE} --tag ${CONTAINER} --build-arg DISTRO=${DISTRO} --build-arg VERSION=${VERSION} --build-arg ROCM_VERSION=${ROCM_VERSION} --build-arg ROCM_REPO_VERSION=${ROCM_REPO_VERSION} --build-arg ROCM_REPO_DIST=${ROCM_REPO_DIST} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\"
elif [ "${DISTRO}" = "rhel" ]; then
if [ -z "${VERSION_MINOR}" ]; then
send-error "Please provide a major and minor version of the OS. Supported: >= 8.7, <= 9.1"
@@ -216,7 +223,7 @@ do
# use Rocky Linux as a base image for RHEL builds
DISTRO_BASE_IMAGE=rockylinux
verbose-build docker build . -f ${DOCKER_FILE} --tag ${CONTAINER} --build-arg DISTRO=${DISTRO_BASE_IMAGE} --build-arg VERSION=${VERSION} --build-arg ROCM_VERSION=${ROCM_VERSION} --build-arg AMDGPU_RPM=${ROCM_RPM} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\"
verbose-build docker build . ${PULL} -f ${DOCKER_FILE} --tag ${CONTAINER} --build-arg DISTRO=${DISTRO_BASE_IMAGE} --build-arg VERSION=${VERSION} --build-arg ROCM_VERSION=${ROCM_VERSION} --build-arg AMDGPU_RPM=${ROCM_RPM} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\"
elif [ "${DISTRO}" = "opensuse" ]; then
case "${VERSION}" in
15.*)
@@ -253,7 +260,7 @@ do
;;
esac
PERL_REPO="SLE_${VERSION_MAJOR}_SP${VERSION_MINOR}"
verbose-build docker build . -f ${DOCKER_FILE} --tag ${CONTAINER} --build-arg DISTRO=${DISTRO_IMAGE} --build-arg VERSION=${VERSION} --build-arg ROCM_VERSION=${ROCM_VERSION} --build-arg AMDGPU_RPM=${ROCM_RPM} --build-arg PERL_REPO=${PERL_REPO} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\"
verbose-build docker build . ${PULL} -f ${DOCKER_FILE} --tag ${CONTAINER} --build-arg DISTRO=${DISTRO_IMAGE} --build-arg VERSION=${VERSION} --build-arg ROCM_VERSION=${ROCM_VERSION} --build-arg AMDGPU_RPM=${ROCM_RPM} --build-arg PERL_REPO=${PERL_REPO} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\"
fi
if [ "${PUSH}" -ne 0 ]; then
docker push ${CONTAINER}
+7 -4
Dosyayı Görüntüle
@@ -1,10 +1,13 @@
#!/bin/bash
#!/bin/bash -l
source /etc/profile.d/modules.sh
module load mpi
if [ -f /etc/profile.d/modules.sh ]; then
source /etc/profile.d/modules.sh
module load mpi &> /dev/null
endif
if [ -z "${1}" ]; then
exec bash
: ${SHELL:=/bin/bash}
exec ${SHELL}
else
set -e
eval $@
+2 -2
Dosyayı Görüntüle
@@ -13,12 +13,12 @@ set -e
tolower()
{
echo "$@" | awk -F '\|~\|' '{print tolower($1)}';
echo "$@" | awk -F '\\|~\\|' '{print tolower($1)}';
}
toupper()
{
echo "$@" | awk -F '\|~\|' '{print toupper($1)}';
echo "$@" | awk -F '\\|~\\|' '{print toupper($1)}';
}
usage()
+96 -15
Dosyayı Görüntüle
@@ -1,5 +1,6 @@
#!/bin/bash -e
: ${VERBOSE:=0}
: ${EXTRA_ARGS:=""}
: ${BUILD_DIR:=build-release}
: ${VERSION:=0.0.4}
@@ -14,7 +15,7 @@
: ${MAX_THREADS:=2048}
: ${PERFETTO_TOOLS:="ON"}
: ${HIDDEN_VIZ:="ON"}
: ${PYTHON_VERSIONS:="6 7 8 9 10"}
: ${PYTHON_VERSIONS:="6 7 8 9 10 11"}
: ${GENERATORS:="STGZ DEB RPM"}
: ${MPI_IMPL:="openmpi"}
: ${CLEAN:=0}
@@ -24,6 +25,7 @@
: ${WITH_ROCM:=0}
: ${WITH_ROCM_MPI:=0}
: ${IS_DOCKER:=0}
: ${CONDA_ROOT:=/opt/conda}
if [ -z "${DISTRO}" ]; then
if [ -f /etc/os-release ]; then
@@ -35,17 +37,17 @@ fi
tolower()
{
echo "$@" | awk -F '\|~\|' '{print tolower($1)}';
echo "$@" | awk -F '\\|~\\|' '{print tolower($1)}';
}
toupper()
{
echo "$@" | awk -F '\|~\|' '{print toupper($1)}';
echo "$@" | awk -F '\\|~\\|' '{print toupper($1)}';
}
usage()
{
print_option() { printf " --%-10s %-24s %s\n" "${1}" "${2}" "${3}"; }
print_option() { printf " --%-10s %-36s %s\n" "${1}" "${2}" "${3}"; }
echo "Options:"
python_info="(Use '+nopython' to build w/o python, use '+python' to python build with python)"
print_option core "[+nopython] [+python]" "Core ${python_info}"
@@ -55,7 +57,7 @@ usage()
print_option mpi-impl "[openmpi|mpich]" "MPI implementation"
echo ""
print_default_option() { printf " --%-20s %-14s %s (default: %s)\n" "${1}" "${2}" "${3}" "$(tolower ${4})"; }
print_default_option() { printf " --%-20s %-26s %s (default: %s)\n" "${1}" "${2}" "${3}" "$(tolower ${4})"; }
print_default_option lto "[on|off]" "Enable LTO" "${LTO}"
print_default_option strip "[on|off]" "Strip libraries" "${STRIP}"
print_default_option perfetto-tools "[on|off]" "Install perfetto tools" "${PERFETTO_TOOLS}"
@@ -64,8 +66,23 @@ usage()
print_default_option hidden-visibility "[on|off]" "Build with hidden visibility" "${HIDDEN_VIZ}"
print_default_option max-threads "N" "Max number of threads supported" "${MAX_THREADS}"
print_default_option parallel "N" "Number of parallel build jobs" "${NJOBS}"
print_default_option generators "[STGZ][DEB][RPM][+others]" "CPack generators" "${GENERATORS}"
}
send-error()
{
usage
echo -e "\nError: ${@}"
exit 1
}
reset-last()
{
last() { send-error "Unsupported argument :: ${1}"; }
}
reset-last
while [[ $# -gt 0 ]]
do
ARG=${1}
@@ -75,10 +92,12 @@ do
case "${ARG}" in
--clean)
CLEAN=1
reset-last
continue
;;
--fresh)
FRESH=1
reset-last
continue
;;
esac
@@ -103,56 +122,72 @@ do
;;
--core)
WITH_CORE=${VAL}
reset-last
;;
--mpi)
WITH_MPI=${VAL}
reset-last
;;
--rocm)
WITH_ROCM=${VAL}
reset-last
;;
--rocm-mpi)
WITH_ROCM_MPI=${VAL}
reset-last
;;
--mpi-impl)
MPI_IMPL=${1}
shift
reset-last
;;
--lto)
LTO=$(toupper ${1})
shift
reset-last
;;
--static-libgcc)
LIBGCC=$(toupper ${1})
shift
reset-last
;;
--static-libstdcxx)
LIBSTDCXX=$(toupper ${1})
shift
reset-last
;;
--strip)
STRIP=$(toupper ${1})
shift
reset-last
;;
--hidden-visibility)
HIDDEN_VIZ=$(toupper ${1})
shift
reset-last
;;
--perfetto-tools)
PERFETTO_TOOLS=$(toupper ${1})
shift
reset-last
;;
--max-threads)
MAX_THREADS=${1}
shift
reset-last
;;
--parallel)
NJOBS=${1}
shift
reset-last
;;
--generators)
GENERATORS=$(toupper ${1})
shift
last() { GENERATORS="${GENERATORS} $(toupper ${1})"; }
;;
*)
echo -e "Error! Unknown option : ${ARG}"
usage
exit 1
last ${ARG} ${1}
;;
esac
done
@@ -210,7 +245,9 @@ build-and-package-base()
fi
pushd ${BUILD_DIR}/${DIR}
verbose-run cat CPackConfig.cmake
verbose-run cat cmake_install.cmake
if [ "${VERBOSE}" -gt 0 ]; then
verbose-run cat cmake_install.cmake
fi
popd
verbose-run cmake --build ${BUILD_DIR}/${DIR} --target all --parallel ${NJOBS}
verbose-run cmake --build ${BUILD_DIR}/${DIR} --target install --parallel ${NJOBS}
@@ -233,12 +270,54 @@ build-and-package-base()
SEP="_"
DEST="deb"
;;
DEB | RPM)
RPM)
verbose-run cpack -G RPM -D CPACK_PACKAGING_INSTALL_PREFIX=/opt/omnitrace
EXT="rpm"
SEP="-"
DEST="rpm"
;;
7Z | 7ZIP)
verbose-run cpack -G 7Z
EXT="7z"
SEP="-"
DEST="zip"
;;
TBZ2)
verbose-run cpack -G TBZ2
EXT="tar.bz2"
SEP="-"
DEST="zip"
;;
TGZ)
verbose-run cpack -G TGZ
EXT="tar.gz"
SEP="-"
DEST="zip"
;;
TXZ)
verbose-run cpack -G TXZ
EXT="tar.xz"
SEP="-"
DEST="zip"
;;
TZ)
verbose-run cpack -G TZ
EXT="tar.Z"
SEP="-"
DEST="zip"
;;
TZST)
verbose-run cpack -G TZST
EXT="tar.zst"
SEP="-"
DEST="zip"
;;
ZIP)
verbose-run cpack -G ZIP
EXT="zip"
SEP="-"
DEST="zip"
;;
*)
echo "Unsupported cpack generator: ${i}"
continue
@@ -284,14 +363,16 @@ build-and-package()
fi
}
if [ -d /opt/conda/bin ]; then
export PATH=${PATH}:/opt/conda/bin
if [ -d ${CONDA_ROOT}/bin ]; then
export PATH=${PATH}:${CONDA_ROOT}/bin
source activate
fi
if [ "${IS_DOCKER}" -ne 0 ]; then git config --global --add safe.directory ${PWD}; fi
build-and-package ${WITH_CORE} ${DISTRO}-core -DOMNITRACE_USE_HIP=OFF
build-and-package ${WITH_MPI} ${DISTRO}-${MPI_IMPL} -DOMNITRACE_USE_HIP=ON
build-and-package ${WITH_ROCM} ${DISTRO}-rocm-${ROCM_VERSION} -DOMNITRACE_USE_HIP=ON
verbose-run echo "Build omnitrace installers with generators: ${GENERATORS}"
build-and-package ${WITH_CORE} ${DISTRO}-core -DOMNITRACE_USE_HIP=OFF -DOMNITRACE_USE_MPI=OFF
build-and-package ${WITH_MPI} ${DISTRO}-${MPI_IMPL} -DOMNITRACE_USE_HIP=OFF -DOMNITRACE_USE_MPI=ON
build-and-package ${WITH_ROCM} ${DISTRO}-rocm-${ROCM_VERSION} -DOMNITRACE_USE_HIP=ON -DOMNITRACE_USE_MPI=OFF
build-and-package ${WITH_ROCM_MPI} ${DISTRO}-rocm-${ROCM_VERSION}-${MPI_IMPL} -DOMNITRACE_USE_HIP=ON -DOMNITRACE_USE_MPI=ON
+2 -2
Dosyayı Görüntüle
@@ -5,12 +5,12 @@ cd $(dirname ${SCRIPT_DIR})
tolower()
{
echo "$@" | awk -F '\|~\|' '{print tolower($1)}';
echo "$@" | awk -F '\\|~\\|' '{print tolower($1)}';
}
toupper()
{
echo "$@" | awk -F '\|~\|' '{print toupper($1)}';
echo "$@" | awk -F '\\|~\\|' '{print toupper($1)}';
}
: ${CMAKE_BUILD_PARALLEL_LEVEL:=$(nproc)}
+1 -1
Dosyayı Görüntüle
@@ -21,7 +21,7 @@ verbose-run()
toupper()
{
echo "$@" | awk -F '\|~\|' '{print toupper($1)}';
echo "$@" | awk -F '\\|~\\|' '{print toupper($1)}';
}
get-bool()
+311
Dosyayı Görüntüle
@@ -0,0 +1,311 @@
# Development Guide
## Miscellaneous Info
- [CDash Testing Dashboard](https://my.cdash.org/index.php?project=Omnitrace)
- requires login to view
## Executables
### omnitrace-avail: [source/bin/omnitrace-avail](https://github.com/AMDResearch/omnitrace/tree/main/source/bin/omnitrace-avail)
The main of `omnitrace-avail` has three important sections:
1. Printing components
2. Printing options
3. Printing hardware counters
### omnitrace-sample: [source/bin/omnitrace-sample](https://github.com/AMDResearch/omnitrace/tree/main/source/bin/omnitrace-sample)
General design:
- Requires a command-line format of `omnitrace-sample <options> -- <command> <command-args>`
- Translates command line options into environment variables
- Adds `libomnitrace-dl.so` to `LD_PRELOAD`
- Application is launched via `execvpe` with `<command> <command-args>` and modified environment
### omnitrace-casual: [source/bin/omnitrace-causal](https://github.com/AMDResearch/omnitrace/tree/main/source/bin/omnitrace-causal)
Nearly identical design to [omnitrace-sample](#omnitrace-sample-sourcebinomnitrace-sample) when
there is exactly one causal profiling configuration variant (this enables debugging).
When more than one causal profiling configuration variant it produced from command-line options,
for each variant:
- `omnitrace-causal` calls `fork()`
- child process launches `<command> <command-args>` via `execvpe` which modified environment for variant
- parent process waits for child process to finish
### omnitrace: [source/bin/omnitrace](https://github.com/AMDResearch/omnitrace/tree/main/source/bin/omnitrace)
- Requires a command-line format of `omnitrace <options> -- <command> <command-args>`
- User specifies in options whether they want to do runtime instrumentation, binary rewrite, or attach to process
- Either opens the instrumentation target (binary rewrite), launches the target and stops it before it starts executing main (runtime), or
attaches to running executable and pauses it
- Finds all functions in target(s)
- Finds `libomnitrace-dl` and finds the functions
- Iterates over all the functions and instruments them as long as they satisfy the defined criteria (minimum number of instructions, etc.)
- See the `module_function` class
- Most of the workflow has been the same at the point but once the instrumentation is complete, it diverges
- For a binary rewrite: outputs new instrumented binary and exits
- For runtime instrumentation or attaching to a process: instructs the application to resume executing and then waits for the application to exit
### omnitrace-critical-trace: [source/bin/omnitrace-critical-trace](https://github.com/AMDResearch/omnitrace/tree/main/source/bin/omnitrace-critical-trace)
Post-processing tool for critical-trace data output by omnitrace.
## Libraries
### Common Library: [source/lib/common](https://github.com/AMDResearch/omnitrace/tree/main/source/lib/common)
General header-only functionality used in multiple executables and/or libraries. Not installed or exported outside of the build tree.
### Core Library: [source/lib/core](https://github.com/AMDResearch/omnitrace/tree/main/source/lib/core)
Static PIC library with functionality that does not depend on any components. Not installed or exported outside of the build tree.
### Binary Library: [source/lib/binary](https://github.com/AMDResearch/omnitrace/tree/main/source/lib/binary)
Static PIC library with functionality for reading/analyzing binary info. Mostly used by the causal profiling sections
of [libomnitrace](#libomnitrace-sourcelibomnitrace). Not installed or exported outside of the build tree.
### libomnitrace: [source/lib/omnitrace](https://github.com/AMDResearch/omnitrace/tree/main/source/lib/omnitrace)
This is the main library encapsulating all the capabilities.
### libomnitrace-dl: [source/lib/omnitrace-dl](https://github.com/AMDResearch/omnitrace/tree/main/source/lib/omnitrace-dl)
Lightweight, front-end library for [libomnitrace](#libomnitrace-sourcelibomnitrace) which serves 3 primary purposes:
1. Dramatically speeds up instrumentation time vs. using [libomnitrace](#libomnitrace-sourcelibomnitrace) directly since Dyninst must parse entire library in order to find instrumentation functions ([libomnitrace](#libomnitrace-sourcelibomnitrace) is dlopen'ed when the instrumentation functions get called)
2. Prevents re-entry if [libomnitrace](#libomnitrace-sourcelibomnitrace) calls an instrumentated function internally)
3. Coordinates communication between [libomnitrace-user](#libomnitrace-user-sourcelibomnitrace-user) and [libomnitrace](#libomnitrace-sourcelibomnitrace)
### libomnitrace-user: [source/lib/omnitrace-user](https://github.com/AMDResearch/omnitrace/tree/main/source/lib/omnitrace-user)
Provides a set of functions and types for the users to add to their code, e.g. disabling data collection globally or on a specific thread,
user-defined regions, etc. If [libomnitrace-dl](#libomnitrace-dl-sourcelibomnitrace-dl) is not loaded, the user API is effectively no-op
function calls.
## Concepts
### Component
Most measurements and capabilities are encapsulated into a "component" with the following definitions:
- Measurement: recording of some data relevant to performance, e.g. current call-stack, hardware counter values, current memory usage, timestamp
- Capability: handles the implementation or orchestration of some feature which is used to collect measurements, e.g. a component which handles setting up function wrappers around various functions such as `pthread_create`, `MPI_Init`, etc.
Components are designed to hold no data at all or only the data for both an instantaeous measurement and a phase measurement.
Components which store data typically implement a static `record()` function (for getting a record of the measurement),
`start()` + `stop()` member functions for calculating a phase measurement, and a `sample()` member function for storing an
instantaneous measurement. In reality, there are several more "standard" functions but these are the most often used ones.
Components which do not store data may also have `start()`, `stop()`, and `sample()` functions but for components which
implement function wrappers, they typically provide a call operator or `audit(...)` functions which are invoked with the
wrappee function's arguments before the wrappee gets called and with the return value after the wrappee gets called.
***The goal of this design is to provide relatively small and resuable lightweight objects for recording measurements
and/or implementing capabilities.***
#### Wall-Clock Component Example
A component for computing the elapsed wall-clock time looks like this:
```cpp
struct wall_clock
{
using value_type = int64_t;
static value_type record() noexcept
{
return std::chrono::steady_clock::now().time_since_epoch().count();
}
void sample() noexcept
{
value = record();
}
void start() noexcept
{
value = record();
}
void stop() noexcept
{
auto _start_value = value;
value = record();
accum += (value - _start_value);
}
private:
int64_t value = 0;
int64_t accum = 0;
};
```
#### Function Wrapper Component Example
A component which implements wrappers around `fork()` and `exit(int)` (and stores no data) may look like this:
```cpp
struct function_wrapper
{
pid_t operator()(const gotcha_data&, pid_t (*real_fork)())
{
// disable all collection before forking
categories::disable_categories(config::get_enabled_categories());
auto _pid_v = real_fork();
// only re-enable collection on parent process
if(_pid_v != 0)
categories::enable_categories(config::get_enabled_categories());
return _pid_v;
}
void operator()(const gotcha_data&, void (*real_exit)(int), int _exit_code)
{
// catch the call to exit and finalize before truly exiting
omnitrace_finalize();
real_exit(_exit_code);
}
};
```
#### Component Member Functions
There are no real restrictions or requirements on the member functions a component needs to provide.
Unless the component is being directly used, invocation of component member functions via "component bundlers"
(provided via timemory) makes extensive use of template metaprogramming concept to find the best match (if any)
for calling a components member function. This is a bit easier to demonstrate via example:
```cpp
struct foo
{
void sample() { puts("foo::sample()"); }
};
struct bar
{
void sample(int) { puts("bar::sample(int)"); }
};
struct spam
{
void start(int) { puts("spam::start()"); }
void stop() { puts("spam::stop()"); }
};
int main()
{
auto _bundle = component_tuple<foo, bar, spam>{ "main" };
puts("A");
_bundle.start();
puts("B");
_bundle.sample(10);
puts("C");
_bundle.sample();
puts("D");
_bundle.stop();
}
```
In the above, this would be the message printed:
```console
A
bar::start()
B
foo::sample()
bar::sample(int)
C
foo::sample()
D
spam::stop()
```
In section A, the bundle determined only the `spam` object had a `start` function. Since this is determined
via template metaprogramming instead of dynamic polymorphism, this effectively elides any code related to
the `foo` or `bar` objects. In section B, since an integer of `10` was passed to the bundle,
the bundle forwards that value onto `spam::sample(int)` after it invokes `foo::sample()` -- which
is invoked because it recognizes that the call is the `sample` member function is still possible without
the arguments.
## Memory Model
Collected data is generally stored in one of following 3 places:
1. Perfetto (i.e. data is handed directly to perfetto)
2. Managed implictly by timemory and accessed as needed
3. Thread-local data
In general, only instrumentation for relatively simple data is directly passed to Perfetto and/or timemory during runtime.
For example, the callbacks from binary instrumentation, user API instrumentation, and roctracer directly invoke
calls to Perfetto and/or timemory's storage model. Otherwise, the data is stored by omnitrace in the thread-data model
which is more persistent than simply using `thread_local` static data (which is problematic because the data gets deleted
when a thread terminates).
### Thread Identification
Each CPU thread is assigned two integral identifiers. One identifier is simply an atomic increment everytime a new thread is created
(called `internal_value`).
The other identifier tries to account for the fact that OmniTrace, Perfetto, ROCm, etc. start background threads and for these threads
(called `sequent_value`). When a thread is created as a byproduct of OmniTrace, the index is offset by a large value. This serves
two purposes: (1) accessing the data for threads created by the user is closer in memory and (2) when log messages are printed,
the index more-or-less correlates to the order of thread creation to the user's knowledge.
The `sequent_value` is typically the one used to access the thread-data.
### Thread-Data Class
Currently, most thread data is effectively stored in a static `std::array<std::unique_ptr<T>, OMNITRACE_MAX_THREADS>` instance.
`OMNITRACE_MAX_THREADS` is a value defined a compile-time and set to 2048 for release builds. During finalization,
omnitrace iterates over all the thread-data and then transforms that data into something that is passed to perfetto and/or timemory.
The downside of the current model is that if the user exceeds `OMNITRACE_MAX_THREADS`, omnitrace segfaults. To fix this issue,
a new model is being adopted which has all the benefits of this model but permits dynamic expansion.
## Sampling Model
The general structure for the sampling is within timemory (`source/timemory/sampling`). Currently, all sampling is done per-thread
via POSIX timers. Omnitrace supports using a realtime timer and a CPU-time timer. Both have adjustable frequencies, delays, and durations.
By default, only CPU-time sampling is enabled. Initial settings are inherited from the settings starting with `OMNITRACE_SAMPLING_`.
For each type of timer, there exists timer-specific settings that can be used to override the common/inherited settings for that timer
specifically. For the CPU-time sampler, these settings start with `OMNITRACE_SAMPLING_CPUTIME` and `OMNITRACE_SAMPLING_REALTIME` for
the realtime sampler. For example, `OMNITRACE_SAMPLING_FREQ=500` initially sets the sampling frequency to 500 interrupts per second
(based on their clock). Settings `OMNITRACE_SAMPLING_REALTIME_FREQ=10` will lower the sampling frequency for the realtime sampler
to 10 interrupts per second of realtime.
The omnitrace-specific implementation can be found in [source/lib/omnitrace/library/sampling.cpp](https://github.com/AMDResearch/omnitrace/blob/main/source/lib/omnitrace/library/sampling.cpp).
Within [sampling.cpp](https://github.com/AMDResearch/omnitrace/blob/main/source/lib/omnitrace/library/sampling.cpp), you will a bundle of 3 sampling components:
`backtrace_timestamp`, `backtrace`, and `backtrace_metrics`.
The first component [backtrace_timestamp](https://github.com/AMDResearch/omnitrace/blob/main/source/lib/omnitrace/library/components/backtrace_timestamp.hpp) simply
records the wall-clock time of the sample.
The second component [backtrace](https://github.com/AMDResearch/omnitrace/blob/main/source/lib/omnitrace/library/components/backtrace.hpp) records the call-stack via libunwind.
The last component [backtrace_metrics](https://github.com/AMDResearch/omnitrace/blob/main/source/lib/omnitrace/library/components/backtrace_metrics.hpp) is responsible for recording the
metrics for that sample, e.g. peak RSS, HW counters, etc. These 3 components are bundled together in a tuple-like struct (e.g. `tuple<backtrace_timestamp, backtrace, backtrace_metrics>`)
a buffer of at least 1024 instances of this tuple are mmap'ed per-thread. When this buffer is full, before taking the next sample, the sampler will hand the buffer
off to it's allocator thread and mmap a new buffer. The allocator thread takes this data and either dynamically stores it in memory or writes it to a file depending on the value of `OMNITRACE_USE_TEMPORARY_FILES`.
This schema avoids all allocations in the signal handler, allows the data to grow dynamically, avoid potentially slow I/O within the signal handler, and also enables the capability to avoid I/O altogether.
The maximum number of samplers handled by each allocator is governed by the setting `OMNITRACE_SAMPLING_ALLOCATOR_SIZE` setting (the default is 8) -- whenever an allocator has reached it's limit,
a new internal thread is created to handle the new samplers.
## Time-Window Constraint Model
Recently with the introduction of tracing delay/duration/etc., the [constraint namespace](https://github.com/AMDResearch/omnitrace/blob/main/source/lib/core/constraint.hpp)
was introduced to improve the management of delays and/or duration limits of data collection. The `spec` class takes a clock identifier, a delay value, a duration value, and an
integer indicating how many times to repeat the delay + duration. Thus, it is possible to perform tasks such as periodically enabling tracing for brief periods
of time in between long periods without data collection during the application, e.g. `OMNITRACE_TRACE_PERIODS = realtime:10:1:5 process_cputime:10:2:20` would enable
five periods of no data collection for 10 seconds of realtime followed by 1 second of data collection + twenty periods of no data collection for 10 seconds
of process CPU time followed by 2 CPU-time seconds of data collection.
Eventually, the goal is have all subsets of data collection which currently support more rudimentary models of time window constraints, such as process sampling and causal profiling,
to be migrated to this model.
+1
Dosyayı Görüntüle
@@ -20,4 +20,5 @@
user_api
python
youtube
development
```
+43 -12
Dosyayı Görüntüle
@@ -53,6 +53,9 @@
#include <stdexcept>
#include <unordered_map>
#define OMNITRACE_PYTHON_VERSION \
((10000 * PY_MAJOR_VERSION) + (100 * PY_MINOR_VERSION) + PY_MICRO_VERSION)
namespace pyomnitrace
{
namespace pyprofile
@@ -260,10 +263,34 @@ get_config()
return *_tl_instance;
}
//
int32_t
get_depth(PyFrameObject* frame)
int
get_frame_lineno(PyFrameObject* frame)
{
return (frame->f_back) ? (get_depth(frame->f_back) + 1) : 0;
#if OMNITRACE_PYTHON_VERSION >= 31100
return PyFrame_GetLineNumber(frame);
#else
return frame->f_lineno;
#endif
}
//
int
get_frame_lasti(PyFrameObject* frame)
{
#if OMNITRACE_PYTHON_VERSION >= 31100
return PyFrame_GetLasti(frame);
#else
return frame->f_lasti;
#endif
}
//
auto
get_frame_code(PyFrameObject* frame)
{
#if OMNITRACE_PYTHON_VERSION >= 31100
return PyFrame_GetCode(frame);
#else
return frame->f_code;
#endif
}
//
void
@@ -364,9 +391,9 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
}
// append the line number
if(_config.include_line && _config.include_filename)
_funcname.append(TIMEMORY_JOIN("", ':', frame->f_lineno, ']'));
_funcname.append(TIMEMORY_JOIN("", ':', get_frame_lineno(frame), ']'));
else if(_config.include_line)
_funcname.append(TIMEMORY_JOIN("", ':', frame->f_lineno));
_funcname.append(TIMEMORY_JOIN("", ':', get_frame_lineno(frame)));
else if(_config.include_filename)
_funcname += "]";
return _funcname;
@@ -386,7 +413,7 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
auto& _only_funcs = _config.restrict_functions;
auto& _incl_funcs = _config.include_functions;
auto& _skip_funcs = _config.exclude_functions;
auto _func = py::cast<std::string>(frame->f_code->co_name);
auto _func = py::cast<std::string>(get_frame_code(frame)->co_name);
if(!_only_funcs.empty())
{
@@ -419,7 +446,7 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
auto& _only_files = _config.restrict_filenames;
auto& _incl_files = _config.include_filenames;
auto& _skip_files = _config.exclude_filenames;
auto _full = py::cast<std::string>(frame->f_code->co_filename);
auto _full = py::cast<std::string>(get_frame_code(frame)->co_filename);
auto _file = (_full.find('/') != std::string::npos)
? _full.substr(_full.find_last_of('/') + 1)
: _full;
@@ -470,14 +497,18 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
// start function
auto _profiler_call = [&]() {
int _lineno = 0;
int _lasti = 0;
if(_annotate)
{
_lineno = get_frame_lineno(frame);
_lasti = get_frame_lasti(frame);
_config.annotations.at(0).value = const_cast<char*>(_full.c_str());
_config.annotations.at(1).value = &frame->f_lineno;
_config.annotations.at(2).value = &frame->f_lasti;
_config.annotations.at(3).value = &frame->f_code->co_argcount;
_config.annotations.at(4).value = &frame->f_code->co_nlocals;
_config.annotations.at(5).value = &frame->f_code->co_stacksize;
_config.annotations.at(1).value = &_lineno;
_config.annotations.at(2).value = &_lasti;
_config.annotations.at(3).value = &get_frame_code(frame)->co_argcount;
_config.annotations.at(4).value = &get_frame_code(frame)->co_nlocals;
_config.annotations.at(5).value = &get_frame_code(frame)->co_stacksize;
}
_config.records.emplace_back([&_label_ref, _annotate]() {