Windows 7 - query remaining Stack Size

Asked By Mario Semo on 09-May-07 09:29 AM
Hello,

is there any way to query the remaining stack size of a thread?

i looked at the registers in the debugger. nothing which helps me to get the
border.

i tried:

unsigned long remainingStack()
{
CONTEXT context;
context.ContextFlags = CONTEXT_CONTROL;
::GetThreadContext(::GetCurrentThread(), &context);
void
*base = (void *)context.Esp,
*cur  = &context;
return (char*)cur - (char*)base;
}

but this returns a constant since context.Esp changes from call to call.

any ideas?


--
mit freundlichen Grüßen/best regards
mario semo




Skywing [MVP] replied on 09-May-07 10:25 AM
Look at the difference between the current stack pointer and
(PNT_TIB)NtCurrentTeb()->StackLimit.

--
Ken Johnson (Skywing)
Windows SDK MVP
http://www.nynaeve.net
Mario Semo replied on 09-May-07 11:17 AM
Hello,

That sounds exactly for what i was looking for.


except that i didnt manage it to compile:


int main(int   argc,char *argv[])
{
PNT_TIB lTeb = (PNT_TIB)NtCurrentTeb();
return 0;
}


cl -W4 -nologo -D__WINDOWS__ teb.c user32.lib gdi32.lib
teb.c
teb.c(4) : warning C4100: 'argv' : unreferenced formal parameter
teb.c(4) : warning C4100: 'argc' : unreferenced formal parameter
teb.c(6) : warning C4189: 'lTeb' : local variable is initialized but not
referenced
teb.obj : error LNK2001: unresolved external symbol _NtCurrentTeb
teb.exe : fatal error LNK1120: 1 unresolved externals

is there anywhere a sample on how to use it?

i found
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/NtCurrentTeb.html

that the IF in winnt.h is incorrect.
Now i changed the decl in winnt.h from/to:

/*struct _TEB * NtCurrentTeb(void);*/
NTSYSAPI struct _TEB * NTAPI NtCurrentTeb();

(and i verified that i changed it at the correct location by adding @@@ to
the line to get a compile error).

ok, now i get:

cl -W4 -nologo -D__WINDOWS__ teb.c user32.lib gdi32.lib
teb.c
teb.c(4) : warning C4100: 'argv' : unreferenced formal parameter
teb.c(4) : warning C4100: 'argc' : unreferenced formal parameter
teb.c(6) : warning C4189: 'lTeb' : local variable is initialized but not
referenced
teb.obj : error LNK2001: unresolved external symbol __imp__NtCurrentTeb@0
teb.exe : fatal error LNK1120: 1 unresolved externals


mario.
Skywing [MVP] replied on 09-May-07 11:42 AM
It should expand to either a macro or an inline function depending on which
compiler and SDK versions you are using, and which processor platform you're
targetting (see winnt.h).  Are you using an old SDK perhaps?

For x86, it ought to expand to one of these:


// (recent compiler version in use)

__inline struct _TEB * NtCurrentTeb( void ) { return (struct _TEB *)
(ULONG_PTR) __readfsdword (PcTeb); }

// -or-

// (old crusty compiler version in use)

__inline struct _TEB * NtCurrentTeb( void ) { __asm mov eax, fs:[PcTeb] }

..that is based on the Vista SDK headers (again, winnt.h being the relevant
one).

--
Ken Johnson (Skywing)
Windows SDK MVP
http://www.nynaeve.net
Mario Semo replied on 09-May-07 12:29 PM
Ken,

i am using XP-SDK
Winnt.h says:

/*++ BUILD Version: 0082     Increment this if a change has global effects

the original decl comes from:

struct _TEB * NtCurrentTeb(void);

here i tried first :

NTSYSAPI struct _TEB * NTAPI NtCurrentTeb();

and now

__inline struct _TEB * NtCurrentTeb( void ) { __asm mov eax,fs:[PcTeb] }

Is there any chance that this just checks (via StackLimit) the current
comitted stack size?

PNT_TIB lTeb = (PNT_TIB)NtCurrentTeb();
printf("%p - %p =
%u\n",(char*)lTeb->StackLimit,&lTeb,(char*)&lTeb-(char*)lTeb->StackLimit);



cl -W4 -nologo -D__WINDOWS__ -Ge -Od -Zi teb.c  user32.lib gdi32.lib
/link"/stack:0x100000,0x20000"
S:\TOOLS\SAMPLES\C>teb
00110000 - 0012FF7C = 130940


S:\TOOLS\SAMPLES\C>del teb.exe

cl -W4 -nologo -D__WINDOWS__ -Ge -Od -Zi teb.c  user32.lib gdi32.lib
/link"/stack:0x10000,0x2000"
0006E000 - 0006FF7C = 8060

which means (at the beginn of "main")

stacksize=1MB, commit 132K   -> free is 130K
stacksize=64K, commit 8K -> free 8060 bytes

one more test: stack=65K, commit 64K --> free 64K.
Pavel Lebedinsky [MSFT] replied on 10-May-07 03:17 AM
StackBase is the last committed page on the stack (the page below
it should have PAGE_GUARD attribute). Most stacks have a bunch
of reserved pages below the guard page so more pages can be
commited as necessary. Stack overflow will only be raised when
a new guard page cannot be committed.

In debugger you can find the actual base of the stack (the bottommost
MEM_RESERVED page) by looking at @$teb->DeallocationStack.
The only supported way to get it programmatically is by doing
VirtualQuery on a stack address and walking downwards until you
find the first MEM_FREE page.

Note that the system will actually raise a stack overflow several
pages before you get to the bottom, to make sure any possible
exception handlers have enough stack space to run.. The following
page says that the stack can grow down to DeallocationBase
minus two pages, but I think the difference is larger on recent
OSes (plus it can be additionally increased with SetStackSizeGuarantee):

http://msdn2.microsoft.com/en-us/library/ms686774.aspx

In short, I don't think there's an easy way to get the actual available
stack space, but using VirtualQuery you should be able to get an
answer that is pretty close (to within a few pages).

--
This posting is provided "AS IS" with no warranties, and confers no
rights.