The MASM Forum Archive 2004 to 2012

General Forums => The Workshop => Topic started by: Eddy on May 04, 2005, 02:10:18 PM

Title: Retrieve available stack space
Post by: Eddy on May 04, 2005, 02:10:18 PM
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
Title: Re: Retrieve available stack space
Post by: roticv on May 04, 2005, 02:11:53 PM
The closest I know of is fs:[4h] and fs:[8h], but I don't think that is what you want.
Title: Re: Retrieve available stack space
Post by: Eddy on May 04, 2005, 02:15:47 PM
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]


Title: Re: Retrieve available stack space
Post by: doomsday on May 04, 2005, 03:19:05 PM
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
Title: Re: Retrieve available stack space
Post by: hutch-- on May 04, 2005, 04:30:31 PM
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.
Title: Re: Retrieve available stack space
Post by: Eddy on May 04, 2005, 09:33:14 PM
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
Title: Re: Retrieve available stack space
Post by: Eddy on May 04, 2005, 09:57:41 PM
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
Title: Re: Retrieve available stack space
Post by: MichaelW on May 04, 2005, 11:47:11 PM
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]
Title: Re: Retrieve available stack space
Post by: ToutEnMasm on May 05, 2005, 06:03:52 AM
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
Title: Re: Retrieve available stack space
Post by: roticv on May 05, 2005, 03:56:08 PM
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
Title: Re: Retrieve available stack space
Post by: pbrennick on May 05, 2005, 04:34:39 PM
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
Title: Re: Retrieve available stack space
Post by: Eddy on May 05, 2005, 09:57:36 PM
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
Title: Re: Retrieve available stack space
Post by: QvasiModo on May 05, 2005, 10:27:21 PM
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:
Title: Re: Retrieve available stack space
Post by: Eddy on May 05, 2005, 10:39:41 PM
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

Title: Re: Retrieve available stack space
Post by: Eddy on May 05, 2005, 10:51:38 PM
small demo prog:

[attachment deleted by admin]
Title: Re: Retrieve available stack space
Post by: pbrennick on May 06, 2005, 12:28:21 AM
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
Title: Re: Retrieve available stack space
Post by: MichaelW on May 06, 2005, 02:14:01 AM
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?



Title: Re: Retrieve available stack space
Post by: Eddy on May 06, 2005, 07:32:57 PM
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


Title: Re: Retrieve available stack space
Post by: QvasiModo on May 06, 2005, 10:27:14 PM
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).
Title: Re: Retrieve available stack space
Post by: MichaelW on May 08, 2005, 07:57:07 AM
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]