34#ifdef KICAD_USE_VALGRIND
35#include <valgrind/valgrind.h>
37#ifdef KICAD_SANITIZE_THREADS
38#include <sanitizer/tsan_interface.h>
40#ifdef KICAD_SANITIZE_ADDRESS
41#include <sanitizer/asan_interface.h>
44#include <libcontext.h>
60#ifdef KICAD_SANITIZE_ADDRESS
61#include <sanitizer/asan_interface.h>
64 void __sanitizer_start_switch_fiber(
void** fake_stack_save,
65 const void* bottom,
size_t size);
66 void __sanitizer_finish_switch_fiber(
void* fake_stack_save,
67 const void** bottom_old,
72static bool& GetAsanFiberSwitchState()
74 thread_local bool asan_fiber_switch_active =
false;
75 return asan_fiber_switch_active;
79static bool IsASanEnabled()
81 static std::optional<bool> asan_enabled;
82 if (!asan_enabled.has_value())
84 asan_enabled = (__has_feature(address_sanitizer) );
86 return asan_enabled.value();
112template <
typename ReturnType,
typename ArgType>
137 libcontext::fcontext_t
ctx;
139#ifdef KICAD_SANITIZE_THREADS
143#ifdef KICAD_SANITIZE_ADDRESS
144 void* asan_fake_stack;
145 const void* asan_stack_bottom;
146 size_t asan_stack_size;
147 bool asan_stack_registered;
152#ifdef KICAD_SANITIZE_THREADS
153 ,tsan_fiber( nullptr )
154 ,own_tsan_fiber( true )
156#ifdef KICAD_SANITIZE_ADDRESS
157 ,asan_fake_stack( nullptr )
158 ,asan_stack_bottom( nullptr )
159 ,asan_stack_size( 0 )
160 ,asan_stack_registered( false )
166#ifdef KICAD_SANITIZE_THREADS
169 __tsan_destroy_fiber( tsan_fiber );
171#ifdef KICAD_SANITIZE_ADDRESS
173 if( asan_stack_registered && IsASanEnabled() )
177 asan_stack_registered =
false;
208#ifdef KICAD_SANITIZE_THREADS
214 reinterpret_cast<intptr_t
>( &args ) );
250 COROUTINE( std::function<ReturnType( ArgType )> aEntry ) :
258#ifdef KICAD_USE_VALGRIND
259 ,m_valgrind_stack( 0 )
267#ifdef KICAD_USE_VALGRIND
268 VALGRIND_STACK_DEREGISTER( m_valgrind_stack );
416#ifdef KICAD_SANITIZE_THREADS
418 m_caller.tsan_fiber = __tsan_get_current_fiber();
421#ifdef KICAD_SANITIZE_ADDRESS
422 if( IsASanEnabled() )
426 m_caller.asan_stack_bottom =
nullptr;
428 m_caller.asan_stack_registered =
false;
439#ifndef LIBCONTEXT_HAS_OWN_STACK
445 std::size_t pages = (
m_stacksize + systemPageSize - 1 ) / systemPageSize;
448 stackSize = ( pages + 1 ) * systemPageSize;
451 m_stack.get_deleter().SetSize( stackSize );
458 sp =
static_cast<char*
>(
m_stack.get() ) + stackSize;
460#ifdef KICAD_USE_VALGRIND
461 m_valgrind_stack = VALGRIND_STACK_REGISTER( sp,
m_stack.get() );
463#ifdef KICAD_SANITIZE_ADDRESS
464 if( IsASanEnabled() )
468 m_callee.asan_stack_size = stackSize;
470 m_callee.asan_stack_registered =
true;
481#ifdef KICAD_SANITIZE_THREADS
483 m_callee.tsan_fiber = __tsan_create_fiber( 0 );
486 __tsan_set_fiber_name(
m_callee.tsan_fiber,
"Coroutine fiber" );
493 return jumpIn( aInvArgs );
496#ifndef LIBCONTEXT_HAS_OWN_STACK
502 void operator()(
void* aMem )
noexcept { ::VirtualFree( aMem, 0, MEM_RELEASE ); }
514 static std::optional<size_t> systemPageSize;
516 if( !systemPageSize.has_value() )
520 ::GetSystemInfo( &si );
521 systemPageSize =
static_cast<size_t>( si.dwPageSize );
523 int size = getpagesize();
524 systemPageSize =
static_cast<size_t>( size );
528 return systemPageSize.value();
535 void* mem = ::VirtualAlloc( 0, aAllocSize, MEM_COMMIT, PAGE_READWRITE );
538 throw std::bad_alloc();
540 void* mem = ::mmap( 0, aAllocSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 );
542 if( mem == (
void*) -1 )
543 throw std::bad_alloc();
549 static inline void GuardMemory(
void* aAddress,
size_t aGuardSize )
553 BOOL
res = ::VirtualProtect( aAddress, aGuardSize,
554 PAGE_READWRITE | PAGE_GUARD, &old_prot );
556 bool res = ( 0 == ::mprotect( aAddress, aGuardSize, PROT_NONE ) );
566#ifdef KICAD_SANITIZE_THREADS
568 m_caller.tsan_fiber = __tsan_get_current_fiber();
571#ifdef KICAD_SANITIZE_ADDRESS
572 if( IsASanEnabled() )
576 m_caller.asan_stack_bottom =
nullptr;
607#ifdef KICAD_SANITIZE_ADDRESS
609 if( IsASanEnabled() && !GetAsanFiberSwitchState()
612 GetAsanFiberSwitchState() =
true;
613 __sanitizer_start_switch_fiber( &
m_caller.asan_fake_stack,
618#ifdef KICAD_SANITIZE_THREADS
620 __tsan_switch_to_fiber(
m_callee.tsan_fiber, 0 );
625 reinterpret_cast<intptr_t
>( args ) )
627#ifdef KICAD_SANITIZE_ADDRESS
629 if( IsASanEnabled() && GetAsanFiberSwitchState() )
631 __sanitizer_finish_switch_fiber(
m_caller.asan_fake_stack,
634 GetAsanFiberSwitchState() =
false;
646#ifdef KICAD_SANITIZE_ADDRESS
648 if( IsASanEnabled() && !GetAsanFiberSwitchState() )
650 GetAsanFiberSwitchState() =
true;
651 __sanitizer_start_switch_fiber( &
m_callee.asan_fake_stack,
656#ifdef KICAD_SANITIZE_THREADS
658 __tsan_switch_to_fiber(
m_caller.tsan_fiber, 0 );
665 reinterpret_cast<intptr_t
>( &args ) )
668#ifdef KICAD_SANITIZE_ADDRESS
670 if( IsASanEnabled() && GetAsanFiberSwitchState() )
672 __sanitizer_finish_switch_fiber(
m_callee.asan_fake_stack,
675 GetAsanFiberSwitchState() =
false;
686#ifdef KICAD_SANITIZE_ADDRESS
688 void LogASanStatus()
const
690 if( !IsASanEnabled() )
694 wxT(
"ASAN Coroutine Status: fiber_switch_active=%s, "
695 "caller_bottom=%p, caller_size=%zu, "
696 "callee_bottom=%p, callee_size=%zu, callee_registered=%s"),
697 GetAsanFiberSwitchState() ?
"yes" :
"no",
700 m_callee.asan_stack_registered ?
"yes" :
"no" );
704#ifndef LIBCONTEXT_HAS_OWN_STACK
706 std::unique_ptr<char[], struct STACK_DELETER>
m_stack;
711 std::function<ReturnType( ArgType )>
m_func;
716 typename std::remove_reference<ArgType>::type*
m_args;
729#ifdef KICAD_USE_VALGRIND
730 uint32_t m_valgrind_stack;
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
CONTEXT_T * m_mainStackContext
void SetMainStack(CONTEXT_T *aStack)
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
void Continue(INVOCATION_ARGS *args)
std::function< void()> m_mainStackFunction
bool Call(ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
CALL_CONTEXT * m_callContext
Main stack information.
void KiYield(ReturnType &aRetVal)
KiYield with a value.
void KiYield()
Stop execution of the coroutine and returns control to the caller.
CONTEXT_T m_caller
Saved caller context.
bool Resume()
Resume execution of a previously yielded coroutine.
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Create a coroutine from a delegate object.
static void callerStub(intptr_t aData)
CONTEXT_T m_callee
Saved coroutine context.
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
std::unique_ptr< char[], struct STACK_DELETER > m_stack
Coroutine stack.
const ReturnType & ReturnValue() const
Return the yielded value (the argument KiYield() was called with).
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
static void * MapMemory(size_t aAllocSize)
Map a page-aligned memory region into our address space.
static size_t SystemPageSize()
The size of the mappable memory page size.
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
bool Call(const COROUTINE &aCor, ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
std::function< ReturnType(ArgType)> m_func
static void GuardMemory(void *aAddress, size_t aGuardSize)
Change protection of memory page(s) to act as stack guards.
bool Resume(const COROUTINE &aCor)
Resume execution of a previously yielded coroutine.
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Create a coroutine from a member method of an object.
void RunMainStack(std::function< void()> func)
Run a functor inside the application main stack context.
std::remove_reference< ArgType >::type * m_args
Pointer to coroutine entry arguments stripped of references to avoid compiler errors.
int m_CoroutineStackSize
Configure the coroutine stack size in bytes.
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
libcontext::fcontext_t ctx
enum COROUTINE::INVOCATION_ARGS::@35 type
A functor that frees the stack.
void SetSize(std::size_t aSize)
void operator()(void *aMem) noexcept
wxLogTrace helper definitions.