Partager via


Utiliser le streaming avec TraceProcessor

Par défaut, TraceProcessor accède aux données en les chargeant dans la mémoire lors du traitement de la trace. Cette approche de mise en mémoire tampon est facile à utiliser, mais elle peut être coûteuse en termes d’utilisation de la mémoire.

TraceProcessor fournit également trace.UseStreaming(), qui prend en charge l’accès à plusieurs types de données de trace en streaming (en traitant les données au fil de leur lecture dans le fichier de trace, au lieu de mettre ces données dans une mémoire tampon). Par exemple, une trace syscalls peut être assez volumineuse et la mise en mémoire tampon de la liste complète des syscalls dans une trace peut consommer beaucoup de ressources.

Accès aux données mises en mémoire tampon

Le code suivant montre l’accès à des données syscall de la façon normale avec une mémoire tampon via trace.UseSyscalls() :

using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Processes;
using Microsoft.Windows.EventTracing.Syscalls;
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.Error.WriteLine("Usage: <trace.etl>");
            return;
        }

        using (ITraceProcessor trace = TraceProcessor.Create(args[0]))
        {
            IPendingResult<ISyscallDataSource> pendingSyscallData = trace.UseSyscalls();

            trace.Process();

            ISyscallDataSource syscallData = pendingSyscallData.Result;

            Dictionary<IProcess, int> syscallsPerCommandLine = new Dictionary<IProcess, int>();

            foreach (ISyscall syscall in syscallData.Syscalls)
            {
                IProcess process = syscall.Thread?.Process;

                if (process == null)
                {
                    continue;
                }

                if (!syscallsPerCommandLine.ContainsKey(process))
                {
                    syscallsPerCommandLine.Add(process, 0);
                }

                ++syscallsPerCommandLine[process];
            }

            Console.WriteLine("Process Command Line: Syscalls Count");

            foreach (IProcess process in syscallsPerCommandLine.Keys)
            {
                Console.WriteLine($"{process.CommandLine}: {syscallsPerCommandLine[process]}");
            }
        }
    }
}

Accès aux données de streaming

Avec une trace syscalls volumineuse, tenter de mettre en mémoire tampon les données syscall peut être très coûteux en ressources ou même impossible. Le code suivant montre comment accéder aux mêmes données syscall de streaming, en remplaçant trace.UseSyscalls() par trace.UseStreaming().UseSyscalls() :

using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Processes;
using Microsoft.Windows.EventTracing.Syscalls;
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.Error.WriteLine("Usage: <trace.etl>");
            return;
        }

        using (ITraceProcessor trace = TraceProcessor.Create(args[0]))
        {
            IPendingResult<IThreadDataSource> pendingThreadData = trace.UseThreads();

            Dictionary<IProcess, int> syscallsPerCommandLine = new Dictionary<IProcess, int>();

            trace.UseStreaming().UseSyscalls(ConsumerSchedule.SecondPass, context =>
            {
                Syscall syscall = context.Data;
                IProcess process = syscall.GetThread(pendingThreadData.Result)?.Process;

                if (process == null)
                {
                    return;
                }

                if (!syscallsPerCommandLine.ContainsKey(process))
                {
                    syscallsPerCommandLine.Add(process, 0);
                }

                ++syscallsPerCommandLine[process];
            });

            trace.Process();

            Console.WriteLine("Process Command Line: Syscalls Count");

            foreach (IProcess process in syscallsPerCommandLine.Keys)
            {
                Console.WriteLine($"{process.CommandLine}: {syscallsPerCommandLine[process]}");
            }
        }
    }
}

Fonctionnement du streaming

Par défaut, toutes les données de streaming sont fournies lors de la première passe dans la trace, et les données mises en mémoire tampon provenant d’autres sources ne sont pas disponibles. L’exemple ci-dessus montre comment combiner le streaming avec la mise en mémoire tampon : les données du thread sont mises en mémoire tampon avant que les données syscall ne soient envoyées en streaming. Par conséquent, la trace doit être lue deux fois : une fois pour obtenir les données du thread mises en mémoire tampon, et une deuxième fois pour accéder aux données syscall de streaming avec les données du thread mises en mémoire tampon maintenant disponibles. Pour combiner le streaming et la mise en mémoire tampon de cette façon, l’exemple passe ConsumerSchedule.SecondPass à trace.UseStreaming().UseSyscalls(), ce qui fait que le traitement de syscall se fait dans une seconde passe sur la trace. En s’exécutant dans une deuxième passe, le rappel de syscall peut accéder au résultat en attente provenant de trace.UseThreads() lors du traitement de chaque syscall. Sans cet argument facultatif, le streaming de syscall aurait été exécuté lors de la première passe sur la trace (il n’y aurait qu’une seule passe) et le résultat en attente provenant de trace.UseThreads() ne serait pas encore disponible. Dans ce cas, le rappel aurait néanmoins toujours accès au ThreadId du syscall, mais il n’aurait pas accès au processus pour le thread (car le thread pour traiter les données de liaison est fourni via d’autres événements qui n’ont peut-être pas encore été traités).

Voici les principales différences d’utilisation entre la mise en mémoire tampon et le streaming :

  1. La mise en mémoire tampon retourne un IPendingResult<T> et le résultat qu’il contient est disponible seulement avant que la trace ait été traitée. Une fois que la trace a été traitée, les résultats peuvent être énumérés en utilisant des techniques comme foreach et LINQ.
  2. Le streaming retourne void et prend à la place un argument de rappel. Il appelle le rappel une fois que chaque élément est disponible. Comme les données ne sont pas mises en mémoire tampon, il n’y a jamais de liste de résultats à énumérer avec foreach ou LINQ : le rappel du streaming doit mettre en mémoire tampon la partie des données qu’il veut enregistrer pour une utilisation une fois le traitement terminé.
  3. Le code pour le traitement des données mises en mémoire tampon figure après l’appel à trace.Process(), quand les résultats en attente sont disponibles.
  4. Le code pour le traitement des données de streaming apparaît avant l’appel à trace.Process(), en tant que rappel de la méthode trace.UseStreaming.Use...().
  5. Un consommateur de streaming peut choisir de traiter seulement une partie du flux et d’annuler les rappels futurs en appelant context.Cancel(). Un consommateur de mise en mémoire tampon reçoit toujours une liste complète mise en mémoire tampon.

Données de streaming corrélées

Parfois, les données de trace sont fournies dans une séquence d’événements : par exemple, les syscalls sont journalisés via des événements d’entrée et de sortie distincts, mais les données combinées des deux événements peuvent être plus utiles. La méthode trace.UseStreaming().UseSyscalls() met en corrélation les données de ces deux événements et les fournit quand la paire devient disponible. Quelques types de données corrélées sont disponibles via trace.UseStreaming() :

Code Description
trace.UseStreaming().UseContextSwitchData() Envoie en streaming des données de changement de contexte corrélées (provenant d’événements compacts et non compacts, avec des SwitchInThreadIds plus précis que des événements bruts non compacts).
trace.UseStreaming().UseScheduledTasks() Envoie en streaming des données de tâches planifiées corrélées.
trace.UseStreaming().UseSyscalls() Envoie en streaming des données d’appel système corrélées.
trace.UseStreaming().UseWindowInFocus() Envoie en streaming des données corrélées de fenêtre ayant le focus.

Événements de streaming autonomes

En outre, trace.UseStreaming() fournit des événements analysés pour plusieurs autres types d’événements autonomes :

Code Description
trace.UseStreaming().UseLastBranchRecordEvents() Envoie en streaming des événements LBR (last branch record) analysés.
trace.UseStreaming().UseReadyThreadEvents() Envoie en streaming des événements de thread prêts et analysés.
trace.UseStreaming().UseThreadCreateEvents() Envoie en streaming des événements de création de thread analysés.
trace.UseStreaming().UseThreadExitEvents() Envoie en streaming des événements de sortie de thread analysés.
trace.UseStreaming().UseThreadRundownStartEvents() Envoie en streaming des événements analysés de début de diminution d’activité des threads.
trace.UseStreaming().UseThreadRundownStopEvents() Envoie en streaming des événements analysés de fin de diminution d’activité des threads.
trace.UseStreaming().UseThreadSetNameEvents() Envoie en streaming des événements de définition de nom de thread analysés.

Événements de streaming sous-jacents pour des données corrélées

Enfin, trace.UseStreaming() fournit également les événements sous-jacents utilisés pour corréler les données de la liste ci-dessus. Ces événements sous-jacents sont :

Code Description Inclus dans
trace.UseStreaming().UseCompactContextSwitchEvents() Envoie en streaming des événements de changement de contexte compact analysés. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseContextSwitchEvents() Envoie en streaming des événements de changement de contexte analysés. SwitchInThreadIds peut ne pas être exact dans certains cas. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseFocusChangeEvents() Envoie en streaming des événements de changement de focus de fenêtre analysés. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseScheduledTaskStartEvents() Envoie en streaming des événements de démarrage de tâche planifiée analysés. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskStopEvents() Envoie en streaming des événements d’arrêt de tâche planifiée analysés. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskTriggerEvents() Envoie en streaming des événements de déclencheur de tâche planifiée analysés. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseSessionLayerSetActiveWindowEvents() Envoie en streaming des événements analysés de fenêtre active définie au niveau de la couche session. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseSyscallEnterEvents() Envoie en streaming des événements d’entrée de syscall analysés. trace.UseStreaming().UseSyscalls()
trace.UseStreaming().UseSyscallExitEvents() Envoie en streaming des événements de sortie de syscall analysés. trace.UseStreaming().UseSyscalls()

Étapes suivantes

Dans ce tutoriel, vous avez découvert comment utiliser le streaming pour accéder immédiatement aux données de trace et utiliser moins de mémoire.

L’étape suivante est de chercher à accéder aux données souhaitées dans vos traces. Examinez les exemples pour trouver quelques idées. Notez que certaines traces n’incluent pas tous les types de données pris en charge.