Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Utilisez DTrace pour Windows pour traiter les événements ETW existants et ajouter de nouveaux événements ETW.
Le suivi d’événements pour Windows (ETW) est une fonctionnalité de suivi au niveau du noyau qui vous permet de journaliser des événements définis par le noyau ou l’application dans un fichier journal. Vous pouvez consommer les événements en temps réel ou à partir d’un fichier journal et les utiliser pour déboguer une application ou déterminer où se produisent les problèmes de performances dans l’application. Pour obtenir des informations générales sur ETW, consultez À propos du suivi d’événements.
Remarque
DTrace est pris en charge dans les builds Insider de Windows après la version 18980 et Windows Server Build 18975.
Pour obtenir des informations générales sur l’utilisation de DTrace sur Windows, consultez DTrace.
Fournisseur ETW Windows DTrace
Vous pouvez utiliser DTrace pour capturer et signaler les événements ETW enregistrés par trace et basés sur des manifestes. Pour sonder des mots clés/niveaux/eventID spécifiques, les sondes ETW fonctionnent beaucoup plus de manière fiable si vous n’utilisez pas de caractères génériques. Au lieu de cela, spécifiez entièrement votre sonde en fonction de ces règles :
Probename = etw
Modname = Guid du fournisseur dans la forme xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, en utilisant tous les caractères minuscules.
Funcname = Level_Keyword du formulaire 0x00_0x0000000000000000. Pour correspondre à tout, il faut le définir sur 0xff_0xffffffffffffffff.
Probename = Integer Event ID ou « generic_event » pour correspondre à tous les ID d’événement.
Le filtrage basé sur probename fonctionne uniquement pour les événements manifestes. Utilisez un caractère générique (*) pour les événements des journaux de trace.
La charge utile ETW est accessible via arg0. Il s'agit de nt`_EVENT_HEADER suivi par une date spécifique à l'événement.
Détermination des fournisseurs ETW disponibles
Utilisez la commande logman pour afficher les fournisseurs ETW actifs et leurs GUID de fournisseur.
C:\>logman query providers
...
Microsoft-Windows-Kernel-Memory {D1D93EF7-E1F2-4F45-9943-03D245FE6C00}
Microsoft-Windows-Kernel-Network {7DD42A49-5329-4832-8DFD-43D979153A88}
Microsoft-Windows-Kernel-PnP {9C205A39-1250-487D-ABD7-E831C6290539}
Microsoft-Windows-Kernel-Power {331C3B3A-2005-44C2-AC5E-77220C37D6B4}
Microsoft-Windows-Kernel-Prefetch {5322D61A-9EFA-4BC3-A3F9-14BE95C144F8}
Microsoft-Windows-Kernel-Process {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}
...
Affichage des informations de fournisseur ETW existantes
DTrace a la possibilité de générer des événements ETW. Cela est utile dans les scénarios où un pipeline ETW est déjà en place pour signaler, collecter et analyser.
Utilisez cet exemple de commande DTrace pour signaler les événements du fournisseur Microsoft-Windows-Kernel-Memory.
C:\>dtrace -n "etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12"
dtrace: description 'etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12' matched 1 probe
CPU ID FUNCTION:NAME
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
Ajout de nouveaux événements ETW
Les événements de trace Etw peuvent être créés en appelant la macro etw_trace. Les événements ne seront enregistrés que s’il existe un écouteur actif pour le fournisseur de trace spécifié, sinon ils seront ignorés.
La macro etw_trace prend en charge les types de données de base tels que int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 et string. Pour plus d’informations, consultez le tableau des types de données ETW pris en charge ci-dessous.
Exemple de macro ETW_TRACE :
Ce script génère un événement ETW personnalisé lorsque la routine syscall retourne 0xc0000001 - STATUS_UNSUCCESSFUL.
Vous pouvez modifier la this->status valeur pour utiliser des valeurs NTSTATUS différentes pour consigner différentes valeurs de retour syscall.
syscall:::return
{
this->status = (uint32_t) arg0;
if (this->status == 0xc0000001UL)
{
etw_trace
(
"Tools.DTrace.Platform", /* Provider Name */
"AAD330CC-4BB9-588A-B252-08276853AF02", /* Provider GUID */
"My custom event from DTrace", /* Event Name */
1, /* Event Level (0 - 5) */
0x0000000000000020, /* Flag */
"etw_int32", /* Field_1 Name */
"PID",/* Field_1 Type */
(int32_t)pid, /* Field_1 Value */
"etw_string", /* Field_2 Name */
"Execname", /* Field_2 type */
execname, /* Field_2 Value */
"etw_string", /* Field_3 Name */
"Probefunc", /* Field_3 type */
probefunc /* Field_3 Value */
);
}
}
C:\> dtrace -s addnewetwevent.d
dtrace: script 'addnewetwevent.d' matched 1881 probes
CPU ID FUNCTION:NAME
0 93 NtAlpcSendWaitReceivePort:return
0 93 NtAlpcSendWaitReceivePort:return
0 93 NtAlpcSendWaitReceivePort:return
Exemple de code ETW NUMA MEM STATS
Cet exemple de script utilise le fournisseur ETW Microsoft-Windows-Kernel-Memory pour vider la mémoire du nœud NUMA. La taille de page peut être convertie en taille en Ko en multipliant par 4. Pour obtenir des informations générales sur NUMA, consultez la prise en charge de NUMA.
Ce code se trouve également à l’adresse suivante : https://github.com/microsoft/DTrace-on-Windows/blob/windows/samples/windows/etw/numamemstats.d
typedef struct KernelMemInfoEvent
{
struct nt`_EVENT_HEADER _EH;
uint32_t PartitionId;
uint32_t Count;
uint32_t NodeNumber;
}kmi;
typedef struct MemoryNodeInfo
{
uint64_t TotalPageCount;
uint64_t SmallFreePageCount;
uint64_t SmallZeroPageCount;
uint64_t MediumFreePageCount;
uint64_t MediumZeroPageCount;
uint64_t LargeFreePageCount;
uint64_t LargeZeroPageCount;
uint64_t HugeFreePageCount;
uint64_t HugeZeroPageCount;
}m_nodeinfo;
int printcounter;
BEGIN
{
printcounter = 0;
}
/* MemNodeInfo */
etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12
{
if (printcounter%10 == 0)
{
printf ("\n \n");
printf("Partition ID: %d \n",((kmi *)arg0)->PartitionId);
printf("Count: %d \n", ((kmi *)arg0)->Count);
printf("Node number: %d\n", ((kmi *)arg0)->NodeNumber);
counters = (m_nodeinfo*)(arg0 + sizeof(struct nt`_EVENT_HEADER) + 12);
print(*counters);
/* Dump rest of the NUMA node info */
if (((kmi *)arg0)->Count > 1)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(1)) + (sizeof(uint32_t)*(1)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(1)) + (sizeof(uint32_t)*(1)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 2)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(2)) + (sizeof(uint32_t)*(2)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(2)) + (sizeof(uint32_t)*(2)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 3)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(3)) + (sizeof(uint32_t)*(3)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(3)) + (sizeof(uint32_t)*(3)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 4)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(4)) + (sizeof(uint32_t)*(4)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(4)) + (sizeof(uint32_t)*(4)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 5)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(5)) + (sizeof(uint32_t)*(5)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(5)) + (sizeof(uint32_t)*(5)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 6)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(6)) + (sizeof(uint32_t)*(6)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(6)) + (sizeof(uint32_t)*(6)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 7)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(7)) + (sizeof(uint32_t)*(7)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(7)) + (sizeof(uint32_t)*(7)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 8)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(8)) + (sizeof(uint32_t)*(8)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(8)) + (sizeof(uint32_t)*(8)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 9)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(9)) + (sizeof(uint32_t)*(9)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(9)) + (sizeof(uint32_t)*(9)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 10)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(10)) + (sizeof(uint32_t)*(10)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(10)) + (sizeof(uint32_t)*(10)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 11)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(11)) + (sizeof(uint32_t)*(11)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(11)) + (sizeof(uint32_t)*(11)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 12)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(12)) + (sizeof(uint32_t)*(12)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(12)) + (sizeof(uint32_t)*(12)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 13)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(13)) + (sizeof(uint32_t)*(13)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(13)) + (sizeof(uint32_t)*(13)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 14)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(14)) + (sizeof(uint32_t)*(14)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(14)) + (sizeof(uint32_t)*(14)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 15)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(15)) + (sizeof(uint32_t)*(15)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(15)) + (sizeof(uint32_t)*(15)) + sizeof(uint32_t));
print(*counters);
}
}
exit(1);
printcounter++;
}
Enregistrer le fichier sous forme d’etwnumamemstats.d
Ouvrez une invite de commandes en tant qu’administrateur et exécutez le script à l’aide de l’option -s.
S’exécutant sur un PC Windows client, un seul nœud NUMA s’affiche.
C:\> dtrace -s etwnumamemstats.d
trace: script 'etwnumamemstats.d' matched 36 probes
CPU ID FUNCTION:NAME
0 42735 0xff_0xffffffffffffffff:12
Partition ID: 0
Count: 1
Node number: 1
m_nodeinfo {
uint64_t TotalPageCount = 0xab98d
uint64_t SmallFreePageCount = 0
uint64_t SmallZeroPageCount = 0x1bec
uint64_t MediumFreePageCount = 0
uint64_t MediumZeroPageCount = 0x5a
uint64_t LargeFreePageCount = 0
uint64_t LargeZeroPageCount = 0
uint64_t HugeFreePageCount = 0
uint64_t HugeZeroPageCount = 0
}
0 42735 0xff_0xffffffffffffffff:12
Types de données ETW pris en charge
| Type ETW | Type de données de langage D | Remarques |
|---|---|---|
| etw_struct | Nombre entier | La valeur de charge utile de ce type représente le nombre de membres qu’une nouvelle structure aura. |
| etw_string | ficelle | N/A |
| etw_mbcsstring | ficelle | N/A |
| etw_int8 | Nombre entier | La taille du type doit correspondre et la conversion en « int8_t » dans le script D est conseillée. |
| etw_uint8 | Nombre entier | La taille du type doit correspondre et la conversion en « uint8_t » dans le script D est conseillée. |
| etw_int16 | Nombre entier | La taille du type doit correspondre et la conversion en « int16_t » dans le script D est conseillée. |
| etw_uint16 | Nombre entier | La taille du type doit correspondre et la conversion en « uint16_t » dans le script D est conseillée. |
| etw_int32 | Nombre entier | N/A |
| etw_uint32 | Nombre entier | N/A |
| etw_int64 | Nombre entier | Le type doit être explicitement « int64_t », car D est défini par défaut sur « int32_t ». |
| etw_uint64 | Nombre entier | Le type doit être explicitement « int64_t », car D est défini par défaut sur « int32_t ». |
| etw_float | Scalaire | Les constantes à virgule flottante ne sont pas autorisées dans le script D, mais elles l’autorisent sur les symboles chargés |
| etw_double | Scalaire | Les constantes à virgule flottante ne sont pas autorisées dans le script D, mais elles l’autorisent sur les symboles chargés |
| etw_bool32 | Nombre entier | N/A |
| etw_hexint32 | Nombre entier | N/A |
| etw_hexint64 | Nombre entier | Le type doit être explicitement « int64_t », car D est défini par défaut sur « int32_t ». |
| etw_countedmbcsstring | Nombre entier | N/A |
| etw_intptr | Nombre entier | La taille du type de données change en fonction de l’architecture ('int32_t' et 'int64_t') |
| etw_uintptr | Nombre entier | La taille du type de données change en fonction de l’architecture ('int32_t' et 'int64_t') |
| etw_pointer | Nombre entier | La taille du type de données change en fonction de l’architecture ('int32_t' et 'int64_t') |
| etw_char16 | Nombre entier | La taille du type doit correspondre et la conversion en « int16_t » dans le script D est conseillée. |
| etw_char8 | Nombre entier | La taille du type doit correspondre et la conversion en « int8_t » dans le script D est conseillée. |
| etw_bool8 | Nombre entier | La taille du type doit correspondre et la conversion en « int8_t » dans le script D est conseillée. |
| etw_hexint8 | Nombre entier | La taille du type doit correspondre et la conversion en « int8_t » dans le script D est conseillée. |
| etw_hexint16 | Nombre entier | La taille du type doit correspondre et la conversion en « int16_t » dans le script D est conseillée. |
| etw_pid | Nombre entier | N/A |
| etw_tid | Nombre entier | N/A |
| etw_mbcsxml | Nombre entier | N/A |
| etw_mbcsjson | Nombre entier | N/A |
| etw_countedmbcsxml | Nombre entier | N/A |
| etw_countedmbcsjson | Nombre entier | N/A |
| etw_win32error | Nombre entier | N/A |
| etw_ntstatus | Nombre entier | N/A |
| etw_hresult | Nombre entier | N/A |