Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions src/subs_tsan.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ area_t areas_[AREAS];
e->atomic_index = idx_b; \
}

#define REL_LOG_FENCE(addr) \
area_t *area = get_area(addr); \
uint64_t idx_a = area->idx_a; \
caslock_release(&area->lock); \
struct coldtrace_atomic_entry *e; \
e = coldtrace_thread_append(md, COLDTRACE_FENCE, addr); \
e->atomic_index = idx_a;

PS_SUBSCRIBE(CAPTURE_BEFORE, EVENT_MA_AREAD, {
struct ma_aread_event *ev = EVENT_PAYLOAD(ev);
Expand Down Expand Up @@ -158,3 +165,13 @@ PS_SUBSCRIBE(CAPTURE_AFTER, EVENT_MA_CMPXCHG, {
struct ma_cmpxchg_event *ev = EVENT_PAYLOAD(ev);
REL_LOG_RW_COND(ev->addr, ev->size, ev->ok);
})

PS_SUBSCRIBE(CAPTURE_BEFORE, EVENT_MA_FENCE, {
struct ma_fence_event *ev = EVENT_PAYLOAD(ev);
FETCH_STACK_ACQ(ev->pc, ev->mo);
})

PS_SUBSCRIBE(CAPTURE_AFTER, EVENT_MA_FENCE, {
struct ma_fence_event *ev = EVENT_PAYLOAD(ev);
REL_LOG_FENCE(ev->pc);
})
2 changes: 2 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ foreach(SRC ${SRCS})
./${TARGET})
endif()
endforeach()
target_compile_options(trace_fence PRIVATE -fsanitize=thread -Wno-error=tsan -Wno-unknown-warning-option)

1 change: 0 additions & 1 deletion test/atomic_test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <atomic>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <pthread.h>
Expand Down
161 changes: 161 additions & 0 deletions test/trace_fence.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#include <atomic>
#include <iostream>
#include <pthread.h>
#include <string>
#include <trace_checker.h>

#define NUM_THREADS 2

#define RW_CYCLE \
EXPECT_SOME(COLDTRACE_READ, 0, 1), EXPECT_SOME(COLDTRACE_WRITE, 0, 1)

#define FENCE_RW_CYCLE \
EXPECT_SOME(COLDTRACE_WRITE, 0, 2), EXPECT_SOME(COLDTRACE_READ, 0, 5), \
EXPECT_SOME(COLDTRACE_WRITE, 0, 2), EXPECT_SOME(COLDTRACE_READ, 0, 1), \
EXPECT_SOME(COLDTRACE_WRITE, 0, 2), RW_CYCLE, RW_CYCLE

#define FENCE_RW_CYCLE_2 \
EXPECT_SOME(COLDTRACE_WRITE, 0, 1), EXPECT_SOME(COLDTRACE_READ, 0, 2), \
EXPECT_SOME(COLDTRACE_WRITE, 0, 2), EXPECT_SOME(COLDTRACE_READ, 0, 2), \
EXPECT_SOME(COLDTRACE_ALLOC, 0, 1), \
EXPECT_SOME(COLDTRACE_WRITE, 0, 2), EXPECT_SOME(COLDTRACE_READ, 0, 8)
// Global
std::string computation(int);
void print(std::string);

std::atomic<int> arr[3];

std::string data[1000]; // non-atomic data

void
print(std::string s)
{
std::cout << s << std::endl;
}

std::string
computation(int value)
{
return std::to_string(value);
}

struct expected_entry expected_1[] = {
EXPECT_SOME(COLDTRACE_ALLOC, 0, 1),
EXPECT_SOME(COLDTRACE_FREE, 0, 1),
EXPECT_SOME(COLDTRACE_WRITE, 0, 3000),
EXPECT_ENTRY(COLDTRACE_ATOMIC_WRITE),
EXPECT_ENTRY(COLDTRACE_ATOMIC_WRITE),
EXPECT_ENTRY(COLDTRACE_ATOMIC_WRITE),
EXPECT_ENTRY(COLDTRACE_ALLOC),
EXPECT_SOME(COLDTRACE_WRITE, 0, 3),
EXPECT_ENTRY(COLDTRACE_THREAD_CREATE),
EXPECT_ENTRY(COLDTRACE_THREAD_CREATE),
EXPECT_SOME(COLDTRACE_READ, 0, 1),
EXPECT_ENTRY(COLDTRACE_THREAD_JOIN),
EXPECT_SOME(COLDTRACE_READ, 0, 1),
EXPECT_ENTRY(COLDTRACE_THREAD_JOIN),
EXPECT_SOME(COLDTRACE_READ, 0, 1000),
EXPECT_ENTRY(COLDTRACE_THREAD_EXIT),
EXPECT_END,
};

struct expected_entry expected_2[] = {
EXPECT_ENTRY(COLDTRACE_THREAD_START),
EXPECT_SOME(COLDTRACE_READ, 0, 3),
EXPECT_SOME(COLDTRACE_FREE, 0, 1),
EXPECT_SOME(COLDTRACE_WRITE, 0, 1),
EXPECT_SOME(COLDTRACE_READ, 0, 1),
FENCE_RW_CYCLE,
FENCE_RW_CYCLE,
FENCE_RW_CYCLE,
EXPECT_ENTRY(COLDTRACE_FENCE),
EXPECT_ENTRY(COLDTRACE_ATOMIC_WRITE),
EXPECT_ENTRY(COLDTRACE_ATOMIC_WRITE),
EXPECT_ENTRY(COLDTRACE_ATOMIC_WRITE),
EXPECT_ENTRY(COLDTRACE_THREAD_EXIT),
EXPECT_END,
};

struct expected_entry expected_3[] = {
EXPECT_ENTRY(COLDTRACE_THREAD_START),
EXPECT_ENTRY(COLDTRACE_ATOMIC_READ),
EXPECT_ENTRY(COLDTRACE_ATOMIC_READ),
EXPECT_ENTRY(COLDTRACE_ATOMIC_READ),
EXPECT_ENTRY(COLDTRACE_FENCE),
EXPECT_SOME(COLDTRACE_WRITE, 0, 1),
EXPECT_SOME(COLDTRACE_READ, 0, 2),
FENCE_RW_CYCLE_2,
FENCE_RW_CYCLE_2,
FENCE_RW_CYCLE_2,
EXPECT_ENTRY(COLDTRACE_THREAD_EXIT),
EXPECT_END,
};

struct ThreadAArgs {
int v0, v1, v2;
};

// Thread A, compute 3 values.
void *
ThreadA(void *arg)
{
ThreadAArgs *args = (ThreadAArgs *)arg;
int v0 = args->v0;
int v1 = args->v1;
int v2 = args->v2;
free(arg); // if dynamically allocated

// assert(0 <= v0, v1, v2 < 1000);
data[v0] = computation(v0);
data[v1] = computation(v1);
data[v2] = computation(v2);
std::atomic_thread_fence(std::memory_order_release);
std::atomic_store_explicit(&arr[0], v0, std::memory_order_relaxed);
std::atomic_store_explicit(&arr[1], v1, std::memory_order_relaxed);
std::atomic_store_explicit(&arr[2], v2, std::memory_order_relaxed);
return NULL;
}


// Thread B, prints between 0 and 3 values already computed.
void *
ThreadB(void *arg)
{
int v0 = std::atomic_load_explicit(&arr[0], std::memory_order_relaxed);
int v1 = std::atomic_load_explicit(&arr[1], std::memory_order_relaxed);
int v2 = std::atomic_load_explicit(&arr[2], std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);

// v0, v1, v2 might turn out to be -1, some or all of them.
// Otherwise it is safe to read the non-atomic data because of the fences:
if (v0 != -1)
print(data[v0]);
if (v1 != -1)
print(data[v1]);
if (v2 != -1)
print(data[v2]);
return NULL;
}

int
main()
{
register_expected_trace(1, expected_1);
register_expected_trace(2, expected_2);
register_expected_trace(3, expected_3);
arr[0].store(-1);
arr[1].store(-1);
arr[2].store(-1);

pthread_t threads[NUM_THREADS];
ThreadAArgs *args = new ThreadAArgs{10, 20, 30};

pthread_create(threads + 0, NULL, ThreadA, args);

pthread_create(threads + 1, NULL, ThreadB, NULL);

for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(*(threads + i), NULL);
}
return 0;
}