Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API to annotate block points #195

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
161 changes: 140 additions & 21 deletions include/coz.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@
#if !defined(COZ_H)
#define COZ_H

/// Embedders can define COZ_ENABLED explicitly to 1 or 0 to globally to
/// enable/disable coz tracepoints at build time without needing to modify code.
/// If set to 0 before including this header for the first time, all macros
/// become noops. This is useful to commit coz tracepoints into a codebase but
/// not require linking against the coz runtime and not having any code bloat
/// nor runtime overhead from using the macros.
#ifndef COZ_ENABLED
#define COZ_ENABLED 1
#endif

#if COZ_ENABLED

#ifndef __USE_GNU
# define __USE_GNU
#endif
Expand Down Expand Up @@ -37,31 +49,57 @@ typedef struct {
size_t backoff; // Used to batch updates to the shared counter. Currently unused.
} coz_counter_t;

// The type of the _coz_get_counter function
typedef coz_counter_t* (*coz_get_counter_t)(int, const char*);

// Locate and invoke _coz_get_counter
static coz_counter_t* _call_coz_get_counter(int type, const char* name) {
static unsigned char _initialized = 0;
static coz_get_counter_t fn; // The pointer to _coz_get_counter

if(!_initialized) {
if(dlsym) {
// Locate the _coz_get_counter method
void* p = dlsym(RTLD_DEFAULT, "_coz_get_counter");

// Use memcpy to avoid pedantic GCC complaint about storing function pointer in void*
memcpy(&fn, &p, sizeof(p));
}

_initialized = 1;
// Use memcpy to avoid pedantic GCC complaint about storing function pointer in void*
static unsigned char initialized = dlsym == NULL;
static coz_counter_t* (*fn)(int, const char*);

if (!initialized) {
void* p = dlsym(RTLD_DEFAULT, "_coz_get_counter");
memcpy(&fn, &p, sizeof(p));
initialized = true;
}

// Call the function, or return null if profiler is not found
if(fn) return fn(type, name);

if (fn) return fn(type, name);
else return 0;
}

static void _call_coz_pre_block() {
static unsigned char initialized = dlsym == 0;
static void (*fn)();

if (!initialized) {
void* p = dlsym(RTLD_DEFAULT, "_coz_pre_block");
memcpy(&fn, &p, sizeof(p));
initialized = true;
}
if (fn) return fn();
}

static void _call_coz_post_block(bool skip_delays) {
static unsigned char initialized = dlsym == 0;
static void (*fn)(bool);

if (!initialized) {
void* p = dlsym(RTLD_DEFAULT, "_coz_post_block");
memcpy(&fn, &p, sizeof(p));
initialized = true;
}
if (fn) return fn(skip_delays);
}

static void _call_coz_wake_other() {
static unsigned char initialized = dlsym == 0;
static void (*fn)();

if (!initialized) {
void* p = dlsym(RTLD_DEFAULT, "_coz_wake_other");
memcpy(&fn, &p, sizeof(p));
initialized = true;
}
if (fn) return fn();
}

// Macro to initialize and increment a counter
#define COZ_INCREMENT_COUNTER(type, name) \
if(1) { \
Expand All @@ -77,17 +115,98 @@ static coz_counter_t* _call_coz_get_counter(int type, const char* name) {
} \
}

#define STR2(x) #x
#define STR2(x) #x
#define STR(x) STR2(x)

/// Indicate progress for the counter with the given name.
///
/// Example:
/// ```
/// while (more_events()) {
/// COZ_PROGRESS_NAMED("event_processed");
/// process_event();
/// }
/// ```
#define COZ_PROGRESS_NAMED(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_THROUGHPUT, name)

/// Indicate progress for the counter named implicitly after the file and line
/// number it is placed on.
///
/// Example:
/// ```
/// while (more_events()) {
/// COZ_PROGRESS;
/// process_event();
/// }
/// ```
#define COZ_PROGRESS COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_THROUGHPUT, __FILE__ ":" STR(__LINE__))
#define COZ_BEGIN(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_BEGIN, name)
#define COZ_END(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_END, name)

/// Call before (possibly) blocking (e.g. if you're about to maybe block on a
/// futex).
///
/// Example:
/// ```
/// lock() {
/// int expected = 0;
/// if (!std::atomic_compare_exchange_weak(&futex, &expected, 1)) {
/// COZ_PRE_BLOCK();
/// syscall(SYS_futex, &futex, FUTEX_WAIT, ...);
/// COZ_POST_BLOCK();
/// }
/// }
/// ```
#define COZ_PRE_BLOCK() _call_coz_pre_block()

/// Call after unblocking. If skip_delays is true, all delays inserted during
/// the blocked period will be skipped.
///
/// Example:
/// ```
/// lock() {
/// int expected = 0;
/// if (!std::atomic_compare_exchange_weak(&futex, &expected, 1)) {
/// COZ_PRE_BLOCK();
/// syscall(SYS_futex, &futex, FUTEX_WAIT, ...);
/// COZ_POST_BLOCK();
/// }
/// }
/// ```
#define COZ_POST_BLOCK(skip_delays) _call_coz_post_block(skip_delays)

/// Ensure a thread has executed all the required delays before possibly
/// unblocking another thread.
///
/// Example:
/// ```
/// // Unlocking the futex.
/// unlock() {
/// let have_waiters = ...;
/// std::atomic_store(&futex, 0);
/// if (have_waiters) {
/// COZ_WAKE_OTHER();
/// syscall(SYS_FUTEX, &futex, FUTEX_WAKE, ...);
/// }
/// }
/// ```
#define COZ_WAKE_OTHER() _call_coz_wake_other()

#if defined(__cplusplus)
}
#endif

