From 2013d441d10b6c5a6327befaefe0de730be8d5ca Mon Sep 17 00:00:00 2001 From: mariaxarisi Date: Fri, 12 Jun 2026 12:39:12 +0200 Subject: [PATCH 1/2] feat(trace): capture memory access size for atomic operations Introduces a new `coldtrace_atomic_access_entry` struct to record the exact byte size of atomic reads and writes in the binary trace files. - Extends atomic read/write trace entries from 16 to 24 bytes to include `size`. - Modifies TSan subscriber macros in `subs_tsan.c` to extract and serialize the size parameter. - Updates the `trace_dump.py` tool to correctly unpack the new 24-byte format and print the access size. Signed-off-by: mariaxarisi --- include/coldtrace/entries.h | 6 +++++ scripts/trace_dump.py | 8 +++++-- src/entries.c | 44 +++++++++++++++++++++++-------------- src/subs_tsan.c | 24 ++++++++++++-------- 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/include/coldtrace/entries.h b/include/coldtrace/entries.h index 7f76a72e..9b6a0cb2 100644 --- a/include/coldtrace/entries.h +++ b/include/coldtrace/entries.h @@ -104,6 +104,12 @@ struct coldtrace_free_entry { struct coldtrace_stack_diff stack; }; +struct coldtrace_atomic_access_entry { + struct coldtrace_entry_header _; + uint64_t size; + uint64_t atomic_index; +}; + struct coldtrace_atomic_entry { struct coldtrace_entry_header _; uint64_t atomic_index; diff --git a/scripts/trace_dump.py b/scripts/trace_dump.py index cd5520dd..e35d8fdc 100755 --- a/scripts/trace_dump.py +++ b/scripts/trace_dump.py @@ -40,6 +40,7 @@ def get_multiple_file_log(s): COLD_ALLOC_ENTRY_SZ = 5 * 8 # bytes; sizeof(L3_ENTRY) COLD_FREE_ENTRY_SZ = 4 * 8 # bytes; sizeof(L3_ENTRY) COLD_ATOMIC_ENTRY_SZ = 2 * 8 # bytes; sizeof(L3_ENTRY) +COLD_ATOMIC_ACCESS_ENTRY_SZ = 3 * 8 # bytes; sizeof(L3_ENTRY) COLD_THREAD_ENTRY_SZ = 4 * 8 # bytes; sizeof(L3_ENTRY) if len(sys.argv) != 2 and not (len(sys.argv) == 3 and sys.argv[2] == "-d"): @@ -177,6 +178,9 @@ class EntryType(Enum): print(stacks) elif (entry_type.value == 9): atomic_timestamp, thread_stack_ptr, thread_stack_size = struct.unpack('= COLDTRACE_MMAP || type < COLDTRACE_ATOMIC_READ) { return INVALID_ATOMIC_INDEX; } + + if (type == COLDTRACE_ATOMIC_READ || type == COLDTRACE_ATOMIC_WRITE) { + uint64_t atomic_index = ((uint64_t *)buf)[2]; + return atomic_index; + } + uint64_t atomic_index = ((uint64_t *)buf)[1]; return atomic_index; } @@ -107,6 +113,7 @@ coldtrace_entry_parse_size(const void *buf) int type = coldtrace_entry_parse_type(buf); if (type == COLDTRACE_ALLOC || type == COLDTRACE_READ || type == COLDTRACE_WRITE || type == COLDTRACE_MMAP || + type == COLDTRACE_ATOMIC_READ || type == COLDTRACE_ATOMIC_WRITE || type == COLDTRACE_MUNMAP) { uint64_t size = ((uint64_t *)buf)[1]; return size; @@ -146,6 +153,9 @@ coldtrace_entry_get_size(const void *buf) case COLDTRACE_ATOMIC_READ: case COLDTRACE_ATOMIC_WRITE: + next += sizeof(struct coldtrace_atomic_access_entry); + break; + case COLDTRACE_LOCK_ACQUIRE: case COLDTRACE_LOCK_RELEASE: case COLDTRACE_RW_LOCK_CREATE: @@ -174,23 +184,23 @@ coldtrace_entry_get_size(const void *buf) static const size_t space_map_[] = { - [COLDTRACE_FREE] = sizeof(struct coldtrace_free_entry), - [COLDTRACE_ALLOC] = sizeof(struct coldtrace_alloc_entry), - [COLDTRACE_MMAP] = sizeof(struct coldtrace_alloc_entry), - [COLDTRACE_MUNMAP] = sizeof(struct coldtrace_alloc_entry), - [COLDTRACE_READ] = sizeof(struct coldtrace_access_entry), - [COLDTRACE_WRITE] = sizeof(struct coldtrace_access_entry), - [COLDTRACE_ATOMIC_READ] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_ATOMIC_WRITE] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_LOCK_ACQUIRE] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_LOCK_RELEASE] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_RW_LOCK_CREATE] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_RW_LOCK_DESTROY] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_RW_LOCK_ACQ_SHR] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_RW_LOCK_ACQ_EXC] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_RW_LOCK_REL_SHR] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_RW_LOCK_REL_EXC] = sizeof(struct coldtrace_atomic_entry), - [COLDTRACE_RW_LOCK_REL] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_FREE] = sizeof(struct coldtrace_free_entry), + [COLDTRACE_ALLOC] = sizeof(struct coldtrace_alloc_entry), + [COLDTRACE_MMAP] = sizeof(struct coldtrace_alloc_entry), + [COLDTRACE_MUNMAP] = sizeof(struct coldtrace_alloc_entry), + [COLDTRACE_READ] = sizeof(struct coldtrace_access_entry), + [COLDTRACE_WRITE] = sizeof(struct coldtrace_access_entry), + [COLDTRACE_ATOMIC_READ] = sizeof(struct coldtrace_atomic_access_entry), + [COLDTRACE_ATOMIC_WRITE] = sizeof(struct coldtrace_atomic_access_entry), + [COLDTRACE_LOCK_ACQUIRE] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_LOCK_RELEASE] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_RW_LOCK_CREATE] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_RW_LOCK_DESTROY] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_RW_LOCK_ACQ_SHR] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_RW_LOCK_ACQ_EXC] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_RW_LOCK_REL_SHR] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_RW_LOCK_REL_EXC] = sizeof(struct coldtrace_atomic_entry), + [COLDTRACE_RW_LOCK_REL] = sizeof(struct coldtrace_atomic_entry), [COLDTRACE_CXA_GUARD_ACQUIRE] = sizeof(struct coldtrace_atomic_entry), [COLDTRACE_CXA_GUARD_RELEASE] = sizeof(struct coldtrace_atomic_entry), [COLDTRACE_THREAD_JOIN] = sizeof(struct coldtrace_atomic_entry), diff --git a/src/subs_tsan.c b/src/subs_tsan.c index 595b18e9..086837a4 100644 --- a/src/subs_tsan.c +++ b/src/subs_tsan.c @@ -65,34 +65,38 @@ area_t areas_[AREAS]; caslock_acquire(&area->lock); \ area->idx_a = coldtrace_next_atomic_idx(); -#define REL_LOG_R(addr, size) \ +#define REL_LOG_R(addr, sz) \ area_t *area = get_area(addr); \ uint64_t idx_a = area->idx_a; \ caslock_release(&area->lock); \ - struct coldtrace_atomic_entry *e; \ + struct coldtrace_atomic_access_entry *e; \ e = coldtrace_thread_append(md, COLDTRACE_ATOMIC_READ, addr); \ + e->size = sz; \ e->atomic_index = idx_a; -#define REL_LOG_W(addr, size) \ +#define REL_LOG_W(addr, sz) \ area_t *area = get_area(addr); \ uint64_t idx_a = area->idx_a; \ caslock_release(&area->lock); \ - struct coldtrace_atomic_entry *e; \ + struct coldtrace_atomic_access_entry *e; \ e = coldtrace_thread_append(md, COLDTRACE_ATOMIC_WRITE, addr); \ + e->size = sz; \ e->atomic_index = idx_a; -#define REL_LOG_RW(addr, size) \ +#define REL_LOG_RW(addr, sz) \ area_t *area = get_area(addr); \ uint64_t idx_a = area->idx_a; \ uint64_t idx_b = coldtrace_next_atomic_idx(); \ caslock_release(&area->lock); \ - struct coldtrace_atomic_entry *e; \ + struct coldtrace_atomic_access_entry *e; \ e = coldtrace_thread_append(md, COLDTRACE_ATOMIC_READ, addr); \ + e->size = sz; \ e->atomic_index = idx_a; \ e = coldtrace_thread_append(md, COLDTRACE_ATOMIC_WRITE, addr); \ + e->size = sz; \ e->atomic_index = idx_b; -#define REL_LOG_RW_COND(addr, size, success) \ +#define REL_LOG_RW_COND(addr, sz, success) \ uint64_t idx_b = 0; \ if (success) { \ idx_b = coldtrace_next_atomic_idx(); \ @@ -100,11 +104,13 @@ area_t areas_[AREAS]; area_t *area = get_area(addr); \ uint64_t idx_a = area->idx_a; \ caslock_release(&area->lock); \ - struct coldtrace_atomic_entry *e; \ + struct coldtrace_atomic_access_entry *e; \ e = coldtrace_thread_append(md, COLDTRACE_ATOMIC_READ, addr); \ + e->size = sz; \ e->atomic_index = idx_a; \ if (success) { \ - e = coldtrace_thread_append(md, COLDTRACE_ATOMIC_WRITE, addr); \ + e = coldtrace_thread_append(md, COLDTRACE_ATOMIC_WRITE, addr); \ + e->size = sz; \ e->atomic_index = idx_b; \ } From b155298b04c52e8124a766e83d95305421451dc9 Mon Sep 17 00:00:00 2001 From: mariaxarisi Date: Mon, 15 Jun 2026 12:23:00 +0200 Subject: [PATCH 2/2] test: add test for atomic reads and writes Signed-off-by: mariaxarisi --- test/trace_aread_awrite_size.cpp | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 test/trace_aread_awrite_size.cpp diff --git a/test/trace_aread_awrite_size.cpp b/test/trace_aread_awrite_size.cpp new file mode 100644 index 00000000..b177545b --- /dev/null +++ b/test/trace_aread_awrite_size.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +struct expected_entry expected_1[] = { + + EXPECT_SUFFIX(COLDTRACE_FENCE), + + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 0, sizeof(uint8_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 0, sizeof(uint8_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 1, sizeof(uint16_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 1, sizeof(uint16_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 2, sizeof(uint32_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 2, sizeof(uint32_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 3, sizeof(uint64_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 3, sizeof(uint64_t)), + + EXPECT_SOME(COLDTRACE_WRITE, 0, 1), + EXPECT_SOME(COLDTRACE_READ, 0, 1), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 5, sizeof(float)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 5, sizeof(float)), + EXPECT_SOME(COLDTRACE_WRITE, 0, 1), + EXPECT_SOME(COLDTRACE_READ, 0, 1), + EXPECT_SOME(COLDTRACE_WRITE, 0, 1), + EXPECT_SOME(COLDTRACE_READ, 0, 1), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 6, sizeof(double)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 6, sizeof(double)), + EXPECT_SOME(COLDTRACE_WRITE, 0, 1), + EXPECT_SOME(COLDTRACE_READ, 0, 1), + + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 0, sizeof(uint8_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 0, sizeof(uint8_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 1, sizeof(uint16_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 1, sizeof(uint16_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 2, sizeof(uint32_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 2, sizeof(uint32_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_READ, 3, sizeof(uint64_t)), + EXPECT_VALUE_SIZE(COLDTRACE_ATOMIC_WRITE, 3, sizeof(uint64_t)), + + EXPECT_SUFFIX(COLDTRACE_THREAD_EXIT), + EXPECT_END, +}; + +#define VALUE 10 +#define FLOAT_VALUE 10.0 +int +main(void) +{ + register_expected_trace(1, expected_1); + + std::atomic at_8; + std::atomic at_16; + std::atomic at_32; + std::atomic at_64; + std::atomic at_float; + std::atomic at_double; + + std::atomic_thread_fence(std::memory_order_release); // fence + + at_8.store(VALUE, std::memory_order_release); // write + uint8_t val_8 = at_8.load(std::memory_order_acquire); // read + + at_16.store(VALUE * 2, std::memory_order_release); // write + uint16_t val_16 = at_16.load(std::memory_order_acquire); // read + + at_32.store(VALUE * 3, std::memory_order_release); // write + uint32_t val_32 = at_32.load(std::memory_order_acquire); // read + + at_64.store(VALUE * 4, std::memory_order_release); // write + uint64_t val_64 = at_64.load(std::memory_order_acquire); // read + + at_float.store(FLOAT_VALUE, std::memory_order_release); // write + float val_float = at_float.load(std::memory_order_acquire); // read + + at_double.store(FLOAT_VALUE * 2, std::memory_order_release); // write + double val_double = at_double.load(std::memory_order_acquire); // read + + at_8.fetch_add(1, std::memory_order_release); // RMW + at_16.fetch_add(1, std::memory_order_release); // RMW + at_32.fetch_add(1, std::memory_order_release); // RMW + at_64.fetch_add(1, std::memory_order_release); // RMW + + printf("Loaded 8-bit atomic: %u\n", val_8); + printf("Loaded 16-bit atomic: %u\n", val_16); + printf("Loaded 32-bit atomic: %u\n", val_32); + printf("Loaded 64-bit atomic: %lu\n", val_64); + printf("Loaded float atomic: %f\n", val_float); + printf("Loaded double atomic: %f\n", val_double); + + return 0; +}