Visual leak Detact 如何获取当前程序的堆栈
tls_t* tls = (tls_t*)TlsGetValue(m_tlsIndex);
Win32
方法一:每个线程创建时系统给它分配一个LPVOID指针的数组(叫做TLS数组),这个数组从C编程角度是隐藏着的不能直接访问,需要通过一些C API函数调用访问。首先定义一些DWORD线程全局变量或函数静态变量,准备作为各个线程访问自己的TLS数组的索引变量。一个线程使用TLS时,第一步在线程内调用TlsAlloc()函数,为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量:
global_dwTLSindex=TLSAlloc();
注意,此步之后,当前线程实际上访问的是这个TLS数组索引变量的线程内的拷贝版本。也就说,不同线程虽然看起来用的是同名的TLS数组索引变量,但实际上各个线程得到的可能是不同DWORD值。其意义在于,每个使用TLS的线程获得了一个DWORD类型的线程局部静态变量作为TLS数组的索引变量。C/C++原本没有直接定义线程局部静态变量的机制,所以在如此大费周折。
第二步,为当前线程动态分配一块内存区域(使用LocalAlloc()函数调用),然后把指向这块内存区域的指针放入TLS数组相应的槽中(使用TlsValue()函数调用)。
第三步,在当前线程的任何函数内,都可以通过TLS数组的索引变量,使用TlsGetValue()函数得到上一步的那块内存区域的指针,然后就可以进行内存区域的读写操作了。这就实现了在一个线程内部这个范围处处可访问的变量。
最后,如果不再需要上述线程局部静态变量,要动态释放掉这块内存区域(使用LocalFree()函数),然后从TLS数组中放弃对应的槽(使用TlsFree()函数)。
2、如何获取具体的堆栈指针信息
VOID FastCallStack::getStackTrace (UINT32 maxdepth, const context_t& context)
{
UINT32 count = 0;
UINT_PTR* framePointer = context.fp;
#if defined(_M_IX86)
while (count < maxdepth) {
if (*framePointer < (UINT_PTR)framePointer) {
if (*framePointer == NULL) {
// 到达栈底
break;
}
else {
// Invalid frame pointer. Frame pointer addresses should always
// increase as we move up the stack.
m_status |= CALLSTACK_STATUS_INCOMPLETE;
break;
}
}
if (*framePointer & (sizeof(UINT_PTR*) - 1)) {
// Invalid frame pointer. Frame pointer addresses should always
// be aligned to the size of a pointer. This probably means that
// we've encountered a frame that was created by a module built with
// frame pointer omission (FPO) optimization turned on.
m_status |= CALLSTACK_STATUS_INCOMPLETE;
break;
}
if (IsBadReadPtr((UINT*)*framePointer, sizeof(UINT_PTR*))) {
// Bogus frame pointer. Again, this probably means that we've
// encountered a frame built with FPO optimization.
m_status |= CALLSTACK_STATUS_INCOMPLETE;
break;
}
count++;
push_back(*(framePointer + 1));
framePointer = (UINT_PTR*)*framePointer;
}
}
3、dump具体的堆栈,格式化为我们所能读懂的,包括源文件的行号,函数信息。
// dump - Dumps a nicely formatted rendition of the CallStack, including
// symbolic information (function names and line numbers) if available.
void CallStack::dump(BOOL showInternalFrames, UINT start_frame) const
{
// The stack was dumped already
if (m_resolved)
{
dumpResolved();
return;
}
if (m_status & CALLSTACK_STATUS_INCOMPLETE) {
// This call stack appears to be incomplete. Using StackWalk64 may be
// more reliable.
Report(L" HINT: The following call stack may be incomplete. Setting \"StackWalkMethod\"\n"
L" in the vld.ini file to \"safe\" instead of \"fast\" may result in a more\n"
L" complete stack trace.\n");
}
IMAGEHLP_LINE64 sourceInfo = { 0 };
sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
BYTE symbolBuffer [sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_SIZE] = { 0 };
WCHAR lowerCaseName [MAX_PATH];
WCHAR callingModuleName [MAX_PATH];
const size_t max_size = MAXREPORTLENGTH + 1;
// Iterate through each frame in the call stack.
for (UINT32 frame = start_frame; frame < m_size; frame++)
{
// Try to get the source file and line number associated with
// this program counter address.
SIZE_T programCounter = (*this)[frame];
g_symbolLock.Enter();
BOOL foundline = FALSE;
DWORD displacement = 0;
DbgTrace(L"dbghelp32.dll %i: SymGetLineFromAddrW64\n", GetCurrentThreadId());
foundline = SymGetLineFromAddrW64(g_currentProcess, programCounter, &displacement, &sourceInfo);
if (foundline && !showInternalFrames) {
wcscpy_s(lowerCaseName, sourceInfo.FileName);
_wcslwr_s(lowerCaseName, wcslen(lowerCaseName) + 1);
if (isInternalModule(lowerCaseName)) {
// Don't show frames in files internal to the heap.
g_symbolLock.Leave();
continue;
}
}
// Initialize structures passed to the symbol handler.
SYMBOL_INFO* functionInfo = (SYMBOL_INFO*)&symbolBuffer;
functionInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
functionInfo->MaxNameLen = MAX_SYMBOL_NAME_LENGTH;
// Try to get the name of the function containing this program
// counter address.
DWORD64 displacement64 = 0;
LPWSTR functionName;
DbgTrace(L"dbghelp32.dll %i: SymFromAddrW\n", GetCurrentThreadId());
if (SymFromAddrW(g_currentProcess, programCounter, &displacement64, functionInfo)) {
functionName = functionInfo->Name;
}
else {
// GetFormattedMessage( GetLastError() );
functionName = L"(Function name unavailable)";
displacement64 = 0;
}
g_symbolLock.Leave();
HMODULE hCallingModule = GetCallingModule(programCounter);
LPWSTR moduleName = L"(Module name unavailable)";
if (hCallingModule &&
GetModuleFileName(hCallingModule, callingModuleName, _countof(callingModuleName)) > 0)
{
moduleName = wcsrchr(callingModuleName, L'\\');
if (moduleName == NULL)
moduleName = wcsrchr(callingModuleName, L'/');
if (moduleName != NULL)
moduleName++;
else
moduleName = callingModuleName;
}
// Use static here to increase performance, and avoid heap allocs. Hopefully this won't
// prove to be an issue in thread safety. If it does, it will have to be simply non-static.
static WCHAR stack_line[MAXREPORTLENGTH + 1] = L"";
int NumChars = -1;
// Display the current stack frame's information.
if (foundline) {
if (displacement == 0)
NumChars = _snwprintf_s(stack_line, max_size, _TRUNCATE, L" %s (%d): %s!%s\n",
sourceInfo.FileName, sourceInfo.LineNumber, moduleName, functionName);
else
NumChars = _snwprintf_s(stack_line, max_size, _TRUNCATE, L" %s (%d): %s!%s + 0x%X bytes\n",
sourceInfo.FileName, sourceInfo.LineNumber, moduleName, functionName, displacement);
}
else {
if (displacement64 == 0)
NumChars = _snwprintf_s(stack_line, max_size, _TRUNCATE, L" " ADDRESSFORMAT L" (File and line number not available): %s!%s\n",
programCounter, moduleName, functionName);
else
NumChars = _snwprintf_s(stack_line, max_size, _TRUNCATE, L" " ADDRESSFORMAT L" (File and line number not available): %s!%s + 0x%X bytes\n",
programCounter, moduleName, functionName, (DWORD)displacement64);
}
Print(stack_line);
}
}