Skip to content

Commit

Permalink
Implementing start_switch_fiber and finish_switch_fiber for uthread t…
Browse files Browse the repository at this point in the history
…o enable address sanitizer
  • Loading branch information
4kangjc committed Dec 23, 2022
1 parent b346c2f commit d08581c
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 7 deletions.
2 changes: 1 addition & 1 deletion async_simple/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if(${UTHREAD})
# for the user thread.
# FIXME2: the new added sanitize action: address-use-after-return
# can't handle stackful coroutine well too. So disable it as a workaround now.
set_property(SOURCE ${uthread_src} PROPERTY COMPILE_OPTIONS "-fno-sanitize=address;-fsanitize-address-use-after-return=never")
# set_property(SOURCE ${uthread_src} PROPERTY COMPILE_OPTIONS "-fno-sanitize=address;-fsanitize-address-use-after-return=never")
#endif()
endif()
file(GLOB uthread_asm_src "uthread/internal/${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}/*.S")
Expand Down
12 changes: 12 additions & 0 deletions async_simple/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@
#define AS_INLINE __attribute__((__always_inline__)) inline
#endif

#ifdef __clang__
#if __has_feature(address_sanitizer)
#define AS_INTERNAL_USE_ASAN 1
#endif // __has_feature(address_sanitizer)
#endif // __clang__

#ifdef __GNUC__
#ifdef __SANITIZE_ADDRESS__ // GCC
#define AS_INTERNAL_USE_ASAN 1
#endif // __SANITIZE_ADDRESS__
#endif // __GNUC__

namespace async_simple {
// Different from assert, logicAssert is meaningful in
// release mode. logicAssert should be used in case that
Expand Down
66 changes: 61 additions & 5 deletions async_simple/uthread/internal/thread.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,36 @@ namespace async_simple {
namespace uthread {
namespace internal {

#ifdef AS_INTERNAL_USE_ASAN

extern "C" {
void __sanitizer_start_switch_fiber(void** fake_stack_save,
const void* stack_bottom,
size_t stack_size);
void __sanitizer_finish_switch_fiber(void* fake_stack_save,
const void** stack_bottom_old,
size_t* stack_size_old);
}

inline void start_switch_fiber(jmp_buf_link* context) {
__sanitizer_start_switch_fiber(&context->asan_fack_stack,
context->asan_stack_bottom,
context->asan_stack_size);
}

inline void finish_switch_fiber(jmp_buf_link* context) {
__sanitizer_finish_switch_fiber(context->asan_fack_stack,
&context->asan_stack_bottom,
&context->asan_stack_size);
}

#else

inline void start_switch_fiber(jmp_buf_link* context) {}
inline void finish_switch_fiber(jmp_buf_link* context) {}

#endif // AS_INTERNAL_USE_ASAN

thread_local jmp_buf_link g_unthreaded_context;
thread_local jmp_buf_link* g_current_context = nullptr;

Expand All @@ -55,16 +85,41 @@ inline void jmp_buf_link::switch_in() {
link = std::exchange(g_current_context, this);
if (!link)
AS_UNLIKELY { link = &g_unthreaded_context; }
start_switch_fiber(this);
// `thread` is currently only used in `s_main`
fcontext = _fl_jump_fcontext(fcontext, thread).fctx;
finish_switch_fiber(this);
}

inline void jmp_buf_link::switch_out() {
g_current_context = link;
start_switch_fiber(link);
link->fcontext = _fl_jump_fcontext(link->fcontext, thread).fctx;
finish_switch_fiber(link);
}

inline void jmp_buf_link::initial_switch_in_completed() {}
inline void jmp_buf_link::initial_switch_in_completed() {
#ifdef AS_INTERNAL_USE_ASAN
// This is a new thread and it doesn't have the fake stack yet. ASan will
// create it lazily, for now just pass nullptr.
__sanitizer_finish_switch_fiber(nullptr, &link->asan_stack_bottom,
&link->asan_stack_size);
#endif
}

inline void jmp_buf_link::final_switch_out() {
g_current_context = link;
#ifdef AS_INTERNAL_USE_ASAN
// Since the thread is about to die we pass nullptr as fake_stack_save
// argument so that ASan knows it can destroy the fake stack if it exists.
__sanitizer_start_switch_fiber(nullptr, link->asan_stack_bottom,
link->asan_stack_size);
#endif
_fl_jump_fcontext(link->fcontext, thread);

// never reach here
assert(false);
}

thread_context::thread_context(std::function<void()> func, size_t stack_size)
: stack_size_(stack_size ? stack_size : get_base_stack_size()),
Expand All @@ -87,6 +142,10 @@ void thread_context::setup() {
context_.fcontext = _fl_make_fcontext(stack_.get() + stack_size_,
stack_size_, thread_context::s_main);
context_.thread = this;
#ifdef AS_INTERNAL_USE_ASAN
context_.asan_stack_bottom = stack_.get();
context_.asan_stack_size = stack_size_;
#endif
context_.switch_in();
}

Expand Down Expand Up @@ -122,10 +181,7 @@ void thread_context::main() {
done_.setException(std::current_exception());
}

context_.switch_out();

// never reach here
assert(false);
context_.final_switch_out();
}

namespace thread_impl {
Expand Down
9 changes: 9 additions & 0 deletions async_simple/uthread/internal/thread_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#ifndef ASYNC_SIMPLE_UTHREAD_INTERNAL_UTHREAD_IMPL_H
#define ASYNC_SIMPLE_UTHREAD_INTERNAL_UTHREAD_IMPL_H

#include "async_simple/Common.h"

namespace async_simple {
namespace uthread {
namespace internal {
Expand All @@ -44,10 +46,17 @@ struct jmp_buf_link {
jmp_buf_link* link = nullptr;
thread_context* thread = nullptr;

#ifdef AS_INTERNAL_USE_ASAN
const void* asan_stack_bottom = nullptr;
std::size_t asan_stack_size = 0;
void* asan_fack_stack = nullptr;
#endif

public:
void switch_in();
void switch_out();
void initial_switch_in_completed();
void final_switch_out();
};

extern thread_local jmp_buf_link* g_current_context;
Expand Down
2 changes: 1 addition & 1 deletion async_simple/uthread/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
# for the user thread.
# FIXME2: the new added sanitize action: address-use-after-return
# can't handle stackful coroutine well too. So disable it as a workaround now.
set_property(SOURCE ${uthread_test_src} PROPERTY COMPILE_OPTIONS "-fno-sanitize=address;-fsanitize-address-use-after-return=never")
# set_property(SOURCE ${uthread_test_src} PROPERTY COMPILE_OPTIONS "-fno-sanitize=address;-fsanitize-address-use-after-return=never")
endif()

add_executable(async_simple_uthread_test ${uthread_test_src} ${PROJECT_SOURCE_DIR}/async_simple/test/dotest.cpp)
Expand Down

1 comment on commit d08581c

@github-actions
Copy link

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: d08581c Previous: 06634c1 Ratio
async_simple_Lazy_collectAll 14341232.744681185 ns/iter 9472943.986486448 ns/iter 1.51
Uthread_read_diff_size_bench/256/iterations:3 96029548.99999835 ns/iter 27697850.666659936 ns/iter 3.47
Uthread_read_diff_size_bench/1024/iterations:3 236130653.6666632 ns/iter 96815727.0000063 ns/iter 2.44
Uthread_same_read_bench/32/iterations:3 3635312.000000112 ns/iter 811566.9999995134 ns/iter 4.48
Uthread_same_read_bench/128/iterations:3 4537731.666677776 ns/iter 2812668.3333293083 ns/iter 1.61
ThreadPool_noWorkSteal 9905392726.000002 ns/iter 4576536926.000017 ns/iter 2.16
Lazy_read_diff_size_bench/32/iterations:3 10099220.333340025 ns/iter 6148637.333334743 ns/iter 1.64
Lazy_read_diff_size_bench/128/iterations:3 35608377.666657515 ns/iter 18579045.999994528 ns/iter 1.92
Lazy_read_diff_size_bench/256/iterations:3 76987514.66667394 ns/iter 47959899.33333544 ns/iter 1.61
Lazy_read_diff_size_bench/2048/iterations:3 632088961.3333331 ns/iter 363930783.0000007 ns/iter 1.74
Lazy_same_read_bench/128/iterations:3 1989210.3333252938 ns/iter 1203367.3333367764 ns/iter 1.65
Lazy_same_read_bench/2048/iterations:3 37872063.999998316 ns/iter 20880180.999995444 ns/iter 1.81
Uthread_call_depth_bench/1/iterations:3 2366.6666682705304 ns/iter 1533.3333275672583 ns/iter 1.54
Uthread_call_depth_bench/2/iterations:3 2133.333339315868 ns/iter 1133.3333323667223 ns/iter 1.88
Uthread_call_depth_bench/16/iterations:3 2133.333339315868 ns/iter 1166.6666637211165 ns/iter 1.83
Uthread_call_depth_bench/32/iterations:3 2200.0000020246566 ns/iter 1066.666669657934 ns/iter 2.06
Uthread_call_depth_bench/64/iterations:3 2499.9999936881068 ns/iter 1100.0000010123283 ns/iter 2.27
Uthread_call_depth_bench/256/iterations:3 2733.6666524509687 ns/iter 999.9999974752427 ns/iter 2.73
Uthread_call_depth_bench/512/iterations:3 2833.6666749358606 ns/iter 1299.999998612596 ns/iter 2.18
Lazy_call_depth_bench/8/iterations:3 5066.999998841008 ns/iter 2566.666665870798 ns/iter 1.97
Lazy_call_depth_bench/16/iterations:3 6099.999988388542 ns/iter 2666.6666599339806 ns/iter 2.29
Lazy_call_depth_bench/32/iterations:3 7500.000000012126 ns/iter 3333.3333249174757 ns/iter 2.25
Lazy_call_depth_bench/64/iterations:3 11500.333338669103 ns/iter 6333.333326817107 ns/iter 1.82
Lazy_call_depth_bench/128/iterations:3 18500.33333994361 ns/iter 9433.333332253824 ns/iter 1.96
Lazy_call_depth_bench/256/iterations:3 33633.99999519364 ns/iter 15799.999999899228 ns/iter 2.13
Lazy_call_depth_bench/512/iterations:3 50900.99998975953 ns/iter 26566.666662120042 ns/iter 1.92
Lazy_call_depth_bench/1024/iterations:3 113868.9999985824 ns/iter 50700.000002734676 ns/iter 2.25
Lazy_call_depth_bench/2048/iterations:3 238238.66666816684 ns/iter 103233.33333417395 ns/iter 2.31

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.