DTrace ETW
Utilisez DTrace pour Windows pour traiter des é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 consigner les événements définis par le noyau ou l’application dans un fichier journal. Vous pouvez utiliser 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 plus d’informations générales sur ETW, consultez À propos du suivi d’événements.
Notes
DTrace est pris en charge dans les builds Insider de Windows après la version 18980 et Windows Server Build 18975.
Pour plus d’informations générales sur l’utilisation de DTrace sur Windows, consultez DTrace.
Fournisseur DTrace Windows ETW
Vous pouvez utiliser DTrace pour capturer et signaler des événements ETW basés sur des manifestes et des traces. Pour sonder des mots clés/niveaux/id d’événement spécifiques, les sondes ETW fonctionnent de manière beaucoup plus fiable si vous n’utilisez pas de caractères génériques. Au lieu de cela, spécifiez entièrement votre sonde en fonction des règles suivantes :
Probename = etw
Modname = Guid du fournisseur au format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx, en utilisant tous les caractères minuscules.
Funcname = Level_Keyword du formulaire 0x00_0x0000000000000000. Pour correspondre à tout ce qui doit être défini 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 une carte générique (*) pour les événements enregistrés.
La charge utile ETW est accessible via arg0. Il s’agit d’une _EVENT_HEADER suivie d’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 pour les scénarios où il existe un pipeline ETW existant 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 ETW_TRACE macro :
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 une autre valeur NTSTATUS pour journaliser 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 plus d’informations générales sur NUMA, consultez Support NUMA.
Ce code se trouve également à l’adresse 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++;
}
Enregistrez le fichier sous 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 langage D | Remarques |
---|---|---|
etw_struct | Integer | La valeur de charge utile de ce type représente le nombre de membres d’une nouvelle structure. |
etw_string | string | N/A |
etw_mbcsstring | string | N/A |
etw_int8 | Integer | La taille du type doit correspondre, et la conversion en « int8_t » dans le script D est recommandée. |
etw_uint8 | Integer | La taille du type doit correspondre, et la conversion en « uint8_t » dans le script D est recommandée. |
etw_int16 | Integer | La taille du type doit correspondre, et la conversion en « int16_t » dans le script D est recommandée. |
etw_uint16 | Integer | La taille du type doit correspondre, et la conversion en « uint16_t » dans le script D est recommandée |
etw_int32 | Integer | N/A |
etw_uint32 | Integer | N/A |
etw_int64 | Integer | Le type doit être explicitement « int64_t », car la valeur par défaut de D est « int32_t » |
etw_uint64 | Integer | Le type doit être explicitement « int64_t », car la valeur par défaut de D est « int32_t » |
etw_float | Scalaire | Les constantes à virgule flottante ne sont pas autorisées dans le script D, mais ne l’autorisent pas sur les symboles chargés |
etw_double | Scalaire | Les constantes à virgule flottante ne sont pas autorisées dans le script D, mais ne l’autorisent pas sur les symboles chargés |
etw_bool32 | Integer | N/A |
etw_hexint32 | Integer | N/A |
etw_hexint64 | Integer | Le type doit être explicitement « int64_t », car la valeur par défaut de D est « int32_t » |
etw_countedmbcsstring | Integer | N/A |
etw_intptr | Integer | La taille du type de données change en fonction de l’architecture (« int32_t » ou « int64_t ») |
etw_uintptr | Integer | La taille du type de données change en fonction de l’architecture (« int32_t » ou « int64_t ») |
etw_pointer | Integer | La taille du type de données change en fonction de l’architecture (« int32_t » ou « int64_t ») |
etw_char16 | Integer | La taille du type doit correspondre, et la conversion en « int16_t » dans le script D est recommandée. |
etw_char8 | Integer | La taille du type doit correspondre, et la conversion en « int8_t » dans le script D est recommandée. |
etw_bool8 | Integer | La taille du type doit correspondre, et la conversion en « int8_t » dans le script D est recommandée. |
etw_hexint8 | Integer | La taille du type doit correspondre, et la conversion en « int8_t » dans le script D est recommandée. |
etw_hexint16 | Integer | La taille du type doit correspondre, et la conversion en « int16_t » dans le script D est recommandée. |
etw_pid | Integer | N/A |
etw_tid | Integer | N/A |
etw_mbcsxml | Integer | N/A |
etw_mbcsjson | Integer | N/A |
etw_countedmbcsxml | Integer | N/A |
etw_countedmbcsjson | Integer | N/A |
etw_win32error | Integer | N/A |
etw_ntstatus | Integer | N/A |
etw_hresult | Integer | NON APPLICABLE |
Voir aussi
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : Tout au long de 2024, nous allons supprimer progressivement GitHub Issues comme mécanisme de commentaires pour le contenu et le remplacer par un nouveau système de commentaires. Pour plus d’informations, consultezEnvoyer et afficher des commentaires pour