Astuces pour le débogage de threads
Cet article fournit des informations utiles pour le débogage de threads, notamment sur la définition des noms de threads pour le code natif et managé.
Conseils C/C++
Voici quelques conseils que vous utiles pour le débogage de threads en code natif :
Vous pouvez consulter le contenu du bloc d’informations du thread en entrant
@TIB
dans la fenêtre Espion ou dans la boîte de dialogue Espion express.Vous pouvez consulter le dernier code d’erreur du thread actuel en entrant
@Err
dans la fenêtre Espion ou dans la boîte de dialogue Espion express.Vous pouvez utiliser les fonctions des bibliothèques Runtime C pour déboguer une application multithread. Pour plus d’informations, consultez _malloc_dbg.
Définir un nom de thread en C/C++
Il est possible d'attribuer des noms aux threads dans toutes les éditions de Visual Studio. L’attribution de noms aux threads est utile pour identifier les threads intéressants dans la fenêtre Threads lors du débogage d’un processus en cours d’exécution. Avoir des threads nommés de façon reconnaissable peut également être utile lors de l’exécution du débogage post-mortem via l’inspection d’image mémoire après incident et lors de l’analyse des captures de performances à l’aide de différents outils.
Comment définir le nom d’un thread
Il existe deux façons de définir un nom de thread. La première est par l’intermédiaire de la fonction SetThreadDescription. La seconde consiste à lever une exception particulière pendant que le débogueur Visual Studio est attaché au processus. Chaque approche présente des avantages et des inconvénients. L’utilisation de SetThreadDescription
est prise en charge à partir de Windows 10, version 1607 ou Windows Server 2016.
Il convient de noter que les deux approches peuvent être utilisées ensemble, si vous le souhaitez, car les mécanismes par lesquels elles fonctionnent sont indépendants l’un de l’autre.
Définir un nom de thread à l’aide de SetThreadDescription
Avantages :
- Les noms de threads sont visibles lors du débogage dans Visual Studio, que le débogueur ait été attaché ou non au processus au moment de l’appel de SetThreadDescription.
- Les noms de threads sont visibles lors de l’exécution d’un débogage post-mortem en chargeant une image mémoire après incident dans Visual Studio.
- Les noms de threads sont également visibles lors de l’utilisation d’autres outils, tels que le débogueur WinDbg et l’analyseur de performances Analyseur de performances Windows.
Avertissements :
- Les noms de threads sont uniquement visibles dans Visual Studio 2017 version 15.6 et versions ultérieures.
- Lors du débogage post-mortem d’un fichier d’image mémoire après incident, les noms de threads ne sont visibles que si l’incident a été créé sur Windows 10 version 1607, Windows Server 2016 ou versions ultérieures de Windows.
Exemple :
#include <windows.h>
#include <processthreadsapi.h>
int main()
{
HRESULT r;
r = SetThreadDescription(
GetCurrentThread(),
L"ThisIsMyThreadName!"
);
return 0;
}
Définir un nom de thread en levant une exception
Une autre façon de définir un nom de thread dans votre programme consiste à communiquer le nom de thread souhaité au débogueur Visual Studio en levant une exception spécialement configurée.
Avantages :
- Fonctionne dans toutes les versions de Visual Studio.
Avertissements :
- Fonctionne uniquement si le débogueur est attaché au moment où la méthode basée sur l’exception est utilisée.
- Les noms de threads définis à l’aide de cette méthode ne seront pas disponibles dans les images mémoires ou les outils d’analyse des performances.
Exemple :
La fonction SetThreadName
illustrée ci-dessous illustre cette approche basée sur les exceptions. Notez que le nom du thread est automatiquement copié dans le thread, afin que la mémoire du paramètre threadName
puisse être libérée une fois l’appel SetThreadName
terminé.
//
// Usage: SetThreadName ((DWORD)-1, "MainThread");
//
#include <windows.h>
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetThreadName(DWORD dwThreadID, const char* threadName) {
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
__try{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER){
}
#pragma warning(pop)
}
Définir un nom de thread dans du code managé
Il est possible d'attribuer des noms aux threads dans toutes les éditions de Visual Studio. Ces noms sont utiles pour effectuer le suivi des threads dans la fenêtre Threads.
Pour définir un nom de thread en code managé, utilisez la propriété Name.
Exemple
public class Needle
{
// This method will be called when the thread is started.
public void Baz()
{
Console.WriteLine("Needle Baz is running on another thread");
}
}
public void Main()
{
Console.WriteLine("Thread Simple Sample");
Needle oNeedle = new Needle();
// Create a Thread object.
System.Threading.Thread oThread = new System.Threading.Thread(oNeedle.Baz);
// Set the Thread name to "MyThread".
oThread.Name = "MyThread";
// Starting the thread invokes the ThreadStart delegate
oThread.Start();
}