123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627 |
- #include "il2cpp-config.h"
- #include "StackTrace.h"
- #include "il2cpp-object-internals.h"
- #include "os/Event.h"
- #include "os/StackTrace.h"
- #include "os/Thread.h"
- #include "os/ThreadLocalValue.h"
- #include "os/Image.h"
- #include "vm/Method.h"
- #include "vm/Thread.h"
- #include "vm/Type.h"
- #include "vm-utils/Debugger.h"
- #include "vm-utils/NativeSymbol.h"
- #include "vm-utils/DebugSymbolReader.h"
- #include "vm-utils/Debugger.h"
- #include <map>
- #include <cstdio>
- namespace il2cpp
- {
- namespace vm
- {
- #if IL2CPP_ENABLE_STACKTRACES
- class CachedInfo
- {
- int32_t m_depth;
- const void* m_stackPointer;
- public:
- CachedInfo() : m_depth(INT_MAX), m_stackPointer(NULL) {}
- void Update(int32_t depth, const void *stackPointer)
- {
- m_depth = depth;
- m_stackPointer = stackPointer;
- }
- bool CheckCondition(int32_t depth, const void *stackPointer) const
- {
- // We can use cached value if stack pointer is the same and not NULL, and 'depth' has been incremented since previous call
- return m_stackPointer != NULL && stackPointer == m_stackPointer && depth - 1 == m_depth;
- }
- };
- class MethodStack
- {
- protected:
- os::ThreadLocalValue s_StackFrames;
- os::ThreadLocalValue s_StoredCachedInfo;
- inline StackFrames* GetStackFramesRaw()
- {
- StackFrames* stackFrames = NULL;
- os::ErrorCode result = s_StackFrames.GetValue(reinterpret_cast<void**>(&stackFrames));
- Assert(result == os::kErrorCodeSuccess);
- return stackFrames;
- }
- inline CachedInfo* GetStoredCachedInfoRaw()
- {
- CachedInfo* storedCachedInfo = NULL;
- os::ErrorCode result = s_StoredCachedInfo.GetValue(reinterpret_cast<void**>(&storedCachedInfo));
- Assert(result == os::kErrorCodeSuccess);
- return storedCachedInfo;
- }
- public:
- inline void InitializeForCurrentThread()
- {
- if (GetStackFramesRaw() != NULL)
- return;
- StackFrames* stackFrames = new StackFrames();
- stackFrames->reserve(64);
- os::ErrorCode result = s_StackFrames.SetValue(stackFrames);
- Assert(result == os::kErrorCodeSuccess);
- CachedInfo* cachedInfo = new CachedInfo();
- result = s_StoredCachedInfo.SetValue(cachedInfo);
- Assert(result == os::kErrorCodeSuccess);
- }
- inline void CleanupForCurrentThread()
- {
- StackFrames* frames = GetStackFramesRaw();
- if (frames == NULL)
- return;
- delete frames;
- CachedInfo* cachedInfo = GetStoredCachedInfoRaw();
- if (cachedInfo == NULL)
- return;
- delete cachedInfo;
- os::ErrorCode result = s_StackFrames.SetValue(NULL);
- Assert(result == os::kErrorCodeSuccess);
- result = s_StoredCachedInfo.SetValue(NULL);
- Assert(result == os::kErrorCodeSuccess);
- }
- };
- #if IL2CPP_ENABLE_STACKTRACE_SENTRIES
- class StacktraceSentryMethodStack : public MethodStack
- {
- public:
- inline const StackFrames* GetStackFrames()
- {
- return GetStackFramesRaw();
- }
- inline const StackFrames* GetCachedStackFrames(int32_t depth, const void* stackPointer)
- {
- return GetStackFrames();
- }
- inline bool GetStackFrameAt(int32_t depth, Il2CppStackFrameInfo& frame)
- {
- const StackFrames& frames = *GetStackFramesRaw();
- if (static_cast<int>(frames.size()) + depth < 1)
- return false;
- frame = frames[frames.size() - 1 + depth];
- return true;
- }
- inline void PushFrame(Il2CppStackFrameInfo& frame)
- {
- GetStackFramesRaw()->push_back(frame);
- }
- inline void PopFrame()
- {
- StackFrames* stackFrames = GetStackFramesRaw();
- stackFrames->pop_back();
- }
- inline const void* GetStackPointer()
- {
- return nullptr;
- }
- };
- #endif // IL2CPP_ENABLE_STACKTRACE_SENTRIES
- #if IL2CPP_ENABLE_NATIVE_STACKTRACES
- #if IL2CPP_MONO_DEBUGGER
- class DebuggerMethodStack : public MethodStack
- {
- public:
- inline const StackFrames* GetStackFrames()
- {
- StackFrames* stackFrames = GetStackFramesRaw();
- if (stackFrames == NULL)
- return stackFrames;
- stackFrames->clear();
- utils::Debugger::GetStackFrames(stackFrames);
- return stackFrames;
- }
- inline const StackFrames* GetCachedStackFrames(int32_t depth, const void* stackPointer)
- {
- CachedInfo* cachedInfo = GetStoredCachedInfoRaw();
- const StackFrames* stackFrames = cachedInfo->CheckCondition(depth, stackPointer) ? GetStackFramesRaw() : GetStackFrames();
- cachedInfo->Update(depth, stackPointer);
- return stackFrames;
- }
- inline bool GetStackFrameAt(int32_t depth, Il2CppStackFrameInfo& frame)
- {
- const StackFrames& frames = *GetStackFrames();
- if (static_cast<int>(frames.size()) + depth < 1)
- return false;
- frame = frames[frames.size() - 1 + depth];
- return true;
- }
- inline void PushFrame(Il2CppStackFrameInfo& frame)
- {
- }
- inline void PopFrame()
- {
- }
- inline const void* GetStackPointer()
- {
- return nullptr;
- }
- };
- #else
- class NativeMethodStack : public MethodStack
- {
- static bool GetStackFramesCallback(Il2CppMethodPointer frame, void* context)
- {
- const MethodInfo* method = il2cpp::utils::NativeSymbol::GetMethodFromNativeSymbol(frame);
- StackFrames* stackFrames = static_cast<StackFrames*>(context);
- if (method != NULL)
- {
- bool frames_added = il2cpp::utils::DebugSymbolReader::AddStackFrames(reinterpret_cast<void*>(frame), stackFrames);
- if (!frames_added)
- {
- Il2CppStackFrameInfo frameInfo = { 0 };
- frameInfo.method = method;
- frameInfo.raw_ip = reinterpret_cast<uintptr_t>(frame) - reinterpret_cast<uintptr_t>(os::Image::GetImageBase());
- stackFrames->push_back(frameInfo);
- }
- }
- return true;
- }
- struct GetStackFrameAtContext
- {
- int32_t currentDepth;
- const MethodInfo* method;
- };
- static bool GetStackFrameAtCallback(Il2CppMethodPointer frame, void* context)
- {
- const MethodInfo* method = il2cpp::utils::NativeSymbol::GetMethodFromNativeSymbol(frame);
- GetStackFrameAtContext* ctx = static_cast<GetStackFrameAtContext*>(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();
- if (stackFrames == NULL)
- return stackFrames;
- stackFrames->clear();
- os::StackTrace::WalkStack(&NativeMethodStack::GetStackFramesCallback, stackFrames, os::StackTrace::kFirstCalledToLastCalled);
- return stackFrames;
- }
- // Avoiding calling GetStackFrames() method for the same stack trace with incremented 'depth' value
- inline const StackFrames* GetCachedStackFrames(int32_t depth, const void* stackPointer)
- {
- CachedInfo* cachedInfo = GetStoredCachedInfoRaw();
- const StackFrames* stackFrames = cachedInfo->CheckCondition(depth, stackPointer) ? GetStackFramesRaw() : GetStackFrames();
- cachedInfo->Update(depth, stackPointer);
- return stackFrames;
- }
- inline bool GetStackFrameAt(int32_t depth, Il2CppStackFrameInfo& frame)
- {
- GetStackFrameAtContext context = { depth, NULL };
- os::StackTrace::WalkStack(&NativeMethodStack::GetStackFrameAtCallback, &context, os::StackTrace::kLastCalledToFirstCalled);
- if (context.method != NULL)
- {
- frame.method = context.method;
- return true;
- }
- return false;
- }
- inline void PushFrame(Il2CppStackFrameInfo& frame)
- {
- }
- inline void PopFrame()
- {
- }
- // Returns SP value or nullptr if not implemented
- inline const void* GetStackPointer()
- {
- return os::StackTrace::GetStackPointer();
- }
- };
- #endif // IL2CPP_MONO_DEBUGGER
- #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 const StackFrames* GetCachedStackFrames(int32_t depth, const void* stackPointer)
- {
- return GetStackFrames();
- }
- inline bool GetStackFrameAt(int32_t depth, Il2CppStackFrameInfo& frame)
- {
- return false;
- }
- inline void PushFrame(Il2CppStackFrameInfo& frame)
- {
- }
- inline void PopFrame()
- {
- }
- inline const void* GetStackPointer()
- {
- return nullptr;
- }
- };
- #endif // IL2CPP_ENABLE_STACKTRACES
- #if IL2CPP_ENABLE_STACKTRACES
- #if IL2CPP_ENABLE_STACKTRACE_SENTRIES
- StacktraceSentryMethodStack s_MethodStack;
- #elif IL2CPP_ENABLE_NATIVE_STACKTRACES
- #if IL2CPP_MONO_DEBUGGER
- DebuggerMethodStack s_MethodStack;
- #else
- NativeMethodStack s_MethodStack;
- #endif
- #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();
- }
- const StackFrames* StackTrace::GetCachedStackFrames(int32_t depth)
- {
- return s_MethodStack.GetCachedStackFrames(depth, GetStackPointer());
- }
- bool StackTrace::GetStackFrameAt(int32_t depth, Il2CppStackFrameInfo& frame)
- {
- Assert(depth <= 0 && "Frame depth must be 0 or less");
- return s_MethodStack.GetStackFrameAt(depth, frame);
- }
- void StackTrace::WalkFrameStack(Il2CppFrameWalkFunc callback, void* context)
- {
- const StackFrames& frames = *GetStackFrames();
- for (StackFrames::const_iterator it = frames.begin(); it != frames.end(); it++)
- callback(&*it, context);
- }
- #if IL2CPP_TINY_DEBUGGER
- static std::map<const MethodInfo*, std::string> s_MethodNames;
- static std::string MethodNameFor(const MethodInfo* method)
- {
- auto cachedMethodNameEntry = s_MethodNames.find(method);
- if (cachedMethodNameEntry != s_MethodNames.end())
- return cachedMethodNameEntry->second;
- std::string methodName;
- methodName += vm::Method::GetFullName(method);
- methodName += " (";
- uint32_t numberOfParameters = vm::Method::GetParamCount(method);
- for (uint32_t j = 0; j < numberOfParameters; ++j)
- {
- const Il2CppType* parameterType = vm::Method::GetParam(method, j);
- methodName += vm::Type::GetName(parameterType, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME);
- if (j != numberOfParameters - 1)
- methodName += ", ";
- }
- methodName += ")";
- s_MethodNames[method] = methodName;
- return methodName;
- }
- const int s_StackTraceSize = 4096;
- static char s_StackTrace[s_StackTraceSize];
- static int s_StackTraceOffset = 0;
- static void AppendToStackTrace(const char* value)
- {
- if (s_StackTraceOffset < s_StackTraceSize)
- s_StackTraceOffset += snprintf(s_StackTrace + s_StackTraceOffset, s_StackTraceSize - s_StackTraceOffset, "%s", value);
- }
- const char* StackTrace::GetStackTrace()
- {
- // Since a pointer to a static buffer is used to store the stack trace, only
- // once thread should use it. Tiny does not support managed code on non-main
- // threads, so there is no need to use a thread local buffer here. Protect this
- // from access on multiple threads by not returning anyting on non-main threads.
- if (vm::Thread::Current() != vm::Thread::Main())
- return NULL;
- const StackFrames* frames = s_MethodStack.GetStackFrames();
- const size_t numberOfFramesToSkip = 1;
- int startFrame = (int)frames->size() - 1 - numberOfFramesToSkip;
- s_StackTraceOffset = 0;
- for (int i = startFrame; i > 0; i--)
- {
- if (i == startFrame)
- AppendToStackTrace("at ");
- else
- AppendToStackTrace(" at ");
- Il2CppStackFrameInfo stackFrame = (*frames)[i];
- AppendToStackTrace(MethodNameFor(stackFrame.method).c_str());
- if (stackFrame.filePath != NULL)
- {
- AppendToStackTrace(" in ");
- AppendToStackTrace(stackFrame.filePath);
- AppendToStackTrace(":");
- AppendToStackTrace(std::to_string(stackFrame.sourceCodeLineNumber).c_str());
- }
- if (i != 1)
- AppendToStackTrace("\n");
- }
- return s_StackTrace;
- }
- #endif
- void StackTrace::PushFrame(Il2CppStackFrameInfo& frame)
- {
- s_MethodStack.PushFrame(frame);
- }
- void StackTrace::PopFrame()
- {
- s_MethodStack.PopFrame();
- }
- const void* StackTrace::GetStackPointer()
- {
- return s_MethodStack.GetStackPointer();
- }
- // Remote thread functions
- struct GetThreadFrameAtContext
- {
- il2cpp::os::Event apcDoneEvent;
- int32_t depth;
- Il2CppStackFrameInfo* frame;
- bool hasResult;
- };
- struct WalkThreadFrameStackContext
- {
- il2cpp::os::Event apcDoneEvent;
- Il2CppFrameWalkFunc callback;
- void* userContext;
- };
- struct GetThreadStackDepthContext
- {
- il2cpp::os::Event apcDoneEvent;
- int32_t stackDepth;
- };
- struct GetThreadTopFrameContext
- {
- il2cpp::os::Event apcDoneEvent;
- Il2CppStackFrameInfo* frame;
- bool hasResult;
- };
- static void STDCALL GetThreadFrameAtCallback(void* context)
- {
- GetThreadFrameAtContext* ctx = static_cast<GetThreadFrameAtContext*>(context);
- ctx->hasResult = StackTrace::GetStackFrameAt(ctx->depth, *ctx->frame);
- ctx->apcDoneEvent.Set();
- }
- bool StackTrace::GetThreadStackFrameAt(Il2CppThread* thread, int32_t depth, Il2CppStackFrameInfo& 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;
- #else
- return false;
- #endif
- }
- static void STDCALL WalkThreadFrameStackCallback(void* context)
- {
- WalkThreadFrameStackContext* ctx = static_cast<WalkThreadFrameStackContext*>(context);
- StackTrace::WalkFrameStack(ctx->callback, ctx->userContext);
- ctx->apcDoneEvent.Set();
- }
- void StackTrace::WalkThreadFrameStack(Il2CppThread* thread, Il2CppFrameWalkFunc callback, void* context)
- {
- #if IL2CPP_ENABLE_STACKTRACES
- WalkThreadFrameStackContext apcContext;
- apcContext.callback = callback;
- apcContext.userContext = context;
- thread->GetInternalThread()->handle->QueueUserAPC(WalkThreadFrameStackCallback, &apcContext);
- apcContext.apcDoneEvent.Wait();
- #endif
- }
- static void STDCALL GetThreadStackDepthCallback(void* context)
- {
- GetThreadStackDepthContext* ctx = static_cast<GetThreadStackDepthContext*>(context);
- ctx->stackDepth = static_cast<int32_t>(StackTrace::GetStackDepth());
- ctx->apcDoneEvent.Set();
- }
- int32_t StackTrace::GetThreadStackDepth(Il2CppThread* thread)
- {
- #if IL2CPP_ENABLE_STACKTRACES
- GetThreadStackDepthContext apcContext;
- thread->GetInternalThread()->handle->QueueUserAPC(GetThreadStackDepthCallback, &apcContext);
- apcContext.apcDoneEvent.Wait();
- return apcContext.stackDepth;
- #else
- return 0;
- #endif
- }
- static void STDCALL GetThreadTopFrameCallback(void* context)
- {
- GetThreadTopFrameContext* ctx = static_cast<GetThreadTopFrameContext*>(context);
- ctx->hasResult = StackTrace::GetTopStackFrame(*ctx->frame);
- ctx->apcDoneEvent.Set();
- }
- bool StackTrace::GetThreadTopStackFrame(Il2CppThread* thread, Il2CppStackFrameInfo& frame)
- {
- #if IL2CPP_ENABLE_STACKTRACES
- GetThreadTopFrameContext apcContext;
- apcContext.frame = &frame;
- thread->GetInternalThread()->handle->QueueUserAPC(GetThreadTopFrameCallback, &apcContext);
- apcContext.apcDoneEvent.Wait();
- return apcContext.hasResult;
- #else
- return false;
- #endif
- }
- }
- }
|