diff --git a/.github/workflows/cpack.yml b/.github/workflows/cpack.yml index 0541095935..042824a9eb 100644 --- a/.github/workflows/cpack.yml +++ b/.github/workflows/cpack.yml @@ -161,16 +161,11 @@ jobs: echo "CI_SCRIPT_ARGS=--rocm +python" >> $GITHUB_ENV - name: Build Base Container - timeout-minutes: 20 - uses: nick-fields/retry@v2 - with: - retry_wait_seconds: 120 - timeout_minutes: 20 - max_attempts: 5 - command: | - pushd docker - ./build-docker.sh --distro ${{ matrix.os-distro }} --versions ${{ matrix.os-version }} --rocm-versions ${{ matrix.rocm-version }} - popd + timeout-minutes: 30 + run: | + pushd docker + ./build-docker.sh --distro ${{ matrix.os-distro }} --versions ${{ matrix.os-version }} --rocm-versions ${{ matrix.rocm-version }} + popd - name: Build Release timeout-minutes: 150 diff --git a/.github/workflows/opensuse.yml b/.github/workflows/opensuse.yml index edac4c78f9..578533f309 100644 --- a/.github/workflows/opensuse.yml +++ b/.github/workflows/opensuse.yml @@ -20,6 +20,7 @@ concurrency: env: OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON + OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp" jobs: opensuse: @@ -37,11 +38,11 @@ jobs: - uses: actions/checkout@v3 - name: Install Packages - timeout-minutes: 10 + timeout-minutes: 25 uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 command: | for i in 6 7 8 9 10; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done diff --git a/.github/workflows/ubuntu-bionic.yml b/.github/workflows/ubuntu-bionic.yml index 7f8de83cc2..07d8eb72e1 100644 --- a/.github/workflows/ubuntu-bionic.yml +++ b/.github/workflows/ubuntu-bionic.yml @@ -23,6 +23,7 @@ env: OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON GIT_DISCOVERY_ACROSS_FILESYSTEM: 1 + OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp" jobs: ubuntu-bionic: @@ -37,7 +38,7 @@ jobs: steps: - name: Patch Git - timeout-minutes: 10 + timeout-minutes: 25 run: | apt-get update apt-get install -y software-properties-common @@ -51,11 +52,11 @@ jobs: submodules: recursive - name: Install Packages - timeout-minutes: 10 + timeout-minutes: 25 uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 command: | apt-get update && diff --git a/.github/workflows/ubuntu-focal.yml b/.github/workflows/ubuntu-focal.yml index eb1168bfe5..b14d497138 100644 --- a/.github/workflows/ubuntu-focal.yml +++ b/.github/workflows/ubuntu-focal.yml @@ -20,6 +20,7 @@ concurrency: env: OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON + OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp" jobs: ubuntu-focal-external: @@ -67,11 +68,11 @@ jobs: - uses: actions/checkout@v3 - name: Install Packages - timeout-minutes: 10 + timeout-minutes: 25 uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 command: | apt-get update && @@ -244,11 +245,11 @@ jobs: - uses: actions/checkout@v3 - name: Install Packages - timeout-minutes: 10 + timeout-minutes: 25 uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 command: | apt-get update && @@ -408,11 +409,11 @@ jobs: - uses: actions/checkout@v3 - name: Install Packages - timeout-minutes: 10 + timeout-minutes: 25 uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 command: | sudo apt-get update && @@ -555,11 +556,11 @@ jobs: - uses: actions/checkout@v3 - name: Install Packages - timeout-minutes: 10 + timeout-minutes: 25 uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 command: | apt-get update && diff --git a/.github/workflows/ubuntu-jammy.yml b/.github/workflows/ubuntu-jammy.yml index d40563be2c..94ae96e86c 100644 --- a/.github/workflows/ubuntu-jammy.yml +++ b/.github/workflows/ubuntu-jammy.yml @@ -20,6 +20,7 @@ concurrency: env: OMNITRACE_VERBOSE: 1 OMNITRACE_CI: ON + OMNITRACE_TMPDIR: "%env{PWD}%/testing-tmp" jobs: ubuntu-jammy-external: @@ -70,11 +71,11 @@ jobs: - uses: actions/checkout@v3 - name: Install Packages - timeout-minutes: 10 + timeout-minutes: 25 uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 command: | apt-get update && @@ -87,12 +88,12 @@ jobs: for i in 6 7 8 9 10; do /opt/conda/envs/py3.${i}/bin/python -m pip install numpy perfetto dataclasses; done - name: Install ROCm Packages - timeout-minutes: 10 + timeout-minutes: 25 if: ${{ matrix.rocm-version != '0.0' }} uses: nick-fields/retry@v2 with: retry_wait_seconds: 30 - timeout_minutes: 10 + timeout_minutes: 25 max_attempts: 5 shell: bash command: | diff --git a/docker/build-docker.sh b/docker/build-docker.sh index 4fd2fdc5db..02b1557f61 100755 --- a/docker/build-docker.sh +++ b/docker/build-docker.sh @@ -7,6 +7,7 @@ : ${PYTHON_VERSIONS:="6 7 8 9 10"} : ${BUILD_CI:=""} : ${PUSH:=0} +: ${RETRY:=3} set -e @@ -32,7 +33,9 @@ usage() print_default_option versions "[VERSION] [VERSION...]" "Ubuntu or OpenSUSE release" "${VERSIONS}" print_default_option rocm-versions "[VERSION] [VERSION...]" "ROCm versions" "${ROCM_VERSIONS}" print_default_option python-versions "[VERSION] [VERSION...]" "Python 3 minor releases" "${PYTHON_VERSIONS}" - print_default_option user "[USERNAME]" "DockerHub username" "${USER}" + print_default_option "user -u" "[USERNAME]" "DockerHub username" "${USER}" + print_default_option "retry -r" "[N]" "Number of attempts to build (to account for network errors" "${RETRY}" + print_default_option push "" "Push the image to Dockerhub" "" #print_default_option lto "[on|off]" "Enable LTO" "${LTO}" } @@ -46,7 +49,31 @@ send-error() verbose-run() { echo -e "\n### Executing \"${@}\"... ###\n" - eval $@ + eval "${@}" +} + +verbose-build() +{ + echo -e "\n### Executing \"${@}\" a maximum of ${RETRY} times... ###\n" + for i in $(seq 1 1 ${RETRY}) + do + set +e + eval "${@}" + local RETC=$? + set -e + if [ "${RETC}" -eq 0 ]; then + break + else + echo -en "\n### Command failed with error code ${RETC}... " + if [ "${i}" -ne "${RETRY}" ]; then + echo -e "Retrying... ###\n" + sleep 3 + else + echo -e "Exiting... ###\n" + exit ${RETC} + fi + fi + done } reset-last() @@ -92,6 +119,11 @@ do --push) PUSH=1 ;; + --retry|-r) + shift + RETRY=${1} + reset-last + ;; "--*") send-error "Unsupported argument at position $((${n} + 1)) :: ${1}" ;; @@ -149,7 +181,7 @@ do *) ;; esac - verbose-run 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 . -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}" = "centos" ]; then case "${VERSION}" in 7) @@ -192,7 +224,7 @@ do send-error "Unsupported combination :: ${DISTRO}-${VERSION} + ROCm ${ROCM_VERSION}" ;; esac - verbose-run docker build . -f ${DOCKER_FILE} --tag ${CONTAINER} --build-arg DISTRO=${DISTRO} --build-arg VERSION=${VERSION} --build-arg ROCM_VERSION=${ROCM_VERSION} --build-arg TOOLSET_VERSION=${TOOLSET_VERSION} --build-arg AMDGPU_RPM=${ROCM_RPM} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\" + 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 TOOLSET_VERSION=${TOOLSET_VERSION} --build-arg AMDGPU_RPM=${ROCM_RPM} --build-arg PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\" elif [ "${DISTRO}" = "opensuse" ]; then case "${VERSION}" in 15.*) @@ -225,7 +257,7 @@ do send-error "Unsupported combination :: ${DISTRO}-${VERSION} + ROCm ${ROCM_VERSION}" ;; esac - verbose-run 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 PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\" + 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 PYTHON_VERSIONS=\"${PYTHON_VERSIONS}\" fi if [ "${PUSH}" -ne 0 ]; then docker push ${CONTAINER} diff --git a/scripts/run-ci.py b/scripts/run-ci.py index cbb8c83cce..565c23075d 100755 --- a/scripts/run-ci.py +++ b/scripts/run-ci.py @@ -4,6 +4,7 @@ import os import re import sys +import glob import socket import shutil import argparse @@ -240,15 +241,34 @@ if __name__ == "__main__": for itr in args.stages: dashboard_args.append(f"{args.mode}{itr}") - run( - [CTEST_CMD] - + dashboard_args - + [ - "-S", - os.path.join(args.binary_dir, "dashboard.cmake"), - "--output-on-failure", - "-VV", - ] - + ctest_args, - check=True, - ) + try: + run( + [CTEST_CMD] + + dashboard_args + + [ + "-S", + os.path.join(args.binary_dir, "dashboard.cmake"), + "--output-on-failure", + "-V", + ] + + ctest_args, + check=True, + ) + finally: + if "-VV" not in ctest_args: + for file in glob.glob( + os.path.join(args.binary_dir, "Testing/**"), recursive=True + ): + if not os.path.isfile(file): + continue + print(f"\n\n\n###### Reading {file}... ######\n\n\n") + with open(file, "r") as inpf: + fdata = inpf.read() + if "LastTest" not in file and "Coverage" not in file: + print(fdata) + oname = os.path.basename(file) + if oname.endswith(".log"): + oname += ".log" + with open(os.path.join(args.binary_dir, oname), "w") as outf: + print(f"\n\n###### Writing {oname}... ######\n\n") + outf.write(fdata) diff --git a/source/lib/omnitrace/library/config.cpp b/source/lib/omnitrace/library/config.cpp index 808da6691b..a30f773816 100644 --- a/source/lib/omnitrace/library/config.cpp +++ b/source/lib/omnitrace/library/config.cpp @@ -576,6 +576,12 @@ configure_settings(bool _init) _config->get("collapse_processes"), "perfetto", "data", "advanced"); + OMNITRACE_CONFIG_SETTING( + bool, "OMNITRACE_PERFETTO_ROCTRACER_PER_STREAM", + "Separate roctracer GPU side traces (copies, kernels) into separate " + "tracks based on the stream they're enqueued into", + true, "perfetto", "roctracer", "rocm", "advanced"); + OMNITRACE_CONFIG_SETTING( std::string, "OMNITRACE_PERFETTO_FILL_POLICY", "Behavior when perfetto buffer is full. 'discard' will ignore new entries, " @@ -1518,6 +1524,17 @@ get_use_roctracer() #endif } +bool +get_perfetto_roctracer_per_stream() +{ +#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0 + static auto _v = get_config()->find("OMNITRACE_PERFETTO_ROCTRACER_PER_STREAM"); + return static_cast&>(*_v->second).get(); +#else + return false; +#endif +} + bool get_use_rocprofiler() { diff --git a/source/lib/omnitrace/library/config.hpp b/source/lib/omnitrace/library/config.hpp index 886c32da21..fc1f8fa810 100644 --- a/source/lib/omnitrace/library/config.hpp +++ b/source/lib/omnitrace/library/config.hpp @@ -266,6 +266,9 @@ get_backend(); std::string get_perfetto_output_filename(); +bool +get_perfetto_roctracer_per_stream() OMNITRACE_HOT; + int64_t get_critical_trace_count(); diff --git a/source/lib/omnitrace/library/roctracer.cpp b/source/lib/omnitrace/library/roctracer.cpp index a50b6337fd..5e5763048b 100644 --- a/source/lib/omnitrace/library/roctracer.cpp +++ b/source/lib/omnitrace/library/roctracer.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -118,7 +119,7 @@ get_roctracer_tid_data() return _v; } -using cid_tuple_t = std::tuple; +using cid_tuple_t = std::tuple; struct cid_data : cid_tuple_t { using cid_tuple_t::cid_tuple_t; @@ -128,10 +129,12 @@ struct cid_data : cid_tuple_t auto& cid() { return std::get<0>(*this); } auto& pcid() { return std::get<1>(*this); } auto& depth() { return std::get<2>(*this); } + auto& queue() { return std::get<3>(*this); } auto cid() const { return std::get<0>(*this); } auto pcid() const { return std::get<1>(*this); } auto depth() const { return std::get<2>(*this); } + auto queue() const { return std::get<3>(*this); } }; auto& @@ -344,24 +347,13 @@ hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* } void -hsa_activity_callback(uint32_t op, activity_record_t* record, void* arg) +hsa_activity_callback(uint32_t op, const activity_record_t* record, void* arg) { if(get_state() != State::Active || !trait::runtime_enabled::get()) return; OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal); - static thread_local std::once_flag _once{}; - std::call_once(_once, []() { - threading::offset_this_id(true); - if(threading::get_id() != 0) - { - sampling::block_signals(); - threading::set_thread_name("roctracer.hsa"); - sampling::shutdown(); - } - }); - auto&& _protect = comp::roctracer::protect_flush_activity(); (void) _protect; @@ -760,8 +752,8 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* critical_trace::add_hash_id(op_name), _depth); } - get_roctracer_cid_data(_tid).emplace(_corr_id, - cid_data{ _cid, _parent_cid, _depth }); + get_roctracer_cid_data(_tid).emplace( + _corr_id, cid_data{ _cid, _parent_cid, _depth, _queue }); hip_exec_activity_callbacks(_tid); } @@ -769,7 +761,8 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* { hip_exec_activity_callbacks(_tid); - std::tie(_cid, _parent_cid, _depth) = get_roctracer_cid_data(_tid).at(_corr_id); + std::tie(_cid, _parent_cid, _depth, std::ignore) = + get_roctracer_cid_data(_tid).at(_corr_id); if(get_use_perfetto()) { @@ -814,24 +807,13 @@ hip_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* // Activity tracing callback void -hip_activity_callback(const char* begin, const char* end, void*) +hip_activity_callback(const char* begin, const char* end, void* arg) { if(get_state() != State::Active || !trait::runtime_enabled::get()) return; OMNITRACE_SCOPED_THREAD_STATE(ThreadState::Internal); - static thread_local std::once_flag _once{}; - std::call_once(_once, []() { - threading::offset_this_id(true); - if(threading::get_id() != 0) - { - sampling::block_signals(); - threading::set_thread_name("roctracer.hip"); - sampling::shutdown(); - } - }); - auto&& _protect = comp::roctracer::protect_flush_activity(); (void) _protect; @@ -859,8 +841,12 @@ hip_activity_callback(const char* begin, const char* end, void*) assert(HIP_OP_ID_DISPATCH == 0); assert(HIP_OP_ID_COPY == 1); assert(HIP_OP_ID_BARRIER == 2); - assert(record->domain == ACTIVITY_DOMAIN_HIP_OPS); + if(record->domain == ACTIVITY_DOMAIN_HSA_OPS) + { + hsa_activity_callback(record->op, record, arg); + continue; + } if(record->domain != ACTIVITY_DOMAIN_HIP_OPS) continue; if(record->op > HIP_OP_ID_BARRIER) continue; @@ -880,6 +866,7 @@ hip_activity_callback(const char* begin, const char* end, void*) uint64_t _pcid = 0; // parent corr_id int32_t _devid = record->device_id; // device id int64_t _queid = record->queue_id; // queue id + uintptr_t _queue = 0; // Host queue (stream) auto _laps = _indexes[_corr_id]++; // see note #1 const char* _name = nullptr; bool _found = false; @@ -903,7 +890,7 @@ hip_activity_callback(const char* begin, const char* end, void*) { auto& _cids = get_roctracer_cid_data(_tid); if(_cids.find(_corr_id) != _cids.end()) - std::tie(_cid, _pcid, _depth) = _cids.at(_corr_id); + std::tie(_cid, _pcid, _depth, _queue) = _cids.at(_corr_id); else { OMNITRACE_VERBOSE_F(3, @@ -922,7 +909,7 @@ hip_activity_callback(const char* begin, const char* end, void*) auto _verbose = []() { return get_verbose() >= 0 || get_debug(); }; static size_t _n = 0; static size_t _nmax = - get_env("OMNITRACE_ROCTRACER_DISCARD_INVALID", 10); + get_env("OMNITRACE_ROCTRACER_DISCARD_INVALID", 0); if(_nmax == 0) std::swap(_end_ns, _beg_ns); OMNITRACE_WARNING_IF_F( _n < _nmax && _verbose(), @@ -952,36 +939,36 @@ hip_activity_callback(const char* begin, const char* end, void*) if(_kernel_names.find(_name) == _kernel_names.end()) _kernel_names.emplace(_name, tim::demangle(_name)); + auto _track_desc = [](int32_t _device_id, int64_t _queue_id) { + if(config::get_perfetto_roctracer_per_stream()) + return JOIN("", "HIP Activity Device ", _device_id, ", Queue ", + _queue_id); + return JOIN("", "HIP Activity Device ", _device_id); + }; + + const auto _track = tracing::get_perfetto_track( + category::device_hip{}, _track_desc, _devid, + (get_perfetto_roctracer_per_stream()) ? _queid : 0); + assert(_end_ns >= _beg_ns); - tracing::push_perfetto_ts( - category::device_hip{}, _kernel_names.at(_name).c_str(), _beg_ns, + tracing::push_perfetto_track( + category::device_hip{}, _kernel_names.at(_name).c_str(), _track, _beg_ns, perfetto::Flow::ProcessScoped(_cid), [&](perfetto::EventContext ctx) { if(config::get_perfetto_annotations()) { tracing::add_perfetto_annotation(ctx, "begin_ns", _beg_ns); + tracing::add_perfetto_annotation(ctx, "end_ns", _end_ns); tracing::add_perfetto_annotation(ctx, "corr_id", _corr_id); tracing::add_perfetto_annotation(ctx, "device", _devid); tracing::add_perfetto_annotation(ctx, "queue", _queid); tracing::add_perfetto_annotation(ctx, "tid", _tid); + tracing::add_perfetto_annotation( + ctx, "stream", JOIN("", "0x", std::hex, _queue)); tracing::add_perfetto_annotation(ctx, "op", _op_id_names.at(record->op)); } }); - tracing::pop_perfetto_ts( - category::device_hip{}, "", _end_ns, [&](perfetto::EventContext ctx) { - if(config::get_perfetto_annotations()) - { - tracing::add_perfetto_annotation(ctx, "end_ns", _end_ns); - } - }); - // for some reason, this is necessary to make sure very last one ends - tracing::pop_perfetto_ts( - category::device_hip{}, "", _end_ns, [&](perfetto::EventContext ctx) { - if(config::get_perfetto_annotations()) - { - tracing::add_perfetto_annotation(ctx, "end_ns", _end_ns); - } - }); + tracing::pop_perfetto_track(category::device_hip{}, "", _track, _end_ns); } if(_critical_trace) @@ -1013,6 +1000,9 @@ hip_activity_callback(const char* begin, const char* end, void*) _async_ops->emplace_back(std::move(_func)); } } + + // ensures that all the updates are written + if(get_use_perfetto()) ::perfetto::TrackEvent::Flush(); } bool& diff --git a/source/lib/omnitrace/library/roctracer.hpp b/source/lib/omnitrace/library/roctracer.hpp index 3c8227dfa9..cf6085802e 100644 --- a/source/lib/omnitrace/library/roctracer.hpp +++ b/source/lib/omnitrace/library/roctracer.hpp @@ -57,7 +57,7 @@ void hsa_api_callback(uint32_t domain, uint32_t cid, const void* callback_data, void* arg); void -hsa_activity_callback(uint32_t op, activity_record_t* record, void* arg); +hsa_activity_callback(uint32_t op, const activity_record_t* record, void* arg); void hip_exec_activity_callbacks(int64_t _tid); diff --git a/source/lib/omnitrace/library/tracing.cpp b/source/lib/omnitrace/library/tracing.cpp index 0cd91099a3..514fe091db 100644 --- a/source/lib/omnitrace/library/tracing.cpp +++ b/source/lib/omnitrace/library/tracing.cpp @@ -43,6 +43,13 @@ get_perfetto_session() return _v; } +std::unordered_map& +get_perfetto_track_uuids() +{ + static thread_local auto _v = std::unordered_map{}; + return _v; +} + std::vector>& get_finalization_functions() { diff --git a/source/lib/omnitrace/library/tracing.hpp b/source/lib/omnitrace/library/tracing.hpp index 167e04abf7..4e822a2080 100644 --- a/source/lib/omnitrace/library/tracing.hpp +++ b/source/lib/omnitrace/library/tracing.hpp @@ -38,13 +38,22 @@ #include #include +#include +#include +#include +#include +#include #include +#include +#include +#include namespace omnitrace { namespace tracing { using interval_data_instances = thread_data>; +using hash_value_t = tim::hash_value_t; perfetto::TraceConfig& get_perfetto_config(); @@ -52,6 +61,54 @@ get_perfetto_config(); std::unique_ptr& get_perfetto_session(); +template +auto +get_perfetto_category_uuid(Args&&... _args) +{ + return tim::hash::get_combined_hash_id( + tim::hash::get_hash_id(JOIN('_', "omnitrace", trait::name::value)), + std::forward(_args)...); +} + +std::unordered_map& +get_perfetto_track_uuids(); + +template +auto +get_perfetto_track(CategoryT, FuncT&& _desc_generator, Args&&... _args) +{ + auto _uuid = get_perfetto_category_uuid(std::forward(_args)...); + auto& _track_uuids = get_perfetto_track_uuids(); + if(_track_uuids.find(_uuid) == _track_uuids.end()) + { + const auto _track = TrackT(_uuid); + auto _desc = _track.Serialize(); + + auto _name = std::forward(_desc_generator)(std::forward(_args)...); + _desc.set_name(_name); + ::perfetto::TrackEvent::SetTrackDescriptor(_track, _desc); + + OMNITRACE_VERBOSE_F(4, "[%s] Created %s(%zu) with description: \"%s\"\n", + trait::name::value, demangle().c_str(), + _uuid, _name.c_str()); + + _track_uuids.emplace(_uuid, _name); + } + + // guard this with ppdefs in addition to runtime check to avoid + // overhead of generating string during releases +#if defined(OMNITRACE_CI) && OMNITRACE_CI > 0 + auto _name = std::forward(_desc_generator)(std::forward(_args)...); + OMNITRACE_CI_THROW(_track_uuids.at(_uuid) != _name, + "Error! Multiple invocations of UUID %zu produced different " + "descriptions: \"%s\" and \"%s\"\n", + _uuid, _track_uuids.at(_uuid).c_str(), _name.c_str()); +#endif + + return TrackT(_uuid); +} + std::vector>& get_finalization_functions(); @@ -292,5 +349,24 @@ pop_perfetto_ts(CategoryT, const char*, uint64_t _ts, Args&&... args) TRACE_EVENT_END(trait::name::value, _ts, std::forward(args)...); } + +template +inline void +push_perfetto_track(CategoryT, const char* name, perfetto::Track _track, uint64_t _ts, + Args&&... args) +{ + TRACE_EVENT_BEGIN(trait::name::value, perfetto::StaticString(name), _track, + _ts, std::forward(args)...); +} + +template +inline void +pop_perfetto_track(CategoryT, const char*, perfetto::Track _track, uint64_t _ts, + Args&&... args) +{ + TRACE_EVENT_END(trait::name::value, _track, _ts, + std::forward(args)...); +} + } // namespace tracing } // namespace omnitrace