tmp
This commit is contained in:
parent
a72a3646cd
commit
d5954c4a15
6 changed files with 505 additions and 99 deletions
|
|
@ -1,100 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cassert>
|
#include "Environment.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
#define DEBUG_ASSERT(x) assert(x)
|
|
||||||
#define ASSERT(x) assert(x)
|
|
||||||
|
|
||||||
// #define MEM_STACK_TRACE
|
|
||||||
#define MEM_DEBUG
|
|
||||||
|
|
||||||
namespace tp {
|
|
||||||
using ualni = unsigned long long int;
|
|
||||||
using alni = unsigned long long int;
|
|
||||||
using halnf = float;
|
|
||||||
using uhalni = unsigned long int;
|
|
||||||
using int1 = char;
|
|
||||||
using uint1 = unsigned char;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ENV_ALNI_SIZE_B = sizeof(ualni)
|
|
||||||
};
|
|
||||||
|
|
||||||
inline int1 memCompare(const void* left, const void* right, uhalni len) {
|
|
||||||
if (!len) return 0;
|
|
||||||
|
|
||||||
ualni alignedLength = len / sizeof(alni);
|
|
||||||
for (ualni idx = 0; idx < alignedLength; idx++) {
|
|
||||||
if (((alni*) left)[idx] == ((alni*) right)[idx]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (((alni*) left)[idx] > ((alni*) right)[idx]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ualni unalignedLength = len - (alignedLength * sizeof(alni));
|
|
||||||
for (ualni idx = 0; idx < unalignedLength; idx++) {
|
|
||||||
if (((uint1*) left)[len - idx - 1] == ((uint1*) right)[len - idx - 1]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (((uint1*) left)[len - idx - 1] > ((uint1*) right)[len - idx - 1]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool memEqual(const void* left, const void* right, uhalni len) { return memCompare(left, right, len) == 0; }
|
|
||||||
|
|
||||||
inline int1 memCompareVal(const void* left, uhalni len, uint1 val) {
|
|
||||||
if (!len) return 0;
|
|
||||||
|
|
||||||
alni valAligned = val;
|
|
||||||
valAligned = (valAligned << 8) | valAligned;
|
|
||||||
valAligned = (valAligned << 16) | valAligned;
|
|
||||||
valAligned = (valAligned << 32) | valAligned;
|
|
||||||
|
|
||||||
ualni alignedLength = len / sizeof(alni);
|
|
||||||
for (ualni idx = 0; idx < alignedLength; idx++) {
|
|
||||||
if (((alni*) left)[idx] == valAligned) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (((alni*) left)[idx] > valAligned) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ualni unalignedLength = len - (alignedLength * sizeof(alni));
|
|
||||||
for (ualni idx = 0; idx < unalignedLength; idx++) {
|
|
||||||
if (((uint1*) left)[len - idx - 1] == val) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (((uint1*) left)[len - idx - 1] > val) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void memSetVal(void* p, uhalni byteSize, uint1 val) {
|
|
||||||
alni alignedVal = val;
|
|
||||||
alignedVal = (alignedVal << 8) | alignedVal;
|
|
||||||
alignedVal = (alignedVal << 16) | alignedVal;
|
|
||||||
alignedVal = (alignedVal << 32) | alignedVal;
|
|
||||||
|
|
||||||
ualni alignedLen = byteSize / sizeof(alni);
|
|
||||||
for (ualni idx = 0; idx < alignedLen; idx++) {
|
|
||||||
((alni*) p)[idx] = alignedVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
ualni unalignedLen = byteSize - (alignedLen * sizeof(alni));
|
|
||||||
for (ualni idx = 0; idx < unalignedLen; idx++) {
|
|
||||||
((uint1*) p)[byteSize - idx - 1] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,10 @@ set(WINDOWS_LIBRARIES "../moduleswindowsl" CACHE STRING "Svn repository with win
|
||||||
|
|
||||||
#add_subdirectory(Externals)
|
#add_subdirectory(Externals)
|
||||||
|
|
||||||
|
add_subdirectory(Modules)
|
||||||
|
add_subdirectory(Callstack)
|
||||||
add_subdirectory(Allocators)
|
add_subdirectory(Allocators)
|
||||||
|
|
||||||
#add_subdirectory(Modules)
|
|
||||||
#add_subdirectory(Containers)
|
#add_subdirectory(Containers)
|
||||||
#add_subdirectory(Math)
|
#add_subdirectory(Math)
|
||||||
# add_subdirectory(Language)
|
# add_subdirectory(Language)
|
||||||
|
|
|
||||||
14
Callstack/CMakeLists.txt
Normal file
14
Callstack/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
project(Callstack)
|
||||||
|
|
||||||
|
### ---------------------- Static Library --------------------- ###
|
||||||
|
file(GLOB SOURCES "./private/*.cpp")
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} STATIC ${SOURCES})
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC Modules)
|
||||||
|
|
||||||
|
### -------------------------- Tests -------------------------- ###
|
||||||
|
file(GLOB TEST_SOURCES "./tests/*.cpp")
|
||||||
|
add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
|
||||||
|
target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
|
||||||
|
add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})
|
||||||
338
Callstack/private/Callstack.cpp
Normal file
338
Callstack/private/Callstack.cpp
Normal file
|
|
@ -0,0 +1,338 @@
|
||||||
|
|
||||||
|
#include "Callstack.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace tp;
|
||||||
|
|
||||||
|
// frame to reference on no sapce left
|
||||||
|
static void errorNoSpaceLeft() {
|
||||||
|
// to disable optimization of function
|
||||||
|
static int dummy;
|
||||||
|
dummy = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
ualni CallStackCapture::CallStack::getDepth() const {
|
||||||
|
ualni len = 0;
|
||||||
|
for (long long frame : frames) {
|
||||||
|
if (!frame) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
return len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ualni CallStackCapture::hashCallStack(CallStackKey key) {
|
||||||
|
auto const cs = key.cs;
|
||||||
|
ualni out = 0;
|
||||||
|
for (ualni i = 0; cs->frames[i]; i++) {
|
||||||
|
out += cs->frames[i];
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CallStackCapture::CallStackKey::operator==(const CallStackCapture::CallStackKey& in) const {
|
||||||
|
for (ualni i = 0; i < MAX_CALL_DEPTH_CAPTURE; i++) {
|
||||||
|
if (cs->frames[i] != in.cs->frames[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cs->frames[i] == 0 && in.cs->frames[i] == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_ASSERT(0 && "Must Not Happen")
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallStackCapture::CallStackCapture() {
|
||||||
|
static_assert(MAX_CALL_DEPTH_CAPTURE >= 1);
|
||||||
|
static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB > 0);
|
||||||
|
static_assert(MAX_DEBUG_INFO_LEN > sizeof("unresolved"));
|
||||||
|
static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024 > sizeof(CallStack));
|
||||||
|
|
||||||
|
mBuffLoad = 0;
|
||||||
|
mBuffLen = STACKS_LENGTH;
|
||||||
|
mBuff = (CallStack*) malloc(mBuffLen * sizeof(CallStack));
|
||||||
|
|
||||||
|
mErrorSnapshot.frames[0] = (alni) & errorNoSpaceLeft;
|
||||||
|
|
||||||
|
|
||||||
|
platformInit();
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CallStackCapture::CallStack* CallStackCapture::getSnapshot() {
|
||||||
|
|
||||||
|
if (mBuffLoad > mBuffLen) {
|
||||||
|
return &mErrorSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallStack* cs = &mBuff[mBuffLoad];
|
||||||
|
|
||||||
|
platformWriteStackTrace(cs);
|
||||||
|
|
||||||
|
auto idx = mSnapshots.presents({ cs });
|
||||||
|
if (idx) {
|
||||||
|
return mSnapshots.getSlotVal(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
mSnapshots.put({ cs }, cs);
|
||||||
|
|
||||||
|
mBuffLoad++;
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CallStackCapture::DebugSymbols* CallStackCapture::getSymbols(FramePointer frame) {
|
||||||
|
auto idx = mSymbols.presents(frame);
|
||||||
|
if (idx) {
|
||||||
|
return &mSymbols.getSlotVal(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
mSymbols.put(frame, {});
|
||||||
|
auto symbols = &mSymbols.get(frame);
|
||||||
|
|
||||||
|
platformWriteDebugSymbols(frame, symbols);
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::clear() {
|
||||||
|
mBuffLoad = 0;
|
||||||
|
mSnapshots.removeAll();
|
||||||
|
mSymbols.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
CallStackCapture::~CallStackCapture() {
|
||||||
|
free(mBuff);
|
||||||
|
initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------- Platform Depended ---------------------------------- //
|
||||||
|
|
||||||
|
#if defined(ENV_OS_LINUX)
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
void CallStackCapture::platformInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::platformDeinit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::platformWriteStackTrace(CallStack* stack) {
|
||||||
|
auto depth = backtrace((void**) stack->frames, (int) MAX_CALL_DEPTH_CAPTURE - 1);
|
||||||
|
stack->frames[depth] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void getGetSourceFromBinaryAddress(const char* binary, const char* address, char* file, ualni* line) {
|
||||||
|
static char buff[1024];
|
||||||
|
|
||||||
|
snprintf(buff, sizeof(buff), "addr2line -e %s -a %s", binary, address);
|
||||||
|
FILE* pipe = popen(buff, "r");
|
||||||
|
if (pipe) {
|
||||||
|
fgets(buff, sizeof(buff), pipe);
|
||||||
|
fgets(buff, sizeof(buff), pipe);
|
||||||
|
pclose(pipe);
|
||||||
|
char* linePtr = strchr(buff, ':');
|
||||||
|
|
||||||
|
printf("%s\n", buff);
|
||||||
|
|
||||||
|
if (linePtr != nullptr && buff[0] != '?' && buff[1] != '?' && buff[2] != ':') {
|
||||||
|
*linePtr = '\0';
|
||||||
|
auto sourceLen = std::strlen(buff);
|
||||||
|
std::strcpy(file, buff + ((sourceLen > CallStackCapture::MAX_DEBUG_INFO_LEN) ? (sourceLen - CallStackCapture::MAX_DEBUG_INFO_LEN) : 0));
|
||||||
|
*line = strtoul(linePtr + 1, nullptr, 10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::strcpy(file, "unresolved");
|
||||||
|
*line = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void getDemangledName(const char* func, char* out) {
|
||||||
|
int status;
|
||||||
|
size_t funcDemangledSize = CallStackCapture::MAX_DEBUG_INFO_LEN;
|
||||||
|
char* funcDemangled = (char*) malloc(funcDemangledSize);
|
||||||
|
char* ret = abi::__cxa_demangle(func, funcDemangled, &funcDemangledSize, &status);
|
||||||
|
if (status == 0) {
|
||||||
|
funcDemangled = ret;
|
||||||
|
auto funcLen = std::strlen(funcDemangled);
|
||||||
|
std::strcpy(out, funcDemangled + ((funcLen > CallStackCapture::MAX_DEBUG_INFO_LEN) ? (funcLen - CallStackCapture::MAX_DEBUG_INFO_LEN) : 0));
|
||||||
|
free(ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto funcLen = std::strlen(func);
|
||||||
|
std::strcpy(out, func + ((funcLen > CallStackCapture::MAX_DEBUG_INFO_LEN) ? (funcLen - CallStackCapture::MAX_DEBUG_INFO_LEN) : 0));
|
||||||
|
free(funcDemangled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
|
||||||
|
void* addrList[1] = { (void*) frame };
|
||||||
|
auto symbolsArray = backtrace_symbols(addrList, 1);
|
||||||
|
|
||||||
|
// 'bin(fun+addr)'
|
||||||
|
char* bin = *symbolsArray;
|
||||||
|
char* func = nullptr;
|
||||||
|
char* offset = nullptr;
|
||||||
|
|
||||||
|
// 'bin fun+addr'
|
||||||
|
for (char* p = bin; *p; ++p) {
|
||||||
|
if (*p == '(') {
|
||||||
|
*p = 0;
|
||||||
|
func = p + 1;
|
||||||
|
} else if (*p == '+') {
|
||||||
|
offset = p;
|
||||||
|
} else if (*p == ')' && offset) {
|
||||||
|
*p = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (func && offset) {
|
||||||
|
getGetSourceFromBinaryAddress(bin, func, out->file, &out->line);
|
||||||
|
|
||||||
|
if (offset != func) {
|
||||||
|
*offset = 0;
|
||||||
|
getDemangledName(func, out->function);
|
||||||
|
} else {
|
||||||
|
std::strcpy(out->function, "unresolved");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::strcpy(out->file, "unresolved");
|
||||||
|
std::strcpy(out->function, "unresolved");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(symbolsArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::printSnapshot(const CallStack* snapshot) {
|
||||||
|
printf("CallStack: \n");
|
||||||
|
if (snapshot) {
|
||||||
|
for (auto frame : *snapshot) {
|
||||||
|
auto symbols = getSymbols(frame.getFrame());
|
||||||
|
printf(" %s ----- %s:%llu\n", symbols->getFunc(), symbols->getFile(), symbols->getLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::logAll() {
|
||||||
|
for (auto cs : *this) {
|
||||||
|
printSnapshot(cs.getCallStack());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <windows.h>
|
||||||
|
#include <dbghelp.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "dbghelp.lib")
|
||||||
|
|
||||||
|
void CallStackCapture::platformInit() {
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
SymInitialize(process, NULL, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::platformDeinit() {
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
SymCleanup(process);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::platformWriteStackTrace(CallStack* stack) {
|
||||||
|
DWORD hash;
|
||||||
|
CONTEXT context;
|
||||||
|
memset(&context, 0, sizeof(CONTEXT));
|
||||||
|
context.ContextFlags = CONTEXT_FULL;
|
||||||
|
RtlCaptureContext(&context);
|
||||||
|
|
||||||
|
STACKFRAME64 stackFrame;
|
||||||
|
memset(&stackFrame, 0, sizeof(STACKFRAME64));
|
||||||
|
#ifdef _M_IX86
|
||||||
|
hash = IMAGE_FILE_MACHINE_I386;
|
||||||
|
stackFrame.AddrPC.Offset = context.Eip;
|
||||||
|
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||||
|
stackFrame.AddrFrame.Offset = context.Ebp;
|
||||||
|
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||||
|
stackFrame.AddrStack.Offset = context.Esp;
|
||||||
|
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||||
|
#elif _M_X64
|
||||||
|
hash = IMAGE_FILE_MACHINE_AMD64;
|
||||||
|
stackFrame.AddrPC.Offset = context.Rip;
|
||||||
|
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||||
|
stackFrame.AddrFrame.Offset = context.Rbp;
|
||||||
|
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||||
|
stackFrame.AddrStack.Offset = context.Rsp;
|
||||||
|
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int frameIndex = 0; frameIndex < MAX_CALL_DEPTH_CAPTURE; ++frameIndex) {
|
||||||
|
if (!StackWalk64(
|
||||||
|
hash,
|
||||||
|
GetCurrentProcess(),
|
||||||
|
GetCurrentThread(),
|
||||||
|
&stackFrame,
|
||||||
|
&context,
|
||||||
|
NULL,
|
||||||
|
SymFunctionTableAccess64,
|
||||||
|
SymGetModuleBase64,
|
||||||
|
NULL
|
||||||
|
)) {
|
||||||
|
|
||||||
|
stack->frames[frameIndex] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stack->frames[frameIndex] = (alni) (void*) stackFrame.AddrPC.Offset;
|
||||||
|
}
|
||||||
|
stack->frames[MAX_CALL_DEPTH_CAPTURE - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
|
||||||
|
IMAGEHLP_LINE64 lineInfo;
|
||||||
|
DWORD displacement;
|
||||||
|
lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
|
if (SymGetLineFromAddr64(process, (DWORD64) frame, &displacement, &lineInfo)) {
|
||||||
|
strcpy(out->file, lineInfo.FileName);
|
||||||
|
out->line = lineInfo.LineNumber;
|
||||||
|
} else {
|
||||||
|
strcpy(out->file, "unresolved");
|
||||||
|
out->line = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD64 displacementSym = 0;
|
||||||
|
char symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH] = { 0 };
|
||||||
|
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*) symbolBuffer;
|
||||||
|
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||||
|
symbol->MaxNameLength = MAX_PATH;
|
||||||
|
|
||||||
|
if (SymGetSymFromAddr64(process, (DWORD64) frame, &displacementSym, symbol)) {
|
||||||
|
strcpy(out->function, symbol->Name);
|
||||||
|
} else {
|
||||||
|
strcpy(out->function, "unresolved");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::printSnapshot(const CallStack* snapshot) {
|
||||||
|
printf("CallStack: \n");
|
||||||
|
if (snapshot) {
|
||||||
|
for (int i = 0; i < snapshot->getDepth(); ++i) {
|
||||||
|
DebugSymbols symbols;
|
||||||
|
platformWriteDebugSymbols(snapshot->frames[i], &symbols);
|
||||||
|
printf(" %s ----- %s:%lu\n", symbols.function, symbols.file, symbols.line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::logAll() {
|
||||||
|
for (auto cs : *this) {
|
||||||
|
printSnapshot(cs.getCallStack());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
105
Callstack/public/Callstack.hpp
Normal file
105
Callstack/public/Callstack.hpp
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Environment.hpp"
|
||||||
|
#include "Map.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
|
||||||
|
class CallStackCapture {
|
||||||
|
public:
|
||||||
|
typedef tp::alni FramePointer;
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MAX_CALL_DEPTH_CAPTURE = 16,
|
||||||
|
MAX_CALL_CAPTURES_MEM_SIZE_MB = 32,
|
||||||
|
MAX_DEBUG_INFO_LEN = 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
class CallStack {
|
||||||
|
friend CallStackCapture;
|
||||||
|
FramePointer frames[MAX_CALL_DEPTH_CAPTURE];
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] ualni getDepth() const;
|
||||||
|
|
||||||
|
class Iterator {
|
||||||
|
const FramePointer* mFrame;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Iterator(const FramePointer* frame) : mFrame(frame){};
|
||||||
|
FramePointer getFrame() { return *mFrame; }
|
||||||
|
bool operator==(const Iterator& in) const { return in.mFrame == mFrame; }
|
||||||
|
void operator++() { mFrame++; }
|
||||||
|
const Iterator& operator*() const { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] Iterator begin() const { return Iterator(frames); }
|
||||||
|
[[nodiscard]] Iterator end() const { return Iterator(frames + getDepth()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { STACKS_LENGTH = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack) };
|
||||||
|
|
||||||
|
class DebugSymbols {
|
||||||
|
friend CallStackCapture;
|
||||||
|
char function[MAX_DEBUG_INFO_LEN + 1] = { 0 };
|
||||||
|
char file[MAX_DEBUG_INFO_LEN + 1] = { 0 };
|
||||||
|
ualni line = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] const char* getFunc() const { return function; }
|
||||||
|
[[nodiscard]] const char* getFile() const { return file; }
|
||||||
|
[[nodiscard]] ualni getLine() const { return line; }
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
CallStackCapture();
|
||||||
|
~CallStackCapture();
|
||||||
|
|
||||||
|
[[nodiscard]] const CallStack* getSnapshot();
|
||||||
|
const DebugSymbols* getSymbols(FramePointer fp);
|
||||||
|
|
||||||
|
void printSnapshot(const CallStack* snapshot);
|
||||||
|
void logAll();
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Iterator {
|
||||||
|
const CallStack* mSnapshot;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Iterator(const CallStack* start) : mSnapshot(start) {};
|
||||||
|
const CallStack* getCallStack() { return mSnapshot; }
|
||||||
|
bool operator==(const Iterator& in) const { return in.mSnapshot == mSnapshot; }
|
||||||
|
void operator++() { mSnapshot++; }
|
||||||
|
const Iterator& operator*() const { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] Iterator begin() const { return Iterator(mBuff); }
|
||||||
|
[[nodiscard]] Iterator end() const { return Iterator(mBuff + mBuffLoad); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct CallStackKey {
|
||||||
|
CallStack* cs;
|
||||||
|
bool operator==(const CallStackKey& in) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void platformWriteStackTrace(CallStack* stack);
|
||||||
|
static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out);
|
||||||
|
[[nodiscard]] static ualni hashCallStack(CallStackKey key);
|
||||||
|
void platformInit();
|
||||||
|
void platformDeinit();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallStack mErrorSnapshot;
|
||||||
|
|
||||||
|
ualni mBuffLen;
|
||||||
|
ualni mBuffLoad;
|
||||||
|
CallStack* mBuff;
|
||||||
|
|
||||||
|
Map<CallStackKey, CallStack*, DefaultAllocator, hashCallStack> mSnapshots;
|
||||||
|
Map<FramePointer, DebugSymbols> mSymbols;
|
||||||
|
};
|
||||||
|
}
|
||||||
44
Callstack/tests/Test.cpp
Normal file
44
Callstack/tests/Test.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
#include "UnitTest++/UnitTest++.h"
|
||||||
|
|
||||||
|
#include "Callstack.hpp"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
using namespace tp;
|
||||||
|
|
||||||
|
void common(CallStackCapture& cs) { auto tmp = cs.getSnapshot(); }
|
||||||
|
|
||||||
|
void first(CallStackCapture& cs) {
|
||||||
|
common(cs);
|
||||||
|
common(cs);
|
||||||
|
common(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void second(CallStackCapture& cs) {
|
||||||
|
common(cs);
|
||||||
|
common(cs);
|
||||||
|
common(cs);
|
||||||
|
common(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void third(CallStackCapture& cs) {
|
||||||
|
common(cs);
|
||||||
|
common(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void root(CallStackCapture& cs) {
|
||||||
|
first(cs);
|
||||||
|
second(cs);
|
||||||
|
third(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUITE(Utils) {
|
||||||
|
TEST(CallStackCapture) {
|
||||||
|
CallStackCapture callstack;
|
||||||
|
root(callstack);
|
||||||
|
callstack.logAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() { return UnitTest::RunAllTests(); }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue