Определение стекового пространства с помощью Visual Studio - PullRequest
7 голосов
/ 16 ноября 2009

Я программирую на C в Visual Studio 2005. У меня многопоточная программа, но здесь это не особенно важно.

Как определить (приблизительно), сколько стекового пространства используют мои потоки?

Техника, которую я планировал использовать, - установить для стековой памяти какое-то заранее определенное значение, например 0xDEADBEEF, запустить программу в течение длительного времени, приостановить ее и исследовать стек.

Как читать и записывать стековую память в Visual Studio?

РЕДАКТИРОВАТЬ: см., Например, «Как определить максимальное использование стека». Этот вопрос говорит о встроенной системе, но здесь я пытаюсь определить ответ на обычном ПК.

Ответы [ 4 ]

15 голосов
/ 17 ноября 2009

Windows не фиксирует стековую память немедленно; вместо этого он резервирует адресное пространство для него и фиксирует его постранично при обращении к нему. Прочитайте эту страницу для получения дополнительной информации.

В результате адресное пространство стека состоит из трех смежных областей:

  • Зарезервированная, но незафиксированная память, которую можно использовать для наращивания стека (но к ней еще не обращались);
  • Защитная страница, к которой еще никогда не обращались, и служит для запуска роста стека при обращении;
  • Выделенная память, то есть стековая память, к которой когда-либо обращался поток.

Это позволяет нам построить функцию, которая получает размер стека (с гранулярностью размера страницы):

static size_t GetStackUsage()
{
    MEMORY_BASIC_INFORMATION mbi;
    VirtualQuery(&mbi, &mbi, sizeof(mbi));
    // now mbi.AllocationBase = reserved stack memory base address

    VirtualQuery(mbi.AllocationBase, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe reserved (uncommitted) portion of the stack
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the guard page
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the committed (i.e. accessed) portion of the stack

    return mbi.RegionSize;
}

Следует учитывать одну вещь: CreateThread позволяет указать начальный размер фиксации стека (с помощью параметра dwStackSize, когда флаг STACK_SIZE_PARAM_IS_A_RESERVATION не установлен). Если этот параметр не равен нулю, наша функция вернет правильное значение только тогда, когда использование стека станет больше, чем dwStackSize значение.

7 голосов
/ 17 ноября 2009

Вы можете использовать информацию в Информационном блоке потока Win32

Если вы хотите, чтобы в потоке выяснилось, сколько стекового пространства он использует, вы можете сделать что-то вроде этого:

#include <windows.h>
#include <winnt.h>
#include <intrin.h>

inline NT_TIB* getTib()
{
    return (NT_TIB*)__readfsdword( 0x18 );
}
inline size_t get_allocated_stack_size()
{
    return (size_t)getTib()->StackBase - (size_t)getTib()->StackLimit;
}

void somewhere_in_your_thread()
{
    // ...
    size_t sp_value = 0;
    _asm { mov [sp_value], esp }
    size_t used_stack_size = (size_t)getTib()->StackBase - sp_value;

    printf("Number of bytes on stack used by this thread: %u\n", 
           used_stack_size);
    printf("Number of allocated bytes on stack for this thread : %u\n",
           get_allocated_stack_size());    
    // ...
}
1 голос
/ 16 ноября 2009

Стек работает не так, как вы ожидаете. Стек представляет собой линейную последовательность страниц, последняя (верхняя) из которых отмечена битом защиты страницы. При касании этой страницы защитный бит удаляется, и страницу можно использовать. Для дальнейшего роста выделена новая страница защиты.

Следовательно, вы хотите получить ответ, где находится страница gaurd. Но предложенная вами техника коснется рассматриваемой страницы, и в результате она лишит законной силы то, что вы пытаетесь измерить.

Неинвазивный способ определить, имеет ли (стек) страница защитный бит, через VirtualQuery().

0 голосов
/ 17 ноября 2009

Вы можете использовать функцию GetThreadContext (), чтобы определить текущий указатель стека потока. Затем используйте VirtualQuery (), чтобы найти базу стека для этого указателя. Вычитание этих двух указателей даст вам размер стека для данного потока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...