#include "il2cpp-config.h" #include "StackTrace.h" #include #if IL2CPP_DEBUGGER_ENABLED #include "il2cpp-debugger.h" #endif // IL2CPP_DEBUGGER_ENABLED #if IL2CPP_ENABLE_STACKTRACES #ifdef IL2CPP_ENABLE_NATIVE_STACKTRACES #include "os/Event.h" #include "os/StackTrace.h" #include "os/Thread.h" #include "vm-utils/NativeSymbol.h" #endif // IL2CPP_ENABLE_NATIVE_STACKTRACES #include "os/ThreadLocalValue.h" #endif // IL2CPP_ENABLE_STACKTRACES namespace mono { namespace vm { #if IL2CPP_ENABLE_STACKTRACES class MethodStack { protected: il2cpp::os::ThreadLocalValue s_StackFrames; inline StackFrames* GetStackFramesRaw() { StackFrames* stackFrames; il2cpp::os::ErrorCode result = s_StackFrames.GetValue(reinterpret_cast(&stackFrames)); assert(result == il2cpp::os::kErrorCodeSuccess); return stackFrames; } public: inline void InitializeForCurrentThread() { if (GetStackFramesRaw() != NULL) return; StackFrames* stackFrames = new StackFrames(); stackFrames->reserve(64); il2cpp::os::ErrorCode result = s_StackFrames.SetValue(stackFrames); assert(result == il2cpp::os::kErrorCodeSuccess); } inline void CleanupForCurrentThread() { StackFrames* frames = GetStackFramesRaw(); if (frames == NULL) return; delete frames; il2cpp::os::ErrorCode result = s_StackFrames.SetValue(NULL); assert(result == il2cpp::os::kErrorCodeSuccess); } }; #if IL2CPP_ENABLE_STACKTRACE_SENTRIES class StacktraceSentryMethodStack : public MethodStack { public: inline const StackFrames* GetStackFrames() { return GetStackFramesRaw(); } inline bool GetStackFrameAt(int32_t depth, MonoStackFrameInfo& frame) { const StackFrames& frames = *GetStackFramesRaw(); if (static_cast(frames.size()) + depth < 1) return false; frame = frames[frames.size() - 1 + depth]; return true; } inline void PushFrame(MonoStackFrameInfo& frame) { GetStackFramesRaw()->push_back(frame); #if IL2CPP_DEBUGGER_ENABLED il2cpp_debugger_method_entry(frame); #endif } inline void PopFrame() { StackFrames* stackFrames = GetStackFramesRaw(); #if IL2CPP_DEBUGGER_ENABLED MonoStackFrameInfo frame = stackFrames->back(); #endif stackFrames->pop_back(); #if IL2CPP_DEBUGGER_ENABLED il2cpp_debugger_method_exit(frame); #endif } }; #endif // IL2CPP_ENABLE_STACKTRACE_SENTRIES #if IL2CPP_ENABLE_NATIVE_STACKTRACES class NativeMethodStack : public MethodStack { static bool GetStackFramesCallback(Il2CppMethodPointer frame, void* context) { MonoMethod* method = const_cast(il2cpp::utils::NativeSymbol::GetMethodFromNativeSymbol(frame)); StackFrames* stackFrames = static_cast(context); if (method != NULL) { MonoStackFrameInfo frameInfo; memset(&frameInfo, 0, sizeof(frameInfo)); frameInfo.method = method; frameInfo.actual_method = method; frameInfo.type = FRAME_TYPE_MANAGED; frameInfo.managed = 1; frameInfo.il_offset = 0; frameInfo.native_offset = 0; frameInfo.ji = (MonoJitInfo*)-1; stackFrames->push_back(frameInfo); } return true; } struct GetStackFrameAtContext { int32_t currentDepth; const MonoMethod* method; }; static bool GetStackFrameAtCallback(Il2CppMethodPointer frame, void* context) { const MonoMethod* method = il2cpp::utils::NativeSymbol::GetMethodFromNativeSymbol(frame); GetStackFrameAtContext* ctx = static_cast(context); if (method != NULL) { if (ctx->currentDepth == 0) { ctx->method = method; return false; } ctx->currentDepth++; } return true; } public: inline const StackFrames* GetStackFrames() { StackFrames* stackFrames = GetStackFramesRaw(); stackFrames->clear(); il2cpp::os::StackTrace::WalkStack(&NativeMethodStack::GetStackFramesCallback, stackFrames, il2cpp::os::StackTrace::kFirstCalledToLastCalled); return stackFrames; } inline bool GetStackFrameAt(int32_t depth, MonoStackFrameInfo& frame) { GetStackFrameAtContext context = { depth, NULL }; il2cpp::os::StackTrace::WalkStack(&NativeMethodStack::GetStackFrameAtCallback, &context, il2cpp::os::StackTrace::kLastCalledToFirstCalled); if (context.method != NULL) { MonoMethod* method = const_cast(context.method); frame.method = method; frame.actual_method = method; frame.type = FRAME_TYPE_MANAGED; frame.managed = 1; frame.il_offset = 0; frame.native_offset = 0; frame.ji = (MonoJitInfo*)-1; return true; } return false; } inline void PushFrame(MonoStackFrameInfo& frame) { } inline void PopFrame() { } }; #endif // IL2CPP_ENABLE_NATIVE_STACKTRACES #else static StackFrames s_EmptyStack; class NoOpMethodStack { public: inline void InitializeForCurrentThread() { } inline void CleanupForCurrentThread() { } inline const StackFrames* GetStackFrames() { return &s_EmptyStack; } inline bool GetStackFrameAt(int32_t depth, MonoStackFrameInfo& frame) { return false; } inline void PushFrame(MonoStackFrameInfo& frame) { } inline void PopFrame() { } }; #endif // IL2CPP_ENABLE_STACKTRACES #if IL2CPP_ENABLE_STACKTRACES #if IL2CPP_ENABLE_STACKTRACE_SENTRIES StacktraceSentryMethodStack s_MethodStack; #elif IL2CPP_ENABLE_NATIVE_STACKTRACES NativeMethodStack s_MethodStack; #endif #else NoOpMethodStack s_MethodStack; #endif // IL2CPP_ENABLE_STACKTRACES // Current thread functions void StackTrace::InitializeStackTracesForCurrentThread() { s_MethodStack.InitializeForCurrentThread(); } void StackTrace::CleanupStackTracesForCurrentThread() { s_MethodStack.CleanupForCurrentThread(); } const StackFrames* StackTrace::GetStackFrames() { return s_MethodStack.GetStackFrames(); } bool StackTrace::GetStackFrameAt(int32_t depth, MonoStackFrameInfo& frame) { assert(depth <= 0 && "Frame depth must be 0 or less"); return s_MethodStack.GetStackFrameAt(depth, frame); } void StackTrace::WalkFrameStack(MonoInternalStackWalk callback, MonoContext* context, void *user_data) { const StackFrames& frames = *GetStackFrames(); for (StackFrames::const_reverse_iterator it = frames.rbegin(); it != frames.rend(); ++it) { if (callback(const_cast(&*it), context, user_data)) break; } } void StackTrace::PushFrame(MonoStackFrameInfo& frame) { s_MethodStack.PushFrame(frame); } void StackTrace::PopFrame() { s_MethodStack.PopFrame(); } // Remote thread functions struct GetThreadFrameAtContext { //il2cpp::os::Event apcDoneEvent; int32_t depth; MonoStackFrameInfo* frame; bool hasResult; }; struct WalkThreadFrameStackContext { //il2cpp::os::Event apcDoneEvent; MonoInternalStackWalk callback; MonoContext* context; void *user_data; }; struct GetThreadStackDepthContext { //il2cpp::os::Event apcDoneEvent; int32_t stackDepth; }; struct GetThreadTopFrameContext { //il2cpp::os::Event apcDoneEvent; MonoStackFrameInfo* frame; bool hasResult; }; static void STDCALL GetThreadFrameAtCallback(void* context) { GetThreadFrameAtContext* ctx = static_cast(context); ctx->hasResult = StackTrace::GetStackFrameAt(ctx->depth, *ctx->frame); //ctx->apcDoneEvent.Set(); } bool StackTrace::GetThreadStackFrameAt(MonoThread* thread, int32_t depth, MonoStackFrameInfo& frame) { #if IL2CPP_ENABLE_STACKTRACES //GetThreadFrameAtContext apcContext; //apcContext.depth = depth; //apcContext.frame = &frame; //thread->GetInternalThread()->handle->QueueUserAPC(GetThreadFrameAtCallback, &apcContext); //apcContext.apcDoneEvent.Wait(); //return apcContext.hasResult; return false; #else return false; #endif } static void STDCALL WalkThreadFrameStackCallback(void* context) { WalkThreadFrameStackContext* ctx = static_cast(context); StackTrace::WalkFrameStack(ctx->callback, ctx->context, ctx->user_data); //ctx->apcDoneEvent.Set(); } void StackTrace::WalkThreadFrameStack(MonoThread* thread, MonoInternalStackWalk callback, MonoContext *context, void *user_data) { #if IL2CPP_ENABLE_STACKTRACES WalkThreadFrameStackContext apcContext; apcContext.callback = callback; apcContext.context = context; apcContext.user_data = user_data; //thread->GetInternalThread()->handle->QueueUserAPC(WalkThreadFrameStackCallback, &apcContext); //apcContext.apcDoneEvent.Wait(); #endif } static void STDCALL GetThreadStackDepthCallback(void* context) { GetThreadStackDepthContext* ctx = static_cast(context); ctx->stackDepth = static_cast(StackTrace::GetStackDepth()); //ctx->apcDoneEvent.Set(); } int32_t StackTrace::GetThreadStackDepth(MonoThread* thread) { #if IL2CPP_ENABLE_STACKTRACES //GetThreadStackDepthContext apcContext; //thread->GetInternalThread()->handle->QueueUserAPC(GetThreadStackDepthCallback, &apcContext); //apcContext.apcDoneEvent.Wait(); //return apcContext.stackDepth; return 0; #else return 0; #endif } static void STDCALL GetThreadTopFrameCallback(void* context) { GetThreadTopFrameContext* ctx = static_cast(context); ctx->hasResult = StackTrace::GetTopStackFrame(*ctx->frame); //ctx->apcDoneEvent.Set(); } bool StackTrace::GetThreadTopStackFrame(MonoThread* thread, MonoStackFrameInfo& frame) { #if IL2CPP_ENABLE_STACKTRACES //GetThreadTopFrameContext apcContext; //apcContext.frame = &frame; //thread->GetInternalThread()->handle->QueueUserAPC(GetThreadTopFrameCallback, &apcContext); //apcContext.apcDoneEvent.Wait(); //return apcContext.hasResult; return false; #else return false; #endif } } }