| CodeGuru Home | VC++ / MFC / C++ | .NET / C# | Visual Basic | Newsletters | VB Forums | Developer.com |
|
|||||||
| Assembly Questions and Answers for Assembly here! |
![]() |
|
|
Thread Tools | Search this Thread | Rate Thread | Display Modes |
|
#1
|
||||
|
||||
|
dumping stack for all threads
Aloha!
I'm in a bit of a jam... any hints will be greatly appreciated. Description: There's this multithreaded process which throws an exception. In the exception handler, am trying to dump the stack for all the threads in the process. For the thread generating the exception is a piece of cake, and I figured will be almost as easy for the other threads: Step 1 - get the context of each thread using GetThreadContext() API Step 2 - get TIB (Thread Information Block) of the thread in question Step 3 - dump from stack's top until stack's base Unexpected result: I believed that when switching the thread's context for yielding the execution to a different thread, SegFs member of the CONTEXT structure must have a different value from one thread to another, in order to have FS:[18h] pointing to a different TIB structure. It seems I was wrong! Questions: - Is the following function retrieving the TIB of the calling thread? Code:
static tagXTIB* GetTIB()
{
tagXTIB* pTib;
__asm
{
MOV EAX , FS:[18h]
MOV pTib , EAX
}
return pTib;
}
Code:
typedef struct tagXTIB
{
PEXCEPTION_REGISTRATION_RECORD pvExcept; //00h Head of exception record list
PVOID pvStackUserTop; //04 Top of user stack
PVOID pvStackUserBase; //08h Base of user stack
union
{
struct //Win95 fields
{
WORD pvTDB; //0Ch
WORD pvThunkSS; //0Eh SS selector used for thunking to 16 bits
DWORD unknown1; //10h
} WIN95;
struct //WinNT fields
{
PVOID SubSystemTib; //0Ch
ULONG FiberData; //10h
} WINNT;
} TIB_UNION1;
PVOID pvArbitrary; //14h Available for application
struct _tib *ptibSelf; //18h Linear address of TIB structure
union
{
struct //Win95 fields
{
WORD TIBFlags; //1Ch
WORD Win16MutexCount; //1Eh
DWORD DebugContext; //20h
DWORD pCurrentPriority; //24h
DWORD pvQueue; //28h Message Queue
} WIN95;
struct //WinNT fields
{
DWORD unknown1; //1Ch
DWORD processID; //20h
DWORD threadID; //24h
DWORD unknown2; //28h
} WINNT;
} TIB_UNION2;
PVOID* pvTLSArray;
union
{
struct //Win95 fields
{
PVOID* pProcess; //30h Pointer to owning process database
} WIN95;
} TIB_UNION3;
} XTIB;
#pragma pack()
- Do you know any other way of getting these values, besides reading them from the TIB? - In order to get the address of the TIB for all the other threads of the process (not the calling thread), is it correct to use the SegFs member of the CONTEXT structure filled by a GetThreadContext() call, instead of FS from GetTIB() function? - Then, why SegFs is equal with FS? Makes no sense to get the TIBs this way, since FS:[18h] will be equal with SegFs:[18h], but TIB should be different for two threads, isn't it? Conclusions: My interest is to dump the stack for each thread belonging to the process. Having the stack's top & base addresses, and also the Esp pointer (which seems to be retrieved correctly by the API), I will be able to list the call stack. All registers are looking as valid information, as returned by GetThreadContext() API. I've checked Eip value, too. So, makes me believe that SegFs is also correct. But then, why has the same value with the calling thread's FS? If so, from where to read the TIB of the other threads? Or, at least, how to get the correct location of their stack (because I know for sure that threads are not sharing the stack)? Many anticipated thanks,
__________________
Bogdan Apostol ESRI Developer Network Compilers demystified - Function pointers in Visual Basic 6.0 Enables the use of function pointers in VB6 and shows how to embed native code in a VB application. Customize your R2H The unofficial board dedicated to ASUS R2H UMPC owners. |
|
#2
|
||||
|
||||
|
Re: dumping stack for all threads
I am not sure if your question is answered already, so excuse me if I am posting in old thread.
![]() Here is a good article from Matt Patriek on TIBs, Very similar to what you are trying to do, MSJ Article on TIBs http://www.microsoft.com/msj/archive/S2CE.aspx He also had the sample code (SHOWTIB), see DisplayTIB function, not present in source code downloads (at least I couldnt find it there) So here is a copy I got from somewhere on web, Code:
tib.h and showtib.cpp
TIB.H
//===========================================================
// File: TIB.H
// Author: Matt Pietrek
// From: Microsoft Systems Journal "Under the Hood", May 1996
//===========================================================
#pragma pack(1)
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD * pNext;
FARPROC pfnHandler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
typedef struct _TIB
{
PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list
PVOID pvStackUserTop; // 04h Top of user stack
PVOID pvStackUserBase; // 08h Base of user stack
union // 0Ch (NT/Win95 differences)
{
struct // Win95 fields
{
WORD pvTDB; // 0Ch TDB
WORD pvThunkSS; // 0Eh SS selector used for thunking to 16 bits
DWORD unknown1; // 10h
} WIN95;
struct // WinNT fields
{
PVOID SubSystemTib; // 0Ch
ULONG FiberData; // 10h
} WINNT;
} TIB_UNION1;
PVOID pvArbitrary; // 14h Available for application use
struct _tib *ptibSelf; // 18h Linear address of TIB structure
union // 1Ch (NT/Win95 differences)
{
struct // Win95 fields
{
WORD TIBFlags; // 1Ch
WORD Win16MutexCount; // 1Eh
DWORD DebugContext; // 20h
DWORD pCurrentPriority; // 24h
DWORD pvQueue; // 28h Message Queue selector
} WIN95;
struct // WinNT fields
{
DWORD unknown1; // 1Ch
DWORD processID; // 20h
DWORD threadID; // 24h
DWORD unknown2; // 28h
} WINNT;
} TIB_UNION2;
PVOID* pvTLSArray; // 2Ch Thread Local Storage array
union // 30h (NT/Win95 differences)
{
struct // Win95 fields
{
PVOID* pProcess; // 30h Pointer to owning process database
} WIN95;
} TIB_UNION3;
} TIB, *PTIB;
#pragma pack()
SHOWTIB.CPP
//==========================================================================
// File: SHOWTIB.CPP
// Author: Matt Pietrek
// To Build:
// CL /MT SHOWTIB.CPP USER32.LIB (Visual C++)
// BCC32 -tWM SHOWTIB.CPP (Borland C++, TASM32 required)
//==========================================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#pragma hdrstop
#include "tib.h"
#define SHOWTIB_MAX_THREADS 64
CRITICAL_SECTION gDisplayTIB_CritSect;
void DisplayTIB( PSTR pszThreadName )
{
PTIB pTIB;
WORD fsSel;
EnterCriticalSection( &gDisplayTIB_CritSect );
__asm
{
mov EAX, FS:[18h]
mov [pTIB], EAX
mov [fsSel], FS
}
printf( "Contents of thread %s\n", pszThreadName );
printf( " TIB %04X (Address: %08X)\n", fsSel, pTIB );
printf( " SEH chain: %08X\n", pTIB->pvExcept );
printf( " Stack top: %08X\n", pTIB->pvStackUserTop );
printf( " Stack base: %08X\n", pTIB->pvStackUserBase );
printf( " pvArbitray: %08X\n", pTIB->pvArbitrary );
printf( " TLS array *: %08X\n", pTIB->pvTLSArray );
printf( " ----OS Specific fields----\n" );
if ( 0xC0000000 == (GetVersion() & 0xC0000000) ) // Is this Win95 ?
{
printf( " TDB: %04X\n", pTIB->TIB_UNION1.WIN95.pvTDB );
printf( " Thunk SS: %04X\n", pTIB->TIB_UNION1.WIN95.pvThunkSS );
printf( " TIB flags: %04X\n", pTIB->TIB_UNION2.WIN95.TIBFlags );
printf( " Win16Mutex count: %04X\n",
pTI->TIB_UNION2.WIN95.Win16MutexCount );
printf( " DebugContext: %08X\n", pTIB->TIB_UNION2.WIN95.DebugContext );
printf( " Current Priority *: %08X (%u)\n",
pTIB->TIB_UNION2.WIN95.pCurrentPriority,
*(PDWORD)(pTIB->TIB_UNION2.WIN95.pCurrentPriority) );
printf( " Queue: %04X\n", pTIB->TIB_UNION2.WIN95.pvQueue );
printf( " Process *: %08X\n", pTIB->TIB_UNION3.WIN95.pProcess );
}
else if ( 0 == (GetVersion() & 0xC0000000) ) // Is this WinNT?
{
printf(" SubSystem TIB: %08X\n", pTIB->TIB_UNION1.WINNT.SubSystemTib);
printf(" FiberData: %08X\n", pTIB->TIB_UNION1.WINNT.FiberData );
printf(" unknown1: %08X\n", pTIB->TIB_UNION2.WINNT.unknown1);
printf(" process ID: %08X\n", pTIB->TIB_UNION2.WINNT.processID);
printf(" thread ID: %08X\n", pTIB->TIB_UNION2.WINNT.threadID);
printf(" unknown2: %08X\n", pTIB->TIB_UNION2.WINNT.unknown2);
}
else
{
printf(" Unsupported Win32 implementation\n" );
}
printf( "\n" );
LeaveCriticalSection( &gDisplayTIB_CritSect );
}
void MyThreadFunction( void * threadParam )
{
char szThreadName[128];
wsprintf( szThreadName, "%u", threadParam ); // Give the thread a name
// If multiple threads are specified, give'em different priorities
if ( (DWORD)threadParam & 1 )
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST );
DisplayTIB( szThreadName ); // Display the thread's TIB
// Let other threads execute while this thread is still alive. The idea
// here is to try and prevent memory region and selector reuse.
Sleep( 1000 );
}
int main( int argc, char *argv[] )
{
if ( argc < 2 )
{
printf( "Syntax: SHOWTIB [# of threads]\n" );
return 1;
}
InitializeCriticalSection( &gDisplayTIB_CritSect );
unsigned cThreads = atoi( argv[1] );
if ( (cThreads < 1) || (cThreads > SHOWTIB_MAX_THREADS) )
{
printf( "thread count must be > 1 and < %u\n", SHOWTIB_MAX_THREADS );
}
else
{
// Allocate an array to hold the thread handles
HANDLE threadHandles[ SHOWTIB_MAX_THREADS ];
// Create the specified number of threads
for ( unsigned i = 0; i < cThreads; i++ )
threadHandles[i] = (HANDLE)
_beginthread(MyThreadFunction,0,(PVOID)i);
// Wait for all the threads to finish before we exit the program
WaitForMultipleObjects( cThreads, threadHandles, TRUE, INFINITE );
// We don't need the thread handles anymore. Close'em!
for ( i = 0; i < cThreads; i++ )
CloseHandle( threadHandles[i] );
}
DeleteCriticalSection( &gDisplayTIB_CritSect );
return 0;
}
|
|
#3
|
||||
|
||||
|
Re: dumping stack for all threads
Thanks Krishnaa for your reply! I appreciate your effort of gathering some information about TIBs, but unfortunatelly does not answer my question. Your sample code displays the TIB of each thread from within the threads themselves. What I am looking for is to read from a thread "A" the TIB of a thread "B". I assume it is possible to obtain the address of the TIB of each thread in the process when an exception occurs because most debuggers can.
This is my purpose: to install a "global" hook for a process which catches all exceptions raised by all its threads, and log some info about the state of all threads (not only the one that raised the exception). I have managed to implement everything else (including exe patching used to debug applications which can't be recompiled) but currently my log contains only info about the thread that crashed (i do log only unhandled exceptions, not handled ones) and sometimes that isn't enough to catch a bug. I have also implemented a function call stack retrieval algorithm that works also in some cases of corrupted stack (a bit better than the one in VS 6.0, and similar to the one in VS 7.1). When exceptions are not handled, my implementation uses some heuristics, trying to skip the bad code and resume execution, which in many cases helps an application to regain control and let the user save data,... etc. Before I've posted here I have read most of the related articles (also from MSJ) I could find and tested many sample source code, but unfortunatelly I couldn't find an answer until today, after more than 2 years since it all started. Regards,
__________________
Bogdan Apostol ESRI Developer Network Compilers demystified - Function pointers in Visual Basic 6.0 Enables the use of function pointers in VB6 and shows how to embed native code in a VB application. Customize your R2H The unofficial board dedicated to ASUS R2H UMPC owners. |
|
#4
|
||||
|
||||
|
Re: dumping stack for all threads
Ohh...
Are those other threads suspended before using GetThreadContext() ? |
|
#5
|
||||
|
||||
|
Re: dumping stack for all threads
Quote:
Code:
__asm
{
mov EAX, FS:[18h]
mov [pTIB], EAX
mov [fsSel], FS
}
__________________
Bogdan Apostol ESRI Developer Network Compilers demystified - Function pointers in Visual Basic 6.0 Enables the use of function pointers in VB6 and shows how to embed native code in a VB application. Customize your R2H The unofficial board dedicated to ASUS R2H UMPC owners. |
|
#6
|
||||
|
||||
|
Re: dumping stack for all threads
I am not sure of TIB information, but here is one StackWalker written by Jochen Kalmbach [MVP VC++], It can dump the stack for any thread, of any process.
http://www.codeproject.com/threads/StackWalker.asp This can at least meet your requirement of dumping the call stack of all threads. |
|
#7
|
||||
|
||||
|
Re: dumping stack for all threads
Quote:
Thanks for all your help,
__________________
Bogdan Apostol ESRI Developer Network Compilers demystified - Function pointers in Visual Basic 6.0 Enables the use of function pointers in VB6 and shows how to embed native code in a VB application. Customize your R2H The unofficial board dedicated to ASUS R2H UMPC owners. |
|
#8
|
||||
|
||||
|
Re: dumping stack for all threads
I have tested it, by uncommenting the TestDifferentThread() call in _tmain() function. It works pretty well.
|
|
#9
|
||||
|
||||
|
Re: dumping stack for all threads
Quote:
__________________
Bogdan Apostol ESRI Developer Network Compilers demystified - Function pointers in Visual Basic 6.0 Enables the use of function pointers in VB6 and shows how to embed native code in a VB application. Customize your R2H The unofficial board dedicated to ASUS R2H UMPC owners. |
![]() |
| Bookmarks |
|
||||||
| Thread Tools | Search this Thread |
| Display Modes | Rate This Thread |
|
|