#else

#define _COZ_NOOP() do {} while(0)
#define COZ_PROGRESS_NAMED(name) _COZ_NOOP()
#define COZ_PROGRESS() _COZ_NOOP()
#define COZ_BEGIN(name) _COZ_NOOP()
#define COZ_END(name) _COZ_NOOP()
#define COZ_PRE_BLOCK() _COZ_NOOP()
#define COZ_POST_BLOCK(skip_delay) _COZ_NOOP()
#define COZ_WAKE_OTHER() _COZ_NOOP()

#endif

#endif
18 changes: 15 additions & 3 deletions libcoz/libcoz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,35 @@ extern "C" coz_counter_t* _coz_get_counter(progress_point_type t, const char* na
throughput_point* p = profiler::get_instance().get_throughput_point(name);
if(p) return p->get_counter_struct();
else return nullptr;

} else if(t == progress_point_type::begin) {
latency_point* p = profiler::get_instance().get_latency_point(name);
if(p) return p->get_begin_counter_struct();
else return nullptr;

} else if(t == progress_point_type::end) {
latency_point* p = profiler::get_instance().get_latency_point(name);
if(p) return p->get_end_counter_struct();
else return nullptr;

} else {
WARNING << "Unknown progress point type " << ((int)t) << " named " << name;
return nullptr;
}
}

extern "C" void _coz_pre_block() {
profiler::get_instance().pre_block();
}

extern "C" void _coz_post_block(bool skip_delays) {
profiler::get_instance().post_block(skip_delays);
}

extern "C" void _coz_wake_other() {
profiler::get_instance().catch_up();
}

/**
* Read a link's contents and return it as a string
*/
Expand Down
3 changes: 2 additions & 1 deletion libcoz/profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ class profiler {
state->pre_block_time = _global_delay.load();
}

/// Call after unblocking. If by_thread is true, delays will be skipped
/// Call after unblocking. If skip_delays is true, all delays inserted
/// during the blocked period will be skipped.
void post_block(bool skip_delays) {
thread_state* state = get_thread_state();
if(!state)
Expand Down