我如何找出可用堆栈空间的大小?

时间:2018-02-23 20:10:01

标签: c++ stack stack-overflow visual-studio-debugging

我们都知道堆栈正在向下增长,所以它真的是一个直截了当的假设,如果我们找到 last 声明变量的地址,我们将得出堆栈中的最小地址,所以我们可以假设这个地址将是我们剩余的可用堆栈。

而且我做到了,我得到了一个非常好的地址{0x000000dc9354f540} = {947364623680}我们知道堆栈向下增长,我们知道我们不能低于0。 所以有点数学:

947364623680 / (1024*1024*1024) = 882.302060425

- >他们暗示我的机器上有882Gb的堆栈吗?!

我测试它,显然在堆栈上分配额外的2mb后得到堆栈溢出异常:

uint8 array[1024*1024*2] = {};

我的问题是WTF就是这个问题,我怎样才能得到实际的堆栈大小?

3 个答案:

答案 0 :(得分:2)

由于你的问题有一个标签“visual-studio-debugging”我假设你使用windows。

首先你应该得到当前的堆栈指针。要么获取本地虚拟变量的地址(就像您现在所做的那样),要么获取原始asm读取esp/rsp,或者获取本地虚拟变量的地址(就像您现在所做的那样),或获取CPU通过Win32 API调用GetThreadContext)注册。

现在,为了找出可用的堆栈大小,您可以使用VirtualQuery来查看此虚拟内存区域的起始地址(也称为分配基址)。基本上减去这些指针会得到剩余的堆栈大小(精度达到当前堆栈帧的大小)。

很久以前我写了一个article about this subject,包括查询当前分配/保留的堆栈大小。如果您愿意,可以在那里找到更多信息:

  

他们是否暗示我的机器上有882Gb的堆栈?!

它与“机器上的堆栈”无关。它是关于虚拟地址空间,与系统中可用的物理存储(RAM +页面文件)无关。

答案 1 :(得分:1)

另一种获取Win32应用程序中任意给定点剩余的堆栈空间的近似值的方法将类似于以下函数。它使用结构化异常处理来捕获堆栈溢出异常。

注意:@valdo的解决方案是正确的解决方案。我发布此答案是因为这是一种有趣的解决方法。这将非常慢,因为它的运行时间是线性的(就堆栈大小而言),而不是@valdo解决方案的恒定运行时间。

static uint64_t GetAvailableStackSpace()
{
    volatile uint8_t var;
    volatile uint8_t* addr = &var;
    volatile uint8_t sink;

    auto filter = [](unsigned int code) -> int
    {
        return (code == EXCEPTION_STACK_OVERFLOW) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
    };

    __try
    {
        while (true)
        {
            addr = addr - 1024;
            sink = *addr;
        }
    }
    __except (filter(GetExceptionCode()))
    {
        return (&var - addr);
    }

    return 0;
}

答案 2 :(得分:0)

这是@valdo提到的VirtualQuery技术的实现。 此函数返回可用堆栈的大约字节数。我在Windows x64上进行了测试。

static uint64_t GetAvailableStackSpace()
{
    volatile uint8_t var;
    MEMORY_BASIC_INFORMATION mbi;

    auto virtualQuerySuccess = VirtualQuery((LPCVOID)&var, &mbi, sizeof(mbi));

    if (!virtualQuerySuccess)
    {
        return 0;
    }

    return &var - mbi.AllocationBase;
}