Partager via


DTrace ETW

Utilisez DTrace pour Windows pour traiter les événements ETW existants et pour ajouter de nouveaux événements ETW.

Event Tracing for Windows (ETW) est une installation de traçage au niveau du noyau qui vous permet d’enregistrer 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 pour déterminer où se produisent des problèmes de performance dans l’application. Pour obtenir des informations générales sur ETW, veuillez consulter la section À propos du traçage des é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, veuillez consulter la section DTrace.

Fournisseur ETW Windows DTrace

Vous pouvez utiliser DTrace pour capturer et rapporter des événements ETW enregistrés et basés sur un manifeste. Pour sonder des mots-clés/niveaux/ID d’événements spécifiques, les sondes ETW fonctionneront 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 sous la forme xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, en utilisant uniquement des caractères minuscules.

Funcname = Niveau_Mot-clé sous la forme 0x00_0x0000000000000000. Pour tout correspondre, cela doit être réglé sur 0xff_0xffffffffffffffff.

Probename = ID d’événement entier ou « generic_event » pour correspondre à tous les ID d’événements.

Le filtrage basé sur Probename ne fonctionne que pour les événements manifestés. Utilisez le caractère générique (*) pour les événements journalisés.

La charge utile ETW est accessible via arg0. Celle-ci est composée de nt`_EVENT_HEADER suivie de données spécifiques à 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 sur les fournisseurs ETW existants

DTrace a la capacité de sortir des événements ETW. Cela est utile pour les scénarios où il existe un pipeline ETW pour rapporter, collecter et analyser.

Utilisez cet exemple de commande DTrace pour rapporter 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 y a un récepteur 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. Veuillez consulter le tableau Types de données ETW pris en charge ci-dessous pour plus de détails.

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 changer la valeur this->status pour utiliser d’autres valeurs NTSTATUS pour enregistrer 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

Ce script d’exemple utilise le fournisseur ETW Microsoft-Windows-Kernel-Memory pour vider la mémoire des nœuds NUMA. La taille de la page peut être convertie en taille en Ko en multipliant par 4. Pour des informations générales sur NUMA, veuillez consulter la section Prise en charge de NUMA.

Ce code se trouve également à 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 commande en tant qu’administrateur et exécutez le script en utilisant l’option -s.

Lors de l’exécution sur un PC Windows client, un seul nœud NUMA est affiché.

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 d’ETW Type de données D Language Notes
etw_struct Entier La valeur de la charge utile de ce type représente le nombre de membres qu’une nouvelle structure aura.
etw_string string S/O
etw_mbcsstring string S/O
etw_int8 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « int8_t » dans le script D.
etw_uint8 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « uint8_t » dans le script D.
etw_int16 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « int16_t » dans le script D.
etw_uint16 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « uint16_t » dans le script D.
etw_int32 Entier S/O
etw_uint32 Entier S/O
etw_int64 Entier Le type doit être explicitement « int64_t » car D utilise par défaut « int32_t ».
etw_uint64 Entier Le type doit être explicitement « int64_t » car D utilise par défaut « int32_t ».
etw_float Scalaire Les constantes en virgule flottante ne sont pas autorisées dans le script D, mais elles le sont sur les symboles chargés.
etw_double Scalaire Les constantes en virgule flottante ne sont pas autorisées dans le script D, mais elles le sont sur les symboles chargés.
etw_bool32 Entier S/O
etw_hexint32 Entier S/O
etw_hexint64 Entier Le type doit être explicitement « int64_t » car D utilise par défaut « int32_t ».
etw_countedmbcsstring Entier S/O
etw_intptr Entier La taille du type de données change en fonction de l’architecture (« int32_t » et « int64_t »)
etw_uintptr Entier La taille du type de données change en fonction de l’architecture (« int32_t » et « int64_t »)
etw_pointer Entier La taille du type de données change en fonction de l’architecture (« int32_t » et « int64_t »)
etw_char16 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « int16_t » dans le script D.
etw_char8 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « int8_t » dans le script D.
etw_bool8 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « int8_t » dans le script D.
etw_hexint8 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « int8_t » dans le script D.
etw_hexint16 Entier La taille du type doit correspondre, et il est conseillé de faire un cast en « int16_t » dans le script D.
etw_pid Entier S/O
etw_tid Entier S/O
etw_mbcsxml Entier S/O
etw_mbcsjson Entier S/O
etw_countedmbcsxml Entier S/O
etw_countedmbcsjson Entier S/O
etw_win32error Entier S/O
etw_ntstatus Entier S/O
etw_hresult Entier S/O

Voir aussi

DTrace sur Windows

Programmation Windows DTrace

Exemples de code Windows DTrace

DTrace Live Dump