Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e47f198
Fixed a bug in parsing command line arguments
ajanicijamd Jan 31, 2025
d6ffeb9
Fixed file format
ajanicijamd Jan 31, 2025
080b204
Fixed cmake formatting
ajanicijamd Jan 31, 2025
8c2afd3
Merge pull request #2 from ajanicijamd/omnitrace
ajanicijamd Jan 31, 2025
0eb3c2e
Remove unused `m_is_default` from `struct argument`. Fixed argument a…
dgaliffiAMD Feb 11, 2025
fad254d
Update binutils version to 2.42
pranswarup Mar 21, 2025
858cfc7
Update rpath on libunwind libraries (#4)
dgaliffiAMD Apr 11, 2025
5400e13
Update GOTCHA submodule to fix CMake 4.0 build
dgaliffiAMD Jun 11, 2025
b82ec24
Removed in-file offsets. Replaced with simple line based parsing (#5)
marantic-amd Jul 8, 2025
469eced
Update gotcha submodule (#7)
sputhala-amd Jul 8, 2025
5780cc7
Update gotcha submodule to fix its submodules (#8)
dgaliffiAMD Jul 15, 2025
4897dda
Add type info in ompt labeled arguments (#9)
marantic-amd Aug 12, 2025
8b01785
Enable string to be copied in print_help (#10)
mradosav-amd Aug 18, 2025
b5e41aa
Fixed code to strictly correct C++ so that Clang 20 would accept it (…
ajanicijamd Oct 24, 2025
4daa81b
Exclude perf_event_uncore events from evaluation (#13)
sputhala-amd Nov 14, 2025
2dd0511
Update the ConfigLibunwind.cmake (#14)
dgaliffiAMD Nov 19, 2025
29037f3
Fix: Replace hard-coded thread limit with TIMEMORY_MAX_THREADS (#18)
anujshuk-amd Dec 8, 2025
24407d3
Fix: Prioritize GNU libunwind headers over system LLVM libunwind usin…
anujshuk-amd Dec 23, 2025
696a160
Convert thread storage from std::array to std::vector with dynamic gr…
anujshuk-amd Jan 8, 2026
2624144
Refactor: Extract dynamic_storage_base as reusable template
anujshuk-amd Jan 14, 2026
3bdd03a
Fix race condition in mkdir for MPI environments
anujshuk-amd Jan 20, 2026
079309f
refactor: use public inheritance for dynamic_storage_base and worked …
anujshuk-amd Jan 20, 2026
f3f2b94
Add stable_storage class to fix pointer invalidation in dynamic_stora…
anujshuk-amd Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
url = https://github.com/jrmadsen/Caliper.git
[submodule "external/gotcha"]
path = external/gotcha
url = https://github.com/jrmadsen/GOTCHA.git
url = https://github.com/ROCm/GOTCHA.git
[submodule "external/llvm-ompt"]
path = external/llvm-ompt
url = https://github.com/NERSC/LLVM-openmp.git
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# timemory

## Version 4.0.1rc0

> Date: Tue Dec 3, 2025

### Bug Fixes

- Fixed hard-coded thread limit in `operation::set_storage`
- Changed from `max_threads = 4096` to use `TIMEMORY_MAX_THREADS`
- Prevents segfaults when Linux thread IDs exceed 4096

## Version 3.2.4

> Date: Mon Jul 19 17:22:28 2021 -0500
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.0.0rc0
4.0.1rc0
12 changes: 5 additions & 7 deletions cmake/Modules/ConfigBinutils.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,15 @@ include(ExternalProject)
externalproject_add(
binutils-external
PREFIX ${PROJECT_BINARY_DIR}/external/binutils
URL
${TIMEMORY_BINUTILS_DOWNLOAD_URL}
http://ftpmirror.gnu.org/gnu/binutils/binutils-2.40.tar.gz
http://mirrors.kernel.org/sourceware/binutils/releases/binutils-2.40.tar.gz
URL ${TIMEMORY_BINUTILS_DOWNLOAD_URL}
http://ftpmirror.gnu.org/gnu/binutils/binutils-2.42.tar.gz
http://mirrors.kernel.org/sourceware/binutils/releases/binutils-2.42.tar.gz
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND
${CMAKE_COMMAND} -E env CC=${CMAKE_C_COMPILER} CFLAGS=-fPIC\ -O3
CXX=${CMAKE_CXX_COMPILER} CXXFLAGS=-fPIC\ -O3 <SOURCE_DIR>/configure
--prefix=${TPL_STAGING_PREFIX} ${_binutils_CONFIG_FLAGS}
BUILD_COMMAND ${MAKE_COMMAND} all-libiberty all-bfd all-opcodes all-libelf
all-libsframe
BUILD_COMMAND ${MAKE_COMMAND} all-libiberty all-bfd all-opcodes all-libsframe
INSTALL_COMMAND ""
BUILD_BYPRODUCTS "${_TIMEMORY_BINUTILS_BUILD_BYPRODUCTS}")

Expand All @@ -94,7 +92,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} ARGS -E make_directory ${TPL_STAGING_PREFIX}/lib
COMMAND
install ARGS -C
${PROJECT_BINARY_DIR}/external/binutils/src/binutils-external/bfd/libbfd.a
${PROJECT_BINARY_DIR}/external/binutils/src/binutils-external/bfd/.libs/libbfd.a
${PROJECT_BINARY_DIR}/external/binutils/src/binutils-external/opcodes/libopcodes.a
${PROJECT_BINARY_DIR}/external/binutils/src/binutils-external/libiberty/libiberty.a
${PROJECT_BINARY_DIR}/external/binutils/src/binutils-external/libsframe/.libs/libsframe.a
Expand Down
23 changes: 20 additions & 3 deletions cmake/Modules/ConfigLibunwind.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ if(TIMEMORY_INSTALL_HEADERS)
endforeach()
endif()

file(GLOB libunwind_libs "${PROJECT_BINARY_DIR}/external/libunwind/install/${CMAKE_INSTALL_LIBDIR}/*")
file(GLOB libunwind_libs
"${PROJECT_BINARY_DIR}/external/libunwind/install/${CMAKE_INSTALL_LIBDIR}/*")

foreach(_LIB ${libunwind_libs})
if(IS_DIRECTORY ${_LIB})
Expand All @@ -158,6 +159,19 @@ foreach(_LIB ${libunwind_libs})

if("${_LIB}" MATCHES "\\.so($|\\.)")
execute_process(COMMAND ${CMAKE_STRIP} ${_LIB})
find_program(CHRPATH_EXECUTABLE chrpath)
find_program(PATCHELF_EXECUTABLE patchelf)

if(CHRPATH_EXECUTABLE)
execute_process(COMMAND ${CHRPATH_EXECUTABLE} -r "$ORIGIN" ${_LIB})
elseif(PATCHELF_EXECUTABLE)
execute_process(COMMAND ${PATCHELF_EXECUTABLE} --set-rpath "$ORIGIN" ${_LIB})
else()
message(
AUTHOR_WARNING
"[timemory] Neither chrpath nor patchelf found. Skipping rpath modification for libunwind."
)
endif()
endif()

install(
Expand All @@ -171,12 +185,15 @@ install(
DESTINATION ${CMAKE_INSTALL_LIBDIR}/timemory/libunwind/pkgconfig
OPTIONAL)

# Add include directories with BEFORE to ensure they come first in include search path
# This ensures GNU libunwind headers are found before LLVM libunwind headers
target_include_directories(
timemory-libunwind SYSTEM
timemory-libunwind BEFORE
INTERFACE $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/external/libunwind/install/include>
$<INSTALL_INTERFACE:include/timemory/libunwind>)
target_link_directories(
timemory-libunwind INTERFACE
timemory-libunwind
INTERFACE
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/external/libunwind/install/${CMAKE_INSTALL_LIBDIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_LIBDIR}/timemory/libunwind>)
target_link_libraries(
Expand Down
2 changes: 1 addition & 1 deletion external/gotcha
Submodule gotcha updated 149 files
11 changes: 8 additions & 3 deletions source/timemory/backends/papi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

#include <regex>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -277,8 +278,8 @@ get_event_code(string_view_cref_t event_code_str)
int event_code = -1;
int retval = PAPI_event_name_to_code(event_code_str.data(), &event_code);
working() = check(retval, TIMEMORY_JOIN(' ', "Warning!! Failure converting",
event_code_str, "to enum value")
.c_str());
event_code_str, "to enum value")
.c_str());
return (retval == PAPI_OK) ? event_code : PAPI_NOT_INITED;
#else
consume_parameters(event_code_str);
Expand Down Expand Up @@ -401,7 +402,7 @@ add_events(int event_set, string_t* events, int number)

TIMEMORY_BACKENDS_INLINE
hwcounter_info_t
available_events_info()
available_events_info(const std::unordered_set<std::string>& excluded_components)
{
hwcounter_info_t evts{};

Expand Down Expand Up @@ -528,6 +529,10 @@ available_events_info()
continue;
# endif

// Skip excluded components
if(excluded_components.count(component->name) > 0)
continue;

// show this component has not found any events yet
// int num_cmp_events = 0;

Expand Down
4 changes: 2 additions & 2 deletions source/timemory/backends/papi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ check(int retval, string_view_cref_t mesg, bool quiet = false)
#if defined(TIMEMORY_USE_PAPI)
auto* error_str = PAPI_strerror(retval);
auto&& _msg = TIMEMORY_JOIN(' ', "[timemory][papi]", mesg, ":: PAPI_error",
retval, ":", error_str);
retval, ":", error_str);
if(settings::papi_fail_on_error())
{
TIMEMORY_EXCEPTION(_msg);
Expand Down Expand Up @@ -1023,7 +1023,7 @@ overflow(int evt_set, string_view_cref_t evt_name, int threshold, int flags,
//--------------------------------------------------------------------------------------//

hwcounter_info_t
available_events_info();
available_events_info(const std::unordered_set<std::string>& excluded_components = {});

//--------------------------------------------------------------------------------------//

Expand Down
12 changes: 12 additions & 0 deletions source/timemory/components/ompt/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,20 @@ namespace openmp
{
struct labeled_argument
{
template <typename Type>
std::string get_type_name(const Type& arg)
{
const char* name = typeid(arg).name();
int status;
std::unique_ptr<char, void (*)(void*)> demangledName(
abi::__cxa_demangle(name, nullptr, nullptr, &status), std::free);
return status == 0 ? demangledName.get() : name;
}

template <typename Tp>
labeled_argument(std::string_view _lbl, Tp&& _val)
: label{ _lbl }
, type{ get_type_name(_val) }
, value{ timemory::join::join("", std::forward<Tp>(_val)) }
{}

Expand All @@ -55,6 +66,7 @@ struct labeled_argument
}

std::string_view label = {};
std::string type = {};
std::string value = {};
};

Expand Down
121 changes: 115 additions & 6 deletions source/timemory/operations/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@
#include "timemory/storage/types.hpp"
#include "timemory/variadic/types.hpp"

#include <atomic>
#include <functional>
#include <iosfwd>
#include <mutex>
#include <type_traits>
#include <utility>

Expand Down Expand Up @@ -798,6 +800,109 @@ struct get_depth
}
};
//
//
/// \struct tim::operation::stable_storage
/// \tparam StorageType The storage pointer type (e.g., storage<T>*)
/// \tparam ChunkSize Size of each chunk (default: max_threads)
///
/// \brief A pointer-stable storage container using vector of unique_ptr to arrays.
/// Unlike std::vector, existing element pointers remain valid during growth.
template <typename StorageType, size_t ChunkSize>
class stable_storage
{
public:
using value_type = StorageType;
using chunk_type = std::array<StorageType, ChunkSize>;
using chunk_ptr = std::unique_ptr<chunk_type>;

stable_storage() = default;

explicit stable_storage(size_t initial_size, StorageType default_val = StorageType{})
{
reserve(initial_size);
for(size_t i = 0; i < initial_size; ++i)
(*this)[i] = default_val;
m_size = initial_size;
}

StorageType& operator[](size_t idx)
{
ensure_capacity(idx);
return (*m_chunks[idx / ChunkSize])[idx % ChunkSize];
}

StorageType& at(size_t idx)
{
ensure_capacity(idx);
return (*m_chunks[idx / ChunkSize])[idx % ChunkSize];
}

size_t size() const { return m_size; }
size_t capacity() const { return m_chunks.size() * ChunkSize; }

void reserve(size_t new_cap)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reserve here is also not thread safe, should be guarded with lock.

{
while(capacity() < new_cap)
add_chunk();
}

void ensure_capacity(size_t idx)
{
if(idx >= capacity())
{
std::lock_guard<std::mutex> lock(m_mutex);
while(idx >= capacity())
add_chunk();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After ensure_capacity(idx), at()/operator[] did (*m_chunks[idx/ChunkSize])[idx%ChunkSize] while another thread could be calling add_chunk() and modifying m_chunks.

}
if(idx >= m_size)
m_size = idx + 1;
}

private:
void add_chunk()
{
auto chunk = std::make_unique<chunk_type>();
chunk->fill(StorageType{});
m_chunks.push_back(std::move(chunk));
}

std::vector<chunk_ptr> m_chunks{};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage of m_chunks should guarded with locks, because multiple threads could be calling ensure_capacity and add_chunk at the same time.

size_t m_size{0};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_size should be std::atomic<size_t> to ensure thread safety.

std::mutex m_mutex{};
};

/// \struct tim::operation::dynamic_storage_base
/// \tparam StorageType The storage pointer type (e.g., storage<T>*)
/// \tparam max_threads Initial capacity for the storage array
///
/// \brief A reusable base class for thread-safe dynamic storage management.
/// Uses stable_storage which maintains pointer stability during growth.
template <typename StorageType, size_t max_threads>
struct dynamic_storage_base
{
using storage_array_t = stable_storage<StorageType, max_threads>;

/// @brief Returns the current capacity (thread-safe read-only access)
static size_t get_capacity()
{
return get_storage().capacity();
}

protected:
static storage_array_t& get_storage()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dynamic_storage_base::get_storage() returns a static instance, while set_storage<T>::get() below returned a different static _v.

{
static storage_array_t instance(max_threads, nullptr);
return instance;
}

static void ensure_capacity(storage_array_t& _v, size_t _idx)
{
_v.ensure_capacity(_idx);
}
};
//
//--------------------------------------------------------------------------------------//

//--------------------------------------------------------------------------------------//
//
template <typename T>
Expand All @@ -810,29 +915,30 @@ struct get_storage;
/// the data storage structure for a component which should be updated for
/// aggregation/logging.
template <typename T>
struct set_storage
struct set_storage : public dynamic_storage_base<storage<T>*, TIMEMORY_MAX_THREADS>
{
friend struct get_storage<T>;
static constexpr size_t max_threads = 4096;
static constexpr size_t max_threads = TIMEMORY_MAX_THREADS;
using type = T;
using storage_array_t = std::array<storage<type>*, max_threads>;
using base_type = dynamic_storage_base<storage<T>*, max_threads>;
using storage_array_t = typename base_type::storage_array_t;

TIMEMORY_DEFAULT_OBJECT(set_storage)

TIMEMORY_INLINE auto operator()(storage<type>* _storage, size_t _idx) const
{
base_type::ensure_capacity(get(), _idx);
get().at(_idx) = static_cast<storage<type>*>(_storage);
}

TIMEMORY_INLINE auto operator()(type& _obj, size_t _idx) const
{
base_type::ensure_capacity(get(), _idx);
get().at(_idx) = static_cast<storage<type>*>(_obj.get_storage());
}

private:
static storage_array_t& get()
{
static storage_array_t _v = { nullptr };
static storage_array_t _v(max_threads, nullptr);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should return get_storage(), so there is a single static instance. All set_storage and get_storage logic uses that same instance.

return _v;
}
};
Expand Down Expand Up @@ -865,6 +971,9 @@ struct get_storage

TIMEMORY_INLINE auto operator()(size_t _idx) const
{
// Thread-safe read using atomic capacity
if(_idx >= operation::set_storage<T>::get_capacity())
return static_cast<storage<type>*>(nullptr);
return operation::set_storage<T>::get().at(_idx);
}

Expand Down
4 changes: 2 additions & 2 deletions source/timemory/tpls/cereal/cereal/types/tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ struct serialize
template <class Archive, class... Types>
inline static void apply(Archive& ar, std::tuple<Types...>& tuple)
{
serialize<Height - 1>::template apply(ar, tuple);
serialize<Height - 1>::template apply<>(ar, tuple);
ar(TIMEMORY_CEREAL_NVP_(tuple_element_name<Height - 1>::c_str(),
std::get<Height - 1>(tuple)));
}
Expand All @@ -123,7 +123,7 @@ template <class Archive, class... Types>
inline void
TIMEMORY_CEREAL_SERIALIZE_FUNCTION_NAME(Archive& ar, std::tuple<Types...>& tuple)
{
tuple_detail::serialize<std::tuple_size<std::tuple<Types...>>::value>::template apply(
tuple_detail::serialize<std::tuple_size<std::tuple<Types...>>::value>::template apply<>(
ar, tuple);
}
} // namespace cereal
Expand Down
Loading