Partager via


DTrace ETW

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

Voir aussi

DTrace sur Windows

Programmation Windows DTrace

Exemples de code Windows DTrace

Vidage en direct DTrace