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

DTrace sur Windows

Programmation Windows DTrace

Exemples de code Windows DTrace

DTrace Live Dump