You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
8.5 KiB
300 lines
8.5 KiB
/****************************************************************************
|
|
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "CallStack.h"
|
|
#if USE_MEMORY_LEAK_DETECTOR
|
|
|
|
#if CC_PLATFORM == CC_PLATFORM_ANDROID
|
|
#define __GNU_SOURCE
|
|
#include <cxxabi.h>
|
|
#include <dlfcn.h>
|
|
#include <pthread.h>
|
|
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
|
|
#include <execinfo.h>
|
|
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
|
|
#include <DbgHelp.h>
|
|
#include <Windows.h>
|
|
|
|
#pragma comment(lib, "dbghelp.lib")
|
|
#endif
|
|
|
|
#include <sstream>
|
|
|
|
namespace cc {
|
|
|
|
ccstd::string StackFrame::toString() {
|
|
static ccstd::string unknown("unknown");
|
|
#if CC_PLATFORM == CC_PLATFORM_ANDROID
|
|
std::stringstream stream;
|
|
stream << "\tmodule: " << (module.empty() ? unknown : module)
|
|
<< "\tfunction: " << (function.empty() ? unknown : function);
|
|
|
|
return stream.str();
|
|
|
|
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
|
|
std::stringstream stream;
|
|
stream << "\tfile: " << (file.empty() ? unknown : file);
|
|
|
|
return stream.str();
|
|
|
|
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
|
|
std::stringstream stream;
|
|
stream << "\tmodule: " << (module.empty() ? unknown : module)
|
|
<< "\tfile: " << (file.empty() ? unknown : file)
|
|
<< "\tfunction: " << (function.empty() ? unknown : function)
|
|
<< "\tline: " << line;
|
|
|
|
return stream.str();
|
|
|
|
#else
|
|
return unknown;
|
|
#endif
|
|
}
|
|
|
|
#if CC_PLATFORM == CC_PLATFORM_ANDROID
|
|
|
|
struct ThreadStack {
|
|
void *stack[MAX_STACK_FRAMES];
|
|
int current;
|
|
int overflow;
|
|
};
|
|
|
|
extern "C" {
|
|
|
|
static pthread_once_t s_once = PTHREAD_ONCE_INIT;
|
|
static pthread_key_t s_threadStackKey = 0;
|
|
|
|
static void __attribute__((no_instrument_function))
|
|
init_once(void) {
|
|
pthread_key_create(&s_threadStackKey, NULL);
|
|
}
|
|
|
|
static ThreadStack *__attribute__((no_instrument_function))
|
|
getThreadStack() {
|
|
ThreadStack *ptr = (ThreadStack *)pthread_getspecific(s_threadStackKey);
|
|
if (ptr) {
|
|
return ptr;
|
|
}
|
|
|
|
ptr = (ThreadStack *)calloc(1, sizeof(ThreadStack));
|
|
ptr->current = 0;
|
|
ptr->overflow = 0;
|
|
pthread_setspecific(s_threadStackKey, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
void __attribute__((no_instrument_function))
|
|
__cyg_profile_func_enter(void *this_fn, void *call_site) {
|
|
pthread_once(&s_once, init_once);
|
|
ThreadStack *ptr = getThreadStack();
|
|
if (ptr->current < MAX_STACK_FRAMES) {
|
|
ptr->stack[ptr->current++] = this_fn;
|
|
ptr->overflow = 0;
|
|
} else {
|
|
ptr->overflow++;
|
|
}
|
|
}
|
|
|
|
void __attribute__((no_instrument_function))
|
|
__cyg_profile_func_exit(void *this_fn, void *call_site) {
|
|
pthread_once(&s_once, init_once);
|
|
ThreadStack *ptr = getThreadStack();
|
|
|
|
if (ptr->overflow == 0 && ptr->current > 0) {
|
|
ptr->current--;
|
|
}
|
|
|
|
if (ptr->overflow > 0) {
|
|
ptr->overflow--;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
ccstd::string CallStack::basename(const ccstd::string &path) {
|
|
size_t found = path.find_last_of("/\\");
|
|
|
|
if (ccstd::string::npos != found) {
|
|
return path.substr(found + 1);
|
|
} else {
|
|
return path;
|
|
}
|
|
}
|
|
|
|
ccstd::vector<void *> CallStack::backtrace() {
|
|
#if CC_PLATFORM == CC_PLATFORM_ANDROID
|
|
ccstd::vector<void *> callstack;
|
|
callstack.reserve(MAX_STACK_FRAMES);
|
|
|
|
pthread_once(&s_once, init_once);
|
|
ThreadStack *ptr = getThreadStack();
|
|
for (int i = ptr->current - 1; i >= 0; i--) {
|
|
callstack.push_back(ptr->stack[i]);
|
|
}
|
|
|
|
return callstack;
|
|
|
|
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
|
|
ccstd::vector<void *> callstack;
|
|
callstack.reserve(MAX_STACK_FRAMES);
|
|
|
|
void *array[MAX_STACK_FRAMES];
|
|
int count = ::backtrace(array, MAX_STACK_FRAMES);
|
|
for (auto i = 0; i < count; i++) {
|
|
callstack.push_back(array[i]);
|
|
}
|
|
return callstack;
|
|
|
|
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
|
|
ccstd::vector<void *> callstack;
|
|
callstack.reserve(MAX_STACK_FRAMES);
|
|
|
|
void *array[MAX_STACK_FRAMES];
|
|
int count = CaptureStackBackTrace(0, MAX_STACK_FRAMES, array, NULL);
|
|
for (auto i = 0; i < count; i++) {
|
|
callstack.push_back(array[i]);
|
|
}
|
|
return callstack;
|
|
|
|
#else
|
|
return {};
|
|
#endif
|
|
}
|
|
|
|
ccstd::vector<StackFrame> CallStack::backtraceSymbols(const ccstd::vector<void *> &callstack) {
|
|
#if CC_PLATFORM == CC_PLATFORM_ANDROID
|
|
ccstd::vector<StackFrame> frames;
|
|
size_t size = callstack.size();
|
|
for (size_t i = 0; i < size; i++) {
|
|
Dl_info info;
|
|
StackFrame frame;
|
|
if (dladdr(callstack[i], &info)) {
|
|
if (info.dli_fname && strlen(info.dli_fname) > 0) {
|
|
frame.module = basename(info.dli_fname);
|
|
}
|
|
|
|
if (info.dli_sname && strlen(info.dli_sname) > 0) {
|
|
char *real_name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr);
|
|
if (real_name) {
|
|
frame.function = real_name;
|
|
free(real_name);
|
|
} else {
|
|
frame.function = info.dli_sname;
|
|
}
|
|
}
|
|
}
|
|
|
|
frames.push_back(std::move(frame));
|
|
}
|
|
return frames;
|
|
|
|
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
|
|
size_t size = callstack.size();
|
|
if (size == 0) {
|
|
return {};
|
|
}
|
|
|
|
ccstd::vector<StackFrame> frames;
|
|
char **strs = ::backtrace_symbols(&callstack[0], size);
|
|
for (size_t i = 0; i < size; i++) {
|
|
StackFrame frame;
|
|
frame.file = strs[i];
|
|
frames.push_back(std::move(frame));
|
|
}
|
|
|
|
return frames;
|
|
|
|
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
|
|
ccstd::vector<StackFrame> frames;
|
|
|
|
#if _WIN64
|
|
using PTR_DWORD = DWORD64;
|
|
#else
|
|
using PTR_DWORD = DWORD;
|
|
#endif
|
|
|
|
size_t size = callstack.size();
|
|
for (size_t i = 0; i < size; i++) {
|
|
StackFrame frame;
|
|
PTR_DWORD address = reinterpret_cast<PTR_DWORD>(callstack[i]);
|
|
|
|
char moduelName[MAX_PATH];
|
|
#if _WIN64
|
|
PTR_DWORD moduleBase = SymGetModuleBase64(_process, address);
|
|
#else
|
|
PTR_DWORD moduleBase = SymGetModuleBase(_process, address);
|
|
#endif
|
|
if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduelName, MAX_PATH)) {
|
|
frame.module = basename(moduelName);
|
|
}
|
|
|
|
DWORD64 offset = 0;
|
|
char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_LENGTH] = {0};
|
|
PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbolBuffer;
|
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
symbol->MaxNameLen = MAX_SYMBOL_LENGTH - 1;
|
|
|
|
if (SymFromAddr(_process, address, &offset, symbol)) {
|
|
frame.function = symbol->Name;
|
|
}
|
|
|
|
IMAGEHLP_LINE line;
|
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
|
|
DWORD offset_ln = 0;
|
|
|
|
if (SymGetLineFromAddr(_process, address, &offset_ln, &line)) {
|
|
frame.file = line.FileName;
|
|
frame.line = line.LineNumber;
|
|
}
|
|
|
|
frames.push_back(std::move(frame));
|
|
}
|
|
|
|
return frames;
|
|
|
|
#else
|
|
return {};
|
|
#endif
|
|
}
|
|
|
|
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
|
|
void CallStack::initSym() {
|
|
_process = GetCurrentProcess();
|
|
if (SymInitialize(_process, nullptr, true) == false) {
|
|
CC_ABORT();
|
|
}
|
|
SymSetOptions(SYMOPT_LOAD_LINES);
|
|
}
|
|
|
|
void CallStack::cleanupSym() {
|
|
SymCleanup(_process);
|
|
}
|
|
|
|
HANDLE CallStack::_process = 0;
|
|
#endif
|
|
|
|
} // namespace cc
|
|
|
|
#endif
|
|
|