Hi all,
Is it possible to retrieve the amount of free stack space?
I have a recursive sub and I would like to check how much stack space I got left before my app crashes on me...
I can find the current stack pointer by reading the ESP register, but I can't find a way to find the beginning or end of the stack...
BTW, I have to confess that I normally program in PowerBASIC, with occasionally some inline assembler. So I am certainly not a die-hard assembly programmer..
Hope someone will give me some advice anyway... :wink
Kind regards
Eddy
The closest I know of is fs:[4h] and fs:[8h], but I don't think that is what you want.
I remember seeing that on the internet somewhere. If I only knew what the 'fs' stands for.... ::) (thanks for your patience... )
[added later]
Aha! Segment register..! Got it! :U
[/added later]
Hi Eddy,
Since you set the size of the stack when you build the program, you should be able to calculate how much ESP has moved from where it started, right?
Another way to keep track of a recursive routine is something like:
dwRecursionCount dd 0
foo_recursive_proc:
inc [dwRecursionCount]
;print "recursion count is now "+dwRecursionCount
; insert some code here
dec [dwRecursionCount]
ret
Hope this helps.
regards,
-Brent
Hi Eddy,
Welcome on board. The trick is to know first how much stack space you have and I think Bob has an ajustment with the PB compilers. Once you have this you work out what the stack overhead is for each recursion and you can work out what your limits is easily enough. If you have to run a very deep recursive algo, it may be worth increasing the stack memory. It may be a setting in PB but there are a few tools that will change the stack setting in a PE header as well.
To calcukate yor stack overhead each recursion, ad together the parameter byte count plus any locals you use and you will know what the single instance overhad is. divide your available stack memory by this figure and you will know how deep you can go. If it is possible, I would be inclined to try an iterative procedure as it saves you from recursive stack limits.
Well, a fact is that this function is in a dll, so I do not know in advance how large the stack is. It depends on the calling app. I guess I could check the PE header to find out.
It just amazes me that it seems so difficult to find the stack limits (start and end). I expected it would be something as simple as grabbing the stack pointer from the ESP register, but apparently it is not... :(
I tried grabbing fs:[4h] and fs:[8h], but I am not sure what these values represent. fs:[4h] could be the top of the stack, but the value of fs:[8h] seems too low to be the bottom of the stack...
Thanks all for your replies so far!!
Kind regards
Eddy
Ok, so fs:[4h] and fs:[8h] are the stacks top and base.
I read them, but the difference between the two is only 12288 (bytes??), while I have not changed default stack size, so stack should be 1 MB...
Kind regards
Eddy
This doesn't directly determine the free stack space, but it does determine how much stack "consumption" is too much.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
start_esp dd 0
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Terminated on first recursion
;BITESIZE EQU 2048
; Terminated at ~1MB
;BITESIZE EQU 1024
; Terminated at ~1MB, no change in fs:[8] for ~8 recursions ??
BITESIZE EQU 256
mov start_esp,esp
print chr$("start_esp = ")
print ustr$(start_esp)
print chr$(13,10,13,10)
call eatstack
mov eax,input(13,10,"Press enter to exit...")
exit
eatstack proc
local junk[BITESIZE]:DWORD
assume fs:nothing
mov ebx,start_esp
sub ebx,esp
print chr$("start_esp - esp = ")
print ustr$(ebx)
print chr$(" bytes",13,10)
mov ebx,4
mov ebx,fs:[ebx]
print chr$("fs:[4] = ")
print ustr$(ebx)
print chr$(", fs:[8] = ")
mov esi,8
mov esi,fs:[esi]
print ustr$(esi)
sub ebx,esi
print chr$(", fs:[4] - fs:[8] = ")
print ustr$(ebx)
mov eax,input(" bytes",13,10,"Press enter to continue...",13,10)
call eatstack
eatstack endp
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
[attachment deleted by admin]
Hello,
Use VirtualQuery
invoke VirtualQuery,esp,addr memoirepile,sizeof memoirepile
memoirepile is a MEMORY_BASIC_INFORMATION structure
AllocationBase is the bottom of the region ,esp - AllocationBase = stack space available
ToutEnMasm
Eddy,
fs:[4h] and fs:[8h] are just the limits of the stack - ie if the stack address goes above or below it, there would be a nasty error (or rather autoexitingorprocess). That is why I said that it is not exactly what you are seeking.
http://board.win32asmcommunity.net/index.php?topic=8527.0
Hi Eddy,
Something worth remembering is that fs:[4h] and fs:[8h] is the stack verification cross-check used by APIs. If you move ESP without making mods in those 2 areas the OS will kill your process as soon as it is aware (it essentially sends the equivallent of an int 3 to the process).
Also remember that the OS lives in Ring0 and maintains its own stack space. Your process lives in Ring3 and killing your process will not harm the OS (neither will messing with ESP). That is the 'true beauty' of the modern Windows OS.
hth:
Paul
Guys,
I think I can do something useful with fs:[4h] and fs:[8h].
If I understand correctly, it goes something like this:
fs:[4h] is the top of the stack, which is normally fixed. fs:[8h] is the current bottom of the stack that is currently reserved by the OS. If your exe's stack size is (the default) 1MB, this 1 MB is not reserved immediately from the start of your app. Windows reserves only as much stack space as is required at that time. When your app requires more stack space, Windows reserves more stack space, hereby decreasing fs:[8h].
It is only when the reserved stack space reaches/exceeds 1 MB (or any other stack size you specified at compile time) that your app crashes...
Probably someone else can explain this better or correct me, but if Windows works like this, I can monitor the current stack space by watching fs:[4h] and fs:[8h]. If the difference between these addresses comes close to the apps stack size (1 MB default), I can sound the alarm..
The sample prog posted by MichaelW seems to confirm this...
Kind regards
Eddy
I think this is how it works: the system reserves 1MB, and commits as much as needed. The difference between fs:[8] and fs:[4] seems to be the currently commited stack space. This is not the difference between esp and the top of the stack... remember that virtual memory works on entire pages (4 kilobytes).
I don't know if the system will try to reserve more pages or not, after the 1MB limit is reached. :dazzled:
Yes, that is the correct terminology and I believe that is how it works.
fs:[8] will always be less than ESP (it will be on a 4k boundary).
When the difference between fs:[8] and fs:[4] reaches 1MB (or the stack size you set) the app crashes. I tested this.
So, the actual used stack space should be the difference between fs:[4] and ESP....
I'll test this..
Thanks all for your input so far !! :U
Kind regards
Eddy
small demo prog:
[attachment deleted by admin]
Eddy,
Looks like you got it right, have fun and keep us informed as you are doing some very boundary type stuff and it will be very informative to know what you are learning. :clap:
Paul
Quote from: roticv on May 05, 2005, 03:56:08 PM
fs:[4h] and fs:[8h] are just the limits of the stack - ie if the stack address goes above or below it, there would be a nasty error (or rather autoexitingorprocess).
Would that be the reason why my test ran 8 recursions with an allocation size of 1024 bytes before fs:[8h] changed? Am I correct in assuming that an allocation size of 8192 bytes triggered termination on the first recursion because it caused an access beyond the 'guard' page?
Michael,
What you see when running your program is the following: When your app starts, Windows apparently has committed 2 4kB stack pages for your app, of a maximum of 1 MB.
Since your routine needs about 1kB (1032 bytes) for every recursion, you can do 8 recursions using up the initial stack size of 8kB. After that, Windows has to increase the currently committed stack size and adds another page of 4kB. You can see this because fs:[8h] is decremented by 4kB.
After that, fs:[8h] is decremented every 4 recursions because in 4 recursions you use up the newly added stack page of 4kB.
This goes on and on until the totally committed stack size has reached the (default) limit of 1MB. When this happens and your app still demands more stack space, Windows terminates your app..
You can change the default stack size of 1MB at compile time (or during linking).
One interesting thing: When I run your app on Windows 98, fs:[8h] already decreases after the first 4 recursions! Apparently, Win98 already increases the committed stack space when the last stack page is beginning to being used, while XP first uses up that entire last page.
Kind regards
Eddy
Quote from: Eddy on May 06, 2005, 07:32:57 PM
One interesting thing: When I run your app on Windows 98, fs:[8h] already decreases after the first 4 recursions! Apparently, Win98 already increases the committed stack space when the last stack page is beginning to being used, while XP first uses up that entire last page.
Interesting indeed! :eek I wonder how's that done with guard pages?
Or maybe some API is using more stack on Win98 than on XP, so it "touches" the next page before the app does? (just guessing here).
I corrected some problems in my app and modified it to take a command line argument that specifies the amount of stack space to consume per recursion (in bytes). With no command line it defaults to 1024 bytes per recursion. After an hour of head scratching and experimenting I could not set the stack reserve or commit values using LINK. Regardless of what I tried the reserve and commit values remained at the defaults, and when I tried running the result Windows 2000 would terminate it, after displaying a non-specific error message (I did not bother to determine exactly what the error was). I finally decided to try EDITBIN, and it worked just as described in the documentation.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
start_esp dd 0
bite_size dd 0
argBuffer db 128 dup (0)
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
invoke GetCL,1,ADDR argBuffer
invoke atodw,ADDR argBuffer
; GetCL returns 0 for no command line ??
.IF eax == 0
mov bite_size,1024
.ELSE
mov bite_size,eax
.ENDIF
print chr$("bite_size = ")
print ustr$(bite_size)
print chr$(" bytes",13,10)
mov start_esp,esp
print chr$("start_esp = ")
print ustr$(start_esp)
print chr$(13,10,13,10)
jmp eatstack
mov eax,input(13,10,"Press enter to exit...")
exit
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
eatstack proc
sub esp,bite_size
assume fs:nothing
mov ebx,start_esp
sub ebx,esp
print chr$("start_esp - esp = ")
print ustr$(ebx)
print chr$(" bytes",13,10)
mov ebx,4
mov ebx,fs:[ebx]
print chr$("fs:[4] = ")
print ustr$(ebx)
print chr$(", fs:[8] = ")
mov esi,8
mov esi,fs:[esi]
print ustr$(esi)
sub ebx,esi
print chr$(", fs:[4] - fs:[8] = ")
print ustr$(ebx)
mov eax,input(" bytes",13,10,"Press enter to continue...",13,10)
jmp eatstack
eatstack endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
[attachment deleted by admin]