Détermination de l’espace de stack avec Visual Studio

Je programme en C dans Visual Studio 2005. J’ai un programme multithread, mais ce n’est pas particulièrement important ici.

Comment puis-je déterminer (approximativement) la quantité d’espace de stack que mes threads utilisent?

La technique que je comptais utiliser consiste à définir la mémoire de la stack sur une valeur prédéterminée, par exemple 0xDEADBEEF, à exécuter le programme pendant longtemps, à suspendre le programme et à parsingr la stack.

Comment lire et écrire la mémoire de stack avec Visual Studio?

EDIT: Voir, par exemple, “Comment déterminer l’utilisation maximale de la stack”. Cette question parle d’un système embarqué, mais j’essaie ici de déterminer la réponse sur un PC ordinaire.

Windows ne valide pas immédiatement la mémoire de stack; au lieu de cela, il lui réserve l’espace d’adressage et le valide page par page lors de son access. Lisez cette page pour plus d’informations.

En conséquence, l’espace d’adressage de la stack se compose de trois régions contiguës:

  • Mémoire réservée mais non validée qui peut être utilisée pour la croissance de la stack (mais n’a jamais encore été utilisée);
  • La page de garde, qui n’a encore jamais été consultée, sert à déclencher la croissance de la stack lorsqu’elle est consultée;
  • Mémoire validée, c’est-à-dire la mémoire de stack à laquelle le thread a jamais accédé.

Cela nous permet de construire une fonction qui obtient la taille de la stack (avec une granularité de la taille de la page):

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 (ie accessed) portion of the stack return mbi.RegionSize; } 

Une chose à considérer: CreateThread permet de spécifier la taille de validation initiale de la stack (via le paramètre dwStackSize , lorsque l’indicateur STACK_SIZE_PARAM_IS_A_RESERVATION n’est pas défini). Si ce paramètre est différent de zéro, notre fonction ne retournera la valeur correcte que lorsque l’utilisation de la stack sera supérieure à la valeur de dwStackSize .

Vous pouvez utiliser les informations du bloc d’informations sur les threads Win32.

Lorsque vous voulez dans un thread savoir combien de stack il utilise, vous pouvez faire quelque chose comme ceci:

 #include  #include  #include  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()); // ... } 

La stack ne fonctionne pas comme prévu. La stack est une séquence linéaire de pages, dont la dernière (en haut) est marquée par un bit de garde de page. Lorsque cette page est touchée, le bit de garde est supprimé et la page peut être utilisée. Pour une croissance ultérieure, une nouvelle page de garde est allouée.

Par conséquent, la réponse que vous voulez est l’endroit où la page gaurd est allouée. Mais la technique que vous proposez toucherait la page en question et, par conséquent, invaliderait la chose même que vous essayez de mesurer.

La méthode non invasive pour déterminer si une page (stack) possède le bit de garde est via VirtualQuery() .

Vous pouvez utiliser la fonction GetThreadContext () pour déterminer le pointeur de stack actuel du thread. Utilisez ensuite VirtualQuery () pour trouver la base de stack pour ce pointeur. La soustraction de ces deux pointeurs vous donnera la taille de stack pour un thread donné.