0

I use Windows 10, Visual Studio 2019 The program generates threads. I need to add functionality pointing me what is available stack size in any execution time .

#include <iostream>
#include <thread>

void thread_function()
{
    //AvailableStackSize()?  
    //Code1 //varibales on stack allocation + function call
    //AvailableStackSize()? it should decrease
    //Code2 //varibales on stack allocation + function call
    //AvailableStackSize()? it should decrease
    //Code3 //varibales on stack allocation + function call
    //AvailableStackSize()? it should decrease
}

int main()
{
    std::thread t(&thread;_function);
    std::cout << "main thread\n";
    std::thread t2 = t;

    t2.join();

    return 0;
}

I try to use . But I am not sure how can I proceed. I just can query what is a total size of stack. Not an available one.

bool AvailableStackSize()
{
    // Get the stack pointer
    PBYTE pEsp;
    _asm {
        mov pEsp, esp
    };

    // Query the accessible stack region
    MEMORY_BASIC_INFORMATION mbi;
    VERIFY(VirtualQuery(pEsp, &mbi, sizeof(mbi)));

    return mbi.RegionSize;//This is a total size! what is an available? Where guard is located?

}

Probably I can Also check if some address in inside a stack

PVOID add;
(add>= mbi.BaseAddress) && (add < PBYTE(mbi.BaseAddress) + mbi.RegionSize);

I saw several similar question, but no one answers the question 100%. What is correct approach to get available stack size?

dewaffled
  • 2,850
  • 2
  • 17
  • 30
YAKOVM
  • 9,805
  • 31
  • 116
  • 217
  • 2
    It's a linker options that defaults to 1M per thread see: https://learn.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019 Also see __Determining approximately how much stack space is available__ https://devblogs.microsoft.com/oldnewthing/20200609-00/?p=103847 – Richard Critten Jun 26 '20 at 14:16
  • 1
    Note that your comments "it should decrease" are based on a fundamental misunderstanding of how the stack works. All locals for `thread_function` will have space reserved in the prologue. All called functions will have the stack restored at function exit as part of the calling convention. The only time the end of the stack will move between statements in the same function would be if you were using `_alloca()` in *that* function. Even *_alloca()` in called functions will not affect the stack available when control returns to the function doing the checking. – Ben Voigt Jun 26 '20 at 15:14
  • 2
    https://stackoverflow.com/a/28708409/6401656 – RbMm Jun 26 '20 at 15:26

1 Answers1

1

Since the stack grows downward, it's the difference between the potential bottom of the stack and the end of what is currently used. BTW you don't need any inline assembly, because you know that locals are placed on the stack.

__declspec(noinline) size_t AvailableStackSize()
{
    // Query the accessible stack region
    MEMORY_BASIC_INFORMATION mbi;
    VERIFY(VirtualQuery(&mbi, &mbi, sizeof(mbi)));

    return uintptr_t(&mbi) - uintptr_t(mbi.AllocationBase);
}

This will be slightly different from what's actually available in the caller, because the function call used some (to store the return address and any preserved registers). And you'd need to investigate whether the final guard page appears in the same VirtualQuery result or a neighboring one.

But this is the general approach.

dewaffled
  • 2,850
  • 2
  • 17
  • 30
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    @RbMm: even better, if you don't need to support older Windows versions – Ben Voigt Jun 26 '20 at 15:27
  • must be `mbi.AllocationBase` instead `mbi.BaseAddress` – RbMm Jun 26 '20 at 15:47
  • @dewaffled need also take in account that your test function compiler (with optimization) can replace recursion to loop. for prevent this, need some more complex.. say `void foo(int x) { size_t s = AvailableStackSize(); DbgPrint("%p\n", s); if (x) foo(x - 1); if (!s) __debugbreak(); }` – RbMm Jun 26 '20 at 15:58