diff --git a/projects/rocprofiler-systems/.gitmodules b/projects/rocprofiler-systems/.gitmodules index 89b551a769..c70439d0ab 100644 --- a/projects/rocprofiler-systems/.gitmodules +++ b/projects/rocprofiler-systems/.gitmodules @@ -24,3 +24,6 @@ [submodule "external/pybind11"] path = external/pybind11 url = https://github.com/jrmadsen/pybind11.git +[submodule "external/sqlite"] + path = external/sqlite + url = https://github.com/sqlite/sqlite.git diff --git a/projects/rocprofiler-systems/CHANGELOG.md b/projects/rocprofiler-systems/CHANGELOG.md index d849c5628e..88de0f6a25 100644 --- a/projects/rocprofiler-systems/CHANGELOG.md +++ b/projects/rocprofiler-systems/CHANGELOG.md @@ -10,6 +10,9 @@ Full documentation for ROCm Systems Profiler is available at [https://rocm.docs. - How-to document for VCN and JPEG activity sampling and tracing. - Support for tracing Fortran applications. - Support for tracing MPI API in Fortran. +- Initial support for rocPD database output with the `ROCPROFSYS_USE_ROCPD` configuration setting. +- By default, group "kernel dispatch" and "memory copy" events by HIP stream ID in Perfetto traces. + - Add the "ROCPROFSYS_ROCM_GROUP_BY_QUEUE" configuration setting to group events by queue, instead. ### Changed diff --git a/projects/rocprofiler-systems/CMakeLists.txt b/projects/rocprofiler-systems/CMakeLists.txt index 04bb87d551..605b070f0d 100644 --- a/projects/rocprofiler-systems/CMakeLists.txt +++ b/projects/rocprofiler-systems/CMakeLists.txt @@ -221,6 +221,12 @@ rocprofiler_systems_add_option(ROCPROFSYS_BUILD_CODECOV "Build for code coverage rocprofiler_systems_add_option(ROCPROFSYS_INSTALL_PERFETTO_TOOLS "Install perfetto tools (i.e. traced, perfetto, etc.)" OFF ) +rocprofiler_systems_add_option(ROCPROFILER_BUILD_SQLITE3 + "Enable building sqlite3 library internally" OFF +) +rocprofiler_systems_add_option(ROCPROFSYS_ENABLE_BENCHMARK + "Enable performance benchmarking capabilities for the project" OFF +) if(ROCPROFSYS_USE_PAPI) rocprofiler_systems_add_option(ROCPROFSYS_BUILD_PAPI "Build PAPI from submodule" ON) @@ -328,6 +334,10 @@ if(ROCPROFSYS_BUILD_TESTING OR "$ENV{ROCPROFSYS_CI}" MATCHES "[1-9]+|ON|on|y|yes include(CTest) endif() +if(ROCPROFSYS_ENABLE_BENCHMARK) + add_compile_definitions(-DROCPROFSYS_USE_BENCHMARK=1) +endif() + # ------------------------------------------------------------------------------# # # library and executables diff --git a/projects/rocprofiler-systems/VERSION b/projects/rocprofiler-systems/VERSION index 9084fa2f71..26aaba0e86 100644 --- a/projects/rocprofiler-systems/VERSION +++ b/projects/rocprofiler-systems/VERSION @@ -1 +1 @@ -1.1.0 +1.2.0 diff --git a/projects/rocprofiler-systems/cmake/Packages.cmake b/projects/rocprofiler-systems/cmake/Packages.cmake index f24c2c653a..fd9b302a0b 100644 --- a/projects/rocprofiler-systems/cmake/Packages.cmake +++ b/projects/rocprofiler-systems/cmake/Packages.cmake @@ -53,6 +53,9 @@ rocprofiler_systems_add_interface_library(rocprofiler-systems-python rocprofiler_systems_add_interface_library(rocprofiler-systems-perfetto "Enables Perfetto support" ) +rocprofiler_systems_add_interface_library(rocprofiler-systems-sqlite3 + "Use SQLite3 for rocpd data storage" +) rocprofiler_systems_add_interface_library(rocprofiler-systems-timemory "Provides timemory libraries" ) @@ -532,6 +535,14 @@ rocprofiler_systems_checkout_git_submodule( include(Perfetto) +# ----------------------------------------------------------------------------------------# +# +# SQLite3 +# +# ----------------------------------------------------------------------------------------# + +include(SQLite3) + # ----------------------------------------------------------------------------------------# # # ELFIO diff --git a/projects/rocprofiler-systems/cmake/SQLite3.cmake b/projects/rocprofiler-systems/cmake/SQLite3.cmake new file mode 100644 index 0000000000..44669c3280 --- /dev/null +++ b/projects/rocprofiler-systems/cmake/SQLite3.cmake @@ -0,0 +1,48 @@ +include_guard(GLOBAL) + +if(ROCPROFILER_BUILD_SQLITE3) + message(STATUS "Building SQLite3 from source!") + execute_process( + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/external/sqlite + ) + # checkout submodule if not already checked out or clone repo if no .gitmodules file + rocprofiler_systems_checkout_git_submodule( + RELATIVE_PATH external/sqlite + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + TEST_FILE configure + REPO_URL https://github.com/sqlite/sqlite.git + REPO_BRANCH "version-3.45.3" + ) + + find_program(MAKE_COMMAND NAMES make gmake PATH_SUFFIXES bin REQUIRED) + + include(ExternalProject) + ExternalProject_Add( + rocprofiler-systems-sqlite-build + PREFIX ${PROJECT_BINARY_DIR}/external/sqlite/build + SOURCE_DIR ${PROJECT_SOURCE_DIR}/external/sqlite + BUILD_IN_SOURCE 0 + CONFIGURE_COMMAND + /configure --prefix=${PROJECT_BINARY_DIR}/external/sqlite/install + --libdir=${PROJECT_BINARY_DIR}/external/sqlite/install/lib --disable-shared + --with-tempstore=yes --enable-all --disable-tcl CFLAGS=-O3\ -g1 + BUILD_COMMAND ${MAKE_COMMAND} install -s + INSTALL_COMMAND "" + ) + + target_link_libraries( + rocprofiler-systems-sqlite3 + INTERFACE + $ + ) + target_include_directories( + rocprofiler-systems-sqlite3 + SYSTEM + INTERFACE $ + ) + add_dependencies(rocprofiler-systems-sqlite3 rocprofiler-systems-sqlite-build) +else() + message(STATUS "Using system SQLite3 library") + find_package(SQLite3 REQUIRED) + target_link_libraries(rocprofiler-systems-sqlite3 INTERFACE SQLite::SQLite3) +endif() diff --git a/projects/rocprofiler-systems/docs/how-to/configuring-runtime-options.rst b/projects/rocprofiler-systems/docs/how-to/configuring-runtime-options.rst index 59c1b9099e..9e540243f0 100644 --- a/projects/rocprofiler-systems/docs/how-to/configuring-runtime-options.rst +++ b/projects/rocprofiler-systems/docs/how-to/configuring-runtime-options.rst @@ -217,6 +217,14 @@ The following example: ROCPROFSYS_ROCM_EVENTS = GPUBusy SQ_WAVES:device=0 SQ_INSTS_VALU:device=1 +ROCPROFSYS_ROCM_GROUP_BY_QUEUE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, Perfetto trace will show the HIP streams to which kernel +and memory copy operations submitted. With the +``ROCPROFSYS_ROCM_GROUP_BY_QUEUE=ON`` setting, the trace will display HSA queues +to which these kernel and memory operations were submitted. + Exploring GPU Metrics --------------------- diff --git a/projects/rocprofiler-systems/docs/reference/development-guide.rst b/projects/rocprofiler-systems/docs/reference/development-guide.rst index 6003021484..5bc7385641 100644 --- a/projects/rocprofiler-systems/docs/reference/development-guide.rst +++ b/projects/rocprofiler-systems/docs/reference/development-guide.rst @@ -32,7 +32,7 @@ rocprof-sys-sample: `source/bin/rocprof-sys-sample `` and a modified environment -rocprof-sys-casual: `source/bin/rocprof-sys-causal `_ +rocprof-sys-causal: `source/bin/rocprof-sys-causal `_ --------------------------------------------------------------------------------------------------------------------------------------------------- When there is exactly one causal profiling configuration variant (which enables debugging), diff --git a/projects/rocprofiler-systems/examples/jpegdecode/CMakeLists.txt b/projects/rocprofiler-systems/examples/jpegdecode/CMakeLists.txt index e92c8f5e34..bc1eed1d55 100644 --- a/projects/rocprofiler-systems/examples/jpegdecode/CMakeLists.txt +++ b/projects/rocprofiler-systems/examples/jpegdecode/CMakeLists.txt @@ -60,7 +60,47 @@ if(ROCPROFSYS_DISABLE_EXAMPLES) endif() endif() -find_package(rocjpeg QUIET) +# find rocJPEG - library and headers +find_path( + rocjpeg_ROOT_DIR + NAMES include/rocjpeg/rocjpeg.h + HINTS ${ROCmVersion_DIR} ${ROCM_PATH} + PATHS ${ROCmVersion_DIR} ${ROCM_PATH} +) + +mark_as_advanced(rocjpeg_ROOT_DIR) + +find_path( + rocjpeg_INCLUDE_DIR + NAMES rocjpeg/rocjpeg.h + HINTS ${rocjpeg_ROOT_DIR} + PATHS ${rocjpeg_ROOT_DIR} + PATH_SUFFIXES include +) + +find_library( + rocjpeg_LIBRARY + NAMES rocjpeg + HINTS ${rocjpeg_ROOT_DIR} + PATHS ${rocjpeg_ROOT_DIR} + PATH_SUFFIXES lib +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + rocjpeg + FOUND_VAR rocjpeg_FOUND + REQUIRED_VARS rocjpeg_INCLUDE_DIR rocjpeg_LIBRARY +) + +if(rocjpeg_FOUND) + if(NOT TARGET rocjpeg::rocjpeg) + add_library(rocjpeg::rocjpeg INTERFACE IMPORTED) + target_link_libraries(rocjpeg::rocjpeg INTERFACE ${rocjpeg_LIBRARY}) + target_include_directories(rocjpeg::rocjpeg INTERFACE ${rocjpeg_INCLUDE_DIR}) + endif() +endif() + find_package(rocprofiler-register QUIET) # Copy image files to build directory @@ -107,7 +147,7 @@ if(HIP_FOUND AND rocjpeg_FOUND AND Threads_FOUND AND rocprofiler-register_FOUND) set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} rocprofiler-register::rocprofiler-register) # rocJPEG - message(STATUS "RocJPEG library found: ${rocjpeg_LIBRARIES}") + message(STATUS "RocJPEG library found: ${rocjpeg_LIBRARY}") include_directories(${rocjpeg_INCLUDE_DIR}) set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} rocjpeg::rocjpeg) list(APPEND SOURCES ${PROJECT_SOURCE_DIR} jpegdecodeperf.cpp) @@ -136,11 +176,11 @@ else() message(WARNING "-- ERROR!: rocJPEG Not Found! - please install rocJPEG!") endif() if(NOT Threads_FOUND) - message(FATAL_ERROR "-- ERROR!: Threads Not Found! - please insatll Threads!") + message(WARNING "-- ERROR!: Threads Not Found! - please install Threads!") endif() if(NOT rocprofiler-register_FOUND) message( - FATAL_ERROR + WARNING "-- ERROR!: rocprofiler-register Not Found! - please install rocprofiler-register!" ) endif() diff --git a/projects/rocprofiler-systems/examples/videodecode/CMakeLists.txt b/projects/rocprofiler-systems/examples/videodecode/CMakeLists.txt index f8e8fcbf09..4e4a5bb5a7 100644 --- a/projects/rocprofiler-systems/examples/videodecode/CMakeLists.txt +++ b/projects/rocprofiler-systems/examples/videodecode/CMakeLists.txt @@ -54,8 +54,48 @@ function(videodecode_message _MSG_TYPE) endfunction() # Find RocDecode -find_package(rocdecode QUIET) -if(NOT rocdecode_FOUND) +find_path( + rocdecode_ROOT_DIR + NAMES include/rocdecode/rocdecode.h + HINTS ${ROCmVersion_DIR} ${ROCM_PATH} + PATHS ${ROCmVersion_DIR} ${ROCM_PATH} +) + +mark_as_advanced(rocdecode_ROOT_DIR) + +find_path( + rocdecode_INCLUDE_DIR + NAMES rocdecode/rocdecode.h + HINTS ${rocdecode_ROOT_DIR} + PATHS ${rocdecode_ROOT_DIR} + PATH_SUFFIXES include +) + +find_library( + rocdecode_LIBRARY + NAMES rocdecode + HINTS ${rocdecode_ROOT_DIR} + PATHS ${rocdecode_ROOT_DIR} + PATH_SUFFIXES lib +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + rocdecode + FOUND_VAR rocdecode_FOUND + REQUIRED_VARS rocdecode_INCLUDE_DIR rocdecode_LIBRARY +) + +if(rocdecode_FOUND) + if(NOT TARGET rocdecode::rocdecode) + add_library(rocdecode::rocdecode INTERFACE IMPORTED) + target_link_libraries(rocdecode::rocdecode INTERFACE ${rocdecode_LIBRARY}) + target_include_directories( + rocdecode::rocdecode + INTERFACE ${rocdecode_INCLUDE_DIR} + ) + endif() +else() videodecode_message(AUTHOR_WARNING "${PROJECT_NAME} skipped. Missing RocDecode...") return() endif() diff --git a/projects/rocprofiler-systems/source/bin/rocprof-sys-avail/avail.cpp b/projects/rocprofiler-systems/source/bin/rocprof-sys-avail/avail.cpp index 93128f8651..9313a06fe9 100644 --- a/projects/rocprofiler-systems/source/bin/rocprof-sys-avail/avail.cpp +++ b/projects/rocprofiler-systems/source/bin/rocprof-sys-avail/avail.cpp @@ -118,7 +118,7 @@ write_hw_counter_info(std::ostream&, const array_t& = {}, namespace { // initialize HIP before main so that librocprof-sys is not HSA_TOOLS_LIB -int gpu_count = rocprofsys::gpu::device_count(); +int gpu_count = 0; // statically allocated shared_ptrs to prevent use after free errors auto timemory_manager = tim::manager::master_instance(); @@ -138,6 +138,7 @@ main(int argc, char** argv) tim::unwind::set_bfd_verbose(3); tim::set_env("ROCPROFSYS_INIT_TOOLING", "OFF", 1); rocprofsys_init_library(); + gpu_count = rocprofsys::gpu::device_count(); std::set _category_options = component_categories{}(); { diff --git a/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/details.cpp b/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/details.cpp index 698e8adf14..5da3bd4ba1 100644 --- a/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/details.cpp +++ b/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/details.cpp @@ -488,6 +488,77 @@ find_function(image_t* app_image, const std::string& _name, const strset_t& _ext return _func; } +//======================================================================================// +// +// Find undefined function symbols (external references) in the binary +// +symtab_symbol_t* +find_undefined_function_symbol(image_t* app_image, const std::string& _name) +{ + if(_name.empty()) return nullptr; + + // Get all objects from the image + BPatch_Vector app_objects; + app_image->getObjects(app_objects); + + if(app_objects.empty()) + { + verbprintf(3, "No objects found in image for symbol search\n"); + return nullptr; + } + // Search helper lambda for code reuse + auto _find_symbol = [](SymTab::Symtab* symtab, + const std::string& target_name) -> symtab_symbol_t* { + if(!symtab) return nullptr; + + std::vector all_symbols; + if(!symtab->getAllSymbols(all_symbols)) return nullptr; + + for(auto* symbol : all_symbols) + { + if(!symbol || symbol->getType() != SymTab::Symbol::ST_FUNCTION || + symbol->getRegion()) + continue; + + // Try all possible symbol name representations + std::string symbol_name = symbol->getPrettyName(); + if(symbol_name.empty()) symbol_name = symbol->getMangledName(); + if(symbol_name.empty()) symbol_name = symbol->getTypedName(); + + // Check for exact match and undefined function criteria + if(symbol_name == target_name) return symbol; + } + return nullptr; + }; + + // Search through each object + for(auto* app_object : app_objects) + { + if(!app_object) continue; + + std::string binary_path = app_object->name(); + // Open Symtab directly for comprehensive symbol access + SymTab::Symtab* symtab = nullptr; + if(!SymTab::Symtab::openFile(symtab, binary_path)) + { + verbprintf(3, "Failed to open Symtab for: %s\n", binary_path.c_str()); + continue; + } + + // Search for the primary symbol name + auto* result = _find_symbol(symtab, _name); + if(result) + { + verbprintf(1, "Found undefined function symbol: '%s' in %s\n", _name.c_str(), + binary_path.c_str()); + return result; + } + } + + verbprintf(1, "Undefined function symbol: '%s' ... not found\n", _name.c_str()); + return nullptr; +} + //======================================================================================// // // Get the realpath to this exe diff --git a/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/fwd.hpp b/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/fwd.hpp index 900cd8f17e..ca9a6e7b48 100644 --- a/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/fwd.hpp +++ b/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/fwd.hpp @@ -360,6 +360,9 @@ insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc, procedure_t* find_function(image_t* appImage, const string_t& functionName, const strset_t& = {}); +symtab_symbol_t* +find_undefined_function_symbol(image_t* app_image, const std::string& _name); + void error_func_real(error_level_t level, int num, const char* const* params); diff --git a/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/rocprof-sys-instrument.cpp b/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/rocprof-sys-instrument.cpp index 3c0f9877fd..852473eca7 100644 --- a/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/rocprof-sys-instrument.cpp +++ b/projects/rocprofiler-systems/source/bin/rocprof-sys-instrument/rocprof-sys-instrument.cpp @@ -1721,6 +1721,14 @@ main(int argc, char** argv) use_mpi = true; break; } + else if(find_undefined_function_symbol(app_image, itr) != nullptr) + { + verbprintf(0, + "Found undefined symbol '%s' in '%s'. Enabling MPI support...\n", + itr, _cmdv[0]); + use_mpi = true; + break; + } } #endif diff --git a/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt b/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt index 89bc97f98a..e47850da30 100644 --- a/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/bin/tests/CMakeLists.txt @@ -1,3 +1,26 @@ +################################################################################ +# Copyright (c) 2025 Advanced Micro Devices, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +################################################################################ + set(ROCPROFSYS_ABORT_FAIL_REGEX "### ERROR ###|unknown-hash=|address of faulting memory reference|exiting with non-zero exit code|terminate called after throwing an instance|calling abort.. in |Exit code: [1-9]" CACHE INTERNAL @@ -312,6 +335,7 @@ rocprofiler_systems_add_bin_test( ~PERFETTO ~PROCESS_SAMPLING ~KOKKOSP + ~PAGE --csv --brief --advanced diff --git a/projects/rocprofiler-systems/source/lib/CMakeLists.txt b/projects/rocprofiler-systems/source/lib/CMakeLists.txt index 14e81112e7..e6699c833d 100644 --- a/projects/rocprofiler-systems/source/lib/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/lib/CMakeLists.txt @@ -40,6 +40,7 @@ target_link_libraries( $ $ $ + $ $ $ $ diff --git a/projects/rocprofiler-systems/source/lib/common/CMakeLists.txt b/projects/rocprofiler-systems/source/lib/common/CMakeLists.txt index d096ebf933..8ea90f2aed 100644 --- a/projects/rocprofiler-systems/source/lib/common/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/lib/common/CMakeLists.txt @@ -26,6 +26,8 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/invoke.hpp ${CMAKE_CURRENT_SOURCE_DIR}/join.hpp ${CMAKE_CURRENT_SOURCE_DIR}/setup.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/traits.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/md5sum.hpp ${CMAKE_CURRENT_SOURCE_DIR}/static_object.hpp ${CMAKE_CURRENT_SOURCE_DIR}/synchronized.hpp ) diff --git a/projects/rocprofiler-systems/source/lib/common/md5sum.hpp b/projects/rocprofiler-systems/source/lib/common/md5sum.hpp new file mode 100644 index 0000000000..6de09280e6 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/common/md5sum.hpp @@ -0,0 +1,469 @@ +// MIT License +// +// Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "traits.hpp" + +namespace rocprofsys +{ +inline namespace common +{ + +class md5sum +{ +public: + using size_type = uint32_t; // must be 32bit + using raw_digest_t = std::array; + static constexpr int blocksize = 64; + + template + explicit md5sum(Tp&& arg, Args&&... args); + + md5sum() = default; + ~md5sum() = default; + md5sum(const md5sum&) = default; + md5sum(md5sum&&) = default; + + md5sum& operator=(const md5sum&) = default; + md5sum& operator=(md5sum&&) = default; + + md5sum& update(std::string_view inp); + md5sum& update(const unsigned char* buf, size_type length); + md5sum& update(const char* buf, size_type length); + md5sum& finalize(); + std::string hexdigest() const; + std::string hexliteral() const; + raw_digest_t rawdigest() const { return digest; } + + template ::value, int>> + md5sum& update(Tp inp); + + friend std::ostream& operator<<(std::ostream&, md5sum md5); + +private: + void transform(const uint8_t block[blocksize]); + + bool finalized = false; + // 64bit counter for number of bits (lo, hi) + std::array count = { 0, 0 }; + std::array buffer{}; // overflow bytes from last 64 byte chunk + // digest so far, initialized to magic initialization constants. + std::array state = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }; + std::array digest{}; // result +}; + +template +md5sum::md5sum(Tp&& arg, Args&&... args) +{ + auto _update = [&](auto&& _val) { + using value_type = + std::remove_reference_t>>; + static_assert(!std::is_pointer::value, + "constructor cannot be called with pointer argument"); + update(std::forward(_val)); + }; + + _update(std::forward(arg)); + (_update(std::forward(args)), ...); + finalize(); +} + +template +md5sum& +md5sum::update(Tp inp) +{ + static_assert(std::is_arithmetic::value, "expected arithmetic type"); + return update(reinterpret_cast(&inp), sizeof(Tp)); +} + +template