void ProcessData() { try { using (myRealtimeSession = new TraceEventSession("WMIWatcher_Realtime")) { myRealtimeSession.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process, KernelTraceEventParser.Keywords.None); myRealtimeSession.EnableProvider(WMIProviderDefinitions.WMI_Activity_Provider_Name, TraceEventLevel.Verbose, WMIProviderDefinitions.Keyword_WMI_Activity_Trace); using (myTraceLogSource = TraceLog.CreateFromTraceEventSession(myRealtimeSession)) { WMIEventParser parser = new WMIEventParser(myTraceLogSource, "WmiWatcher", AutoLoggerFileName); myTraceLogSource.Dynamic.All += parser.Parse; parser.OnWMIOperationStart += Parser_OnWMIOperationStart; //parser.OnWMIOperationStop += Parser_OnWMIOperationStop; parser.OnWMIExecAsync += Parser_OnWMIExecAsync; parser.OnProcessEndedWithDuration += Parser_OnProcessEndedWithDuration; myTraceLogSource.Process(); } } } catch (Exception ex) { FileLogger.Logger.Log($"Error: Got Exception while leaving ProcessData: {ex}"); throw; } }
private void SetupProviderAndParser() { try { //enabling the providers if (SessionAttribute.IsKernelSession) { if (SessionAttribute is KernelTraceSessionAttribute) { traceEventSession.EnableKernelProvider(((KernelTraceSessionAttribute)SessionAttribute).KernelSessionKeywords); } else { traceEventSession.EnableKernelProvider(KernelTraceEventParser.Keywords.Default); } } else { traceEventSession.EnableProvider(SessionAttribute.providerName, SessionAttribute.providerLevel, options: SessionAttribute.TraceEventProviderOptions); } //creating the trace event parser traceEventParser = Activator.CreateInstance(SessionAttribute.eventParserType, traceEventSession.Source) as TraceEventParser; //set the trace event parser through session's properties traceEventParser.AddCallbackForProviderEvents(CallbackForProviderEvents, CallbackForEvents); } catch (Exception ex) { TraceSessionError(ex); } }
/// <summary> /// Observe Out-of-Process ETW Kernel TraceEvent. /// </summary> public static IObservable <TraceEvent> FromKernelTraceEvent(KernelTraceEventParser.Keywords flags, KernelTraceEventParser.Keywords stackCapture = KernelTraceEventParser.Keywords.None) { IConnectableObservable <TraceEvent> source; var session = new TraceEventSession("ObservableEventListenerFromKernelTraceEventSession." + Guid.NewGuid().ToString()); try { var guid = KernelTraceEventParser.ProviderGuid; session.EnableKernelProvider(flags, stackCapture); // needs enable before observe source = session.Source.Kernel.Observe((pName, eName) => EventFilterResponse.AcceptEvent) .Where(x => x.ProviderGuid == guid && x.EventName != ManifestEventName && x.ID != ManifestEventID) .Finally(() => session.Dispose()) .Publish(); } catch { session.Dispose(); throw; } Task.Factory.StartNew(state => { using (session) { session.Source.Process(); } }, TaskCreationOptions.LongRunning); return(source.RefCount()); }
/// <summary> /// Observe Out-of-Process ETW Kernel TraceEvent. /// </summary> public static IObservable <TData> FromKernelTraceEvent <TData>(KernelTraceEventParser.Keywords flags, KernelTraceEventParser.Keywords stackCapture = KernelTraceEventParser.Keywords.None) where TData : TraceEvent { IConnectableObservable <TData> source; var session = new TraceEventSession("ObservableEventListenerFromKernelTraceEventSession." + Guid.NewGuid().ToString()); try { session.EnableKernelProvider(flags, stackCapture); source = session.Source.Kernel.Observe <TData>().Finally(() => session.Dispose()).Publish(); } catch { session.Dispose(); throw; } Task.Factory.StartNew(state => { using (session) { session.Source.Process(); } }, TaskCreationOptions.LongRunning); return(source.RefCount()); }
public void Start(IEnumerable <EventType> types) { if (EventTrace == null) { throw new InvalidOperationException("Must register for event notifications"); } _kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, TraceEventSessionOptions.NoRestartOnCreate) { BufferSizeMB = 128, CpuSampleIntervalMSec = 10, }; var keywords = KernelTraceEventParser.Keywords.All; _processingThread = new Thread(() => { _kernelSession.EnableKernelProvider(keywords); _kernelParser = new KernelTraceEventParser(_kernelSession.Source); SetupCallbacks(types); _kernelSession.Source.Process(); }); _processingThread.Priority = ThreadPriority.Lowest; _processingThread.IsBackground = true; _processingThread.Start(); }
/// <summary> /// Starts the ETW session, and processes the stack samples that come in. This method /// does not return until another thread calls <see cref="Stop"/> to stop the session. /// While this method is executing, live stack aggregates can be obtained from the /// <see cref="Stacks"/> property, which is thread-safe. /// </summary> public void Start() { _session = new TraceEventSession($"LiveStacks-{Process.GetCurrentProcess().Id}"); // TODO Make the CPU sampling interval configurable, although changing it doesn't seem to work in a VM? // _session.CpuSampleIntervalMSec = 10.0f; // TODO Should we use _session.StackCompression? What would the events look like? if (_provider == "kernel") { _session.EnableKernelProvider(_kernelKeyword, stackCapture: _kernelKeyword); _session.Source.Kernel.StackWalkStack += OnKernelStackEvent; } if (_provider == "clr") { _session.EnableProvider( ClrTraceEventParser.ProviderGuid, matchAnyKeywords: (ulong)(_clrKeyword | ClrTraceEventParser.Keywords.Stack)); _session.Source.Clr.All += OnAnyClrEvent; _session.Source.Clr.ClrStackWalk += OnClrStackEvent; } _session.Source.Process(); }
public void MonitorNetwork() { var sessionName = "NT Kernel Logger"; using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session' { session.StopOnDispose = true; using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session)) { Action <TraceEvent> action = delegate(TraceEvent data) { if (ready) { tHandler = new TimeoutHandler(1); tHandler.SessionTimeout += tHandler_SessionTimeout; ready = false; addData(data); } }; var registeredParser = new RegisteredTraceEventParser(source); registeredParser.All += action; session.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP); source.Process(); } } }
/// <summary> /// Dump the dot net Heap for process 'processID' to the etl file name 'etlFileName'. Send diagnostics to 'log'. /// If 'memoryGraph is non-null also update it to contain the heap Dump. If null you get just the ETL file. /// returns true if successful. /// </summary> static public bool DumpAsEtlFile(int processID, string etlFileName, TextWriter log, MemoryGraph memoryGraph = null, DotNetHeapInfo dotNetInfo = null) { bool success = false; log.WriteLine("Starting ETW logging on File {0}", etlFileName); using (var session = new TraceEventSession("PerfViewGCHeapETLSession", etlFileName)) { session.EnableKernelProvider(KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread | KernelTraceEventParser.Keywords.ImageLoad); // Isolate this to a single process. var options = new TraceEventProviderOptions() { ProcessIDFilter = new List <int>() { processID } }; // For non-project N we need module rundown to figure out the correct module name session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)(ClrRundownTraceEventParser.Keywords.Loader | ClrRundownTraceEventParser.Keywords.ForceEndRundown), options); session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.GCHeapDump | ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.GCHeapAndTypeNames), options); // Project N support. session.EnableProvider(ClrTraceEventParser.NativeProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.GCHeapDump | ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.GCHeapAndTypeNames), options); success = Dump(processID, memoryGraph, log, dotNetInfo); log.WriteLine("Stopping ETW logging on {0}", etlFileName); } log.WriteLine("DumpAsETLFile returns. Success={0}", success); return(success); }
static void Main(string[] args) { Out = Console.Out; Out.WriteLine("Hello World!"); using (var session = new TraceEventSession("pri")) { void ConsoleOnCancelKeyPress(object sender, ConsoleCancelEventArgs e) { Console.CancelKeyPress -= ConsoleOnCancelKeyPress; session?.Dispose(); } Console.CancelKeyPress += ConsoleOnCancelKeyPress; session.EnableKernelProvider(KernelTraceEventParser.Keywords.Registry); session.Source.Kernel.RegistryCreate += KernelParser_RegistryCreate; session.Source.Kernel.RegistrySetValue += KernelParser_RegistrySetValue; session.Source.Kernel.RegistryDeleteValue += KernelParser_RegistryDeleteValue; session.Source.Kernel.RegistryDelete += KernelParser_RegistryDelete; session.Source.Kernel.RegistryKCBCreate += KernelParser_RegistryKCBCreate; session.Source.Kernel.RegistryKCBDelete += KernelParser_RegistryKCBDelete; session.Source.Kernel.RegistryKCBRundownBegin += Kernel_RegistryKCBRundownBegin; session.Source.Kernel.RegistryKCBRundownEnd += Kernel_RegistryKCBRundownEnd; Out.WriteLine("Collecting events"); session.Source.Process(); Out.WriteLine("Stopping collection of events"); } }
public ETWCollector(KernelTraceEventParser.Keywords flags = TraceProcessFlags) { Session = new TraceEventSession("Optick"); Session.BufferSizeMB = 256; Session.EnableKernelProvider(flags); // Processes Session.Source.Kernel.ProcessStart += Kernel_ProcessStart; Session.Source.Kernel.ProcessStop += Kernel_ProcessStop; // Image Session.Source.Kernel.ImageLoad += Kernel_ImageLoad; // Threads Session.Source.Kernel.ThreadStart += Kernel_ThreadStart; Session.Source.Kernel.ThreadStop += Kernel_ThreadStop; // IO Session.Source.Kernel.FileIORead += Kernel_FileIORead; Session.Source.Kernel.FileIOWrite += Kernel_FileIOWrite; Session.Source.Kernel.FileIOOperationEnd += Kernel_FileIOOperationEnd; // SysCalls Session.Source.Kernel.PerfInfoSysClEnter += Kernel_PerfInfoSysClEnter; Session.Source.Kernel.PerfInfoSysClExit += Kernel_PerfInfoSysClExit; // Switch Contexts Session.Source.Kernel.ThreadCSwitch += Kernel_ThreadCSwitch; // Samples Session.Source.Kernel.StackWalkStack += Kernel_StackWalkStack; }
public CollectTraceEventsHelper(CommandLineOptions options) { if (TraceEventSession.IsElevated() != true) throw new InvalidOperationException("Collecting perf events requires administrative privileges."); if (options.ClrEvents.Events == ClrTraceEventParser.Keywords.None && options.KernelEvents == KernelTraceEventParser.Keywords.None) throw new PowerArgs.ArgException("Must specify at least one CLR or kernel event."); // verify session name var existingSessions = TraceEventSession.GetActiveSessionNames(); if (options.Mode == ExecutionMode.Start && existingSessions.Contains(options.TestName)) throw new InvalidOperationException(string.Format("The session name '{0}' is already in use.", options.TestName)); else if (options.Mode == ExecutionMode.Stop && !existingSessions.Contains(options.TestName)) throw new InvalidOperationException(string.Format("The session name '{0}' does not exist.", options.TestName)); m_traceSession = new TraceEventSession(options.TestName, options.DataFile); if (options.Mode == ExecutionMode.Start) { m_traceSession.BufferSizeMB = 512; // starting a new session, enable providers m_traceSession.EnableKernelProvider(options.KernelEvents); m_traceSession.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)options.ClrEvents.Events); // keep the session active after the process terminates m_traceSession.StopOnDispose = false; } }
public void Run() { var isElevated = TraceEventSession.IsElevated() ?? false; if (!isElevated) { Console.WriteLine("ETW tracing of kernel providers requires admin rights"); Console.WriteLine("Please restart and run as administrator"); return; } Console.WriteLine("Start processing"); Console.CancelKeyPress += (sender, args) => Dispose(); _session.EnableKernelProvider(KernelTraceEventParser.Keywords.All); _session.Source.Kernel.ProcessStart += data => { Console.WriteLine($"Starting process {GetProcessName(data)}"); }; _session.Source.Kernel.ProcessStop += data => { Console.WriteLine($"Stopping process {GetProcessName(data)}"); }; _timer = InitializeTimer(); _session.Source.Process(); }
/// <summary> /// Interface to enable kernel providers. /// </summary> /// <param name="flags">Specifies the particular kernel events of interest.</param> /// <param name="stackCapture">Specifies which events should have their stack traces captured when an event is logged.</param> public void EnableKernelProvider(KernelTraceEventParser.Keywords flags, KernelTraceEventParser.Keywords stackCapture) { if (TraceEventSession.EnableKernelProvider(flags, stackCapture)) { Debug.WriteLine("The session existed before and needed to be restarted."); } }
public static void Run() { if (TraceEventSession.IsElevated() != true) { Console.WriteLine("Must be elevated (Admin) to run this program."); Debugger.Break(); return; } // Set up Ctrl-C to stop both user mode and kernel mode sessions Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs cancelArgs) => { StopSessions(); cancelArgs.Cancel = true; }; Console.WriteLine("Start monitoring for ransomware file activity"); var task1 = Task.Run(() => { using (s_kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName)) { //This is the best spot to examine for efficiency improvments //As you might expect, listening to large amounts of kernel events can be slow s_kernelSession.EnableKernelProvider( KernelTraceEventParser.Keywords.All ); s_kernelSession.Source.Kernel.All += FilterIOEvents; // process events until Ctrl-C is pressed s_kernelSession.Source.Process(); } Console.WriteLine("Thread 1 dieing"); }); Task.WaitAll(task1); }
public void Start(IEnumerable <EventType> types, bool includeInit) { if (EventTrace == null) { throw new InvalidOperationException("Must register for event notifications"); } _includeInit = includeInit; _session = new TraceEventSession(KernelTraceEventParser.KernelSessionName) { BufferSizeMB = 128, CpuSampleIntervalMSec = 10 }; var keywords = KernelTraceEventParser.Keywords.None; foreach (var type in types) { keywords |= EventInfo.AllEventsByType[type].Keyword; } _session.EnableKernelProvider(keywords); _processingThread = new Thread(() => { _parser = new KernelTraceEventParser(_session.Source); SetupCallbacks(types); _session.Source.Process(); }); _processingThread.Priority = ThreadPriority.Lowest; _processingThread.IsBackground = true; _processingThread.Start(); }
private void SetupProviders(TraceEventSession session) { // Note: the kernel provider MUST be the first provider to be enabled // If the kernel provider is not enabled, the callstacks for CLR events are still received // but the symbols are not found (except for the application itself) // TraceEvent implementation details triggered when a module (image) is loaded session.EnableKernelProvider( KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process, KernelTraceEventParser.Keywords.None ); session.EnableProvider( ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, // this is needed in order to receive AllocationTick_V2 event (ulong)( // required to receive AllocationTick events ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Jit | // Turning on JIT events is necessary to resolve JIT compiled code ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | // This is needed if you want line number information in the stacks ClrTraceEventParser.Keywords.Loader | // You must include loader events as well to resolve JIT compiled code. ClrTraceEventParser.Keywords.Stack // Get the callstack for each event ) ); // this provider will send events of already JITed methods session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)( ClrTraceEventParser.Keywords.Jit | // We need JIT events to be rundown to resolve method names ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | // This is needed if you want line number information in the stacks ClrTraceEventParser.Keywords.Loader | // As well as the module load events. ClrTraceEventParser.Keywords.StartEnumeration // This indicates to do the rundown now (at enable time) )); }
internal override Session EnableProviders() { var keywords = Config.KernelKeywords | KernelTraceEventParser.Keywords.ImageLoad // handles stack frames from native modules, SUPER IMPORTANT! | KernelTraceEventParser.Keywords.Profile; // CPU stacks if (Details.Config.GetHardwareCounters().Any()) { keywords |= KernelTraceEventParser.Keywords.PMCProfile; // Precise Machine Counters } TraceEventSession.StackCompression = true; try { TraceEventSession.EnableKernelProvider(keywords, Config.KernelStackKeywords); } catch (Win32Exception) { Details.Config.GetCompositeLogger().WriteLineError( "Please install the latest Microsoft.Diagnostics.Tracing.TraceEvent package in the project with benchmarks so MSBuild can copy the native dependencies of TraceEvent to the output folder."); throw; } return(this); }
static void Main(string[] args) { if (!(TraceEventSession.IsElevated() ?? false)) { Console.WriteLine("Admin deÄŸilsin"); return; } using (var kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName)) { Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { kernelSession.Dispose(); }; kernelSession.EnableKernelProvider( KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.NetworkTCPIP | KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Registry ); kernelSession.Source.Kernel.ProcessStart += _processStart; kernelSession.Source.Kernel.TcpIpConnect += _tcpMetod; kernelSession.Source.Kernel.ImageLoad += _imageLoad; kernelSession.Source.Kernel.RegistryCreate += _regCreate; kernelSession.Source.Process(); } }
private void StartEtwSession() { try { ResetCounters(); using (_etwSession = new TraceEventSession("KernelTcpIpEventsSession")) { _etwSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP); _etwSession.Source.Kernel.TcpIpRecv += data => { if (_processList().Contains(data.ProcessID)) { Interlocked.Add(ref _counters.processDownloadSpeed, data.size * 8); Interlocked.Add(ref _counters.processTotalBytesReceived, data.size); } }; _etwSession.Source.Kernel.TcpIpSend += data => { if (_processList().Contains(data.ProcessID)) { Interlocked.Add(ref _counters.processUploadSpeed, data.size * 8); Interlocked.Add(ref _counters.processTotalBytesSent, data.size); } }; _etwSession.Source.Process(); } } catch { ResetCounters(); } }
void StartSession() { kernelsession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP); kernelsession.Source.Kernel.TcpIpSend += Kernel_TcpIpSend; kernelsession.Source.Kernel.TcpIpRecv += Kernel_TcpIpRecv; kernelsession.Source.Process(); }
public void StartWatch() { KernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName); KernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.Process); KernelSession.Source.Kernel.ProcessStart += OnProcessStart; watcherTask = new Task(Watch, cancellationTokenSource.Token); watcherTask.Start(); }
private static void RealTimeSession() { if (options.ParsedClrKeywords == 0 && options.ParsedKernelKeywords == KernelTraceEventParser.Keywords.None && options.OtherProviders.Count == 0) { Bail("No events to collect"); } Console.CancelKeyPress += (_, __) => CloseSession(); if (options.DurationInSeconds > 0) { Task.Delay(TimeSpan.FromSeconds(options.DurationInSeconds)) .ContinueWith(_ => CloseSession()); } using (session = new TraceEventSession("etrace-realtime-session")) { if (options.ParsedKernelKeywords != KernelTraceEventParser.Keywords.None) { session.EnableKernelProvider(options.ParsedKernelKeywords); } if (options.ParsedClrKeywords != 0) { session.EnableProvider(ClrTraceEventParser.ProviderGuid, matchAnyKeywords: (ulong)options.ParsedClrKeywords); } if (options.OtherProviders.Any()) { foreach (var provider in options.OtherProviders) { Guid guid; if (Guid.TryParse(provider, out guid)) { session.EnableProvider(Guid.Parse(provider)); } else { guid = TraceEventProviders.GetProviderGuidByName(provider); if (guid != Guid.Empty) { session.EnableProvider(guid); } } } } if (options.IsOutFile) { outRelogger = new ETWReloggerTraceEventSource(session.SessionName, TraceEventSourceType.Session, options.OutFile); isOutReloggerSet = true; ProcessTrace(outRelogger); } else { ProcessTrace(session.Source); } } }
static void Main(string[] args) { int samples = 0; var samplesPerProcess = new Dictionary <(int, string), int>(500); Console.WriteLine("Press ENTER to start"); Console.ReadLine(); Console.WriteLine("Profiling for 10 seconds..."); using (var session = new TraceEventSession(KernelTraceEventParser.KernelSessionName)) { session.EnableKernelProvider(KernelTraceEventParser.Keywords.Profile); var parser = session.Source.Kernel; parser.PerfInfoSample += sample => { var name = sample.ProcessName; samples++; if (sample.ProcessID < 0) { if (sample.NonProcess) { name = "(DPC/ISR)"; } } try { if (string.IsNullOrEmpty(name) && sample.ProcessID >= 0) { name = Process.GetProcessById(sample.ProcessID)?.ProcessName; } } catch { name = "<Unknown>"; } var key = (sample.ProcessID, name); if (samplesPerProcess.ContainsKey(key)) { samplesPerProcess[key]++; } else { samplesPerProcess.Add(key, 1); } }; parser.PerfInfoSetInterval += e => Console.WriteLine($"New interval: {e.NewInterval}"); parser.LostEvent += e => Console.WriteLine("Event lost"); Task.Run(() => session.Source.Process()); Thread.Sleep(10000); } Console.WriteLine($"Analyzing {samples} samples"); foreach (var item in samplesPerProcess.OrderByDescending(pair => pair.Value).TakeWhile(pair => pair.Value * 100.0f / samples >= 1)) { Console.WriteLine($"{item}: {item.Value * 100.0f / samples:N2} %"); } }
protected override void Capture(TraceEventSession session) { session.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP, KernelTraceEventParser.Keywords.NetworkTCPIP); //TraceLogEventSource is required on win 7 //see: https://github.com/Microsoft/dotnetsamples/blob/master/Microsoft.Diagnostics.Tracing/TraceEvent/TraceEvent/41_TraceLogMonitor.cs using (TraceLogEventSource traceLogSource = TraceLog.CreateFromTraceEventSession(session)) { //two events in a row for same process and IP/port means failure. //It could be a firewall intervention, silently droping packets to that destination //or the end service is down. traceLogSource.Kernel.TcpIpReconnect += (TcpIpTraceData data) => { try { _sender.Send(data.ProcessID, data.daddr + ":" + data.dport, TCP_RECONNECT); } catch (Exception) { //TODO: log it //TODO: check all Exceptions that can be thrown by the _sender instance. throw; } }; traceLogSource.Kernel.TcpIpConnect += (TcpIpConnectTraceData data) => { try { _sender.Send(data.ProcessID, data.ToXml(new System.Text.StringBuilder()).ToString(), TCP_CONNECT); } catch (Exception) { //TODO: log it //TODO: check all Exceptions that can be thrown by the _sender instance. throw; } }; traceLogSource.Kernel.TcpIpDisconnect += (TcpIpTraceData data) => { try { _sender.Send(data.ProcessID, data.ToXml(new System.Text.StringBuilder()).ToString(), TCP_CONNECT); } catch (Exception) { //TODO: log it //TODO: check all Exceptions that can be thrown by the _sender instance. throw; } }; traceLogSource.Process(); } }
/// <summary> /// Turning on providers and creating files /// </summary> private static void DataCollection(string dataFileName) { Out.WriteLine("Collecting 10 seconds of kernel and CLR events to a file, and then printing."); Out.WriteLine(); Out.WriteLine("Start a .NET program while monitoring to see some events!"); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this program."); Debugger.Break(); return; } string kernelDataFileName = Path.ChangeExtension(dataFileName, ".kernel.etl"); // Create one user mode session and one kernel mode session Out.WriteLine("Creating two raw files, one with kernel events and one with clr events."); using (var userSession = new TraceEventSession("MonitorKernelAndClrEventsSession", dataFileName)) using (var kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, kernelDataFileName)) { // Set up Ctrl-C to stop both user mode and kernel mode sessions Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs cancelArgs) { Out.WriteLine("Insuring all ETW sessions are stopped."); kernelSession.Stop(true); // true means don't throw on error userSession.Stop(true); // true means don't throw on error // Since we don't cancel the Ctrl-C we will terminate the process as normal for Ctrl-C Out.WriteLine("OnCtrl C handler ending."); }; // Enable the events we care about for the kernel in the kernel session // For this instant the session will buffer any incoming events. kernelSession.EnableKernelProvider( KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread); // Enable the events we care about for the CLR (in the user session). // unlike the kernel session, you can call EnableProvider on other things too. // For this instant the session will buffer any incoming events. userSession.EnableProvider( ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.Default)); Out.WriteLine("Collecting data for 10 seconds (run a .Net program to generate events)."); Thread.Sleep(10000); Out.WriteLine("Stopping sessions"); }// Using clauses will insure that session are disposed (and thus stopped) before Main returns. Out.WriteLine("Merging the raw files into a single '{0}' file.", dataFileName); TraceEventSession.MergeInPlace(dataFileName, Out); Out.WriteLine("Merge complete."); }
public void Start() { if (kernelFlags != KernelTraceEventParser.Keywords.None) { traceSession.EnableKernelProvider(kernelFlags); } foreach (var handler in eventHandlers) { handler.SubscribeToSession(traceSession); } traceSession.Source.Process(); }
protected virtual void EnableKernelProviders(TraceEventSession session, bool isLoggingEnabled) { if (isLoggingEnabled) { // Traces cannot be symbolicated without the ImageLoad provider. session.EnableKernelProvider( KernelTraceEventParser.Keywords.Profile | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Thread, KernelTraceEventParser.Keywords.Profile); } }
protected override void Capture(TraceEventSession session) { session.EnableKernelProvider(Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser.Keywords.Process); session.Source.Kernel.ProcessStart += (ProcessTraceData data) => { Debug.WriteLine(data.Dump()); _sender.Send(data.ProcessID, "Process Started:" + data.CommandLine, PROCESS_CREATION); }; session.Source.Kernel.ProcessStop += (ProcessTraceData data) => { Debug.WriteLine(data.Dump()); _sender.Send(data.ProcessID, "Process Ended:" + data.CommandLine, PROCESS_END); }; }
static TraceEventSession CreateKernelTraceEventSession() { var kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName) { StopOnDispose = true }; kernelSession.EnableKernelProvider( KernelTraceEventParser.Keywords.FileIOInit | KernelTraceEventParser.Keywords.FileIO | KernelTraceEventParser.Keywords.NetworkTCPIP | KernelTraceEventParser.Keywords.AdvancedLocalProcedureCalls ); return(kernelSession); }
/// <summary> /// Turning on providers and creating the file /// </summary> static void DataCollection(string dataFileName) { Out.WriteLine("Collecting 10 seconds of kernel and CLR events to a file, and then printing."); Out.WriteLine(); Out.WriteLine("Start a .NET program while monitoring to see some events!"); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this program."); Debugger.Break(); return; } // Create one user mode session and one kernel mode session Out.WriteLine("Creating a file mode session"); using (var session = new TraceEventSession("MonitorKernelAndClrEventsSession", dataFileName)) { // Set up Ctrl-C to stop both user mode and kernel mode sessions Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs cancelArgs) { Out.WriteLine("Insuring all ETW sessions are stopped."); session.Stop(true); // true means don't throw on error // Since we don't cancel the Ctrl-C we will terminate the process as normal for Ctrl-C Out.WriteLine("OnCtrl C handler ending."); }; // Enable the events we care about for the kernel in the kernel session // For this instant the session will buffer any incoming events. // THis has to be first, and it will fail if you are not on Win8. session.EnableKernelProvider( KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread); // Enable the events we care about for the CLR (in the user session). // unlike the kernel session, you can call EnableProvider on other things too. // For this instant the session will buffer any incoming events. session.EnableProvider( ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.Default)); Out.WriteLine("Collecting data for 10 seconds (run a .Net program to generate events)."); Thread.Sleep(10000); Out.WriteLine("Stopping sessions"); } // Using clauses will insure that session are disposed (and thus stopped) before Main returns. }
public static void Listen() { var session = new TraceEventSession(KernelTraceEventParser.KernelSessionName); session.StopOnDispose = true; session.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process); session.Source.Kernel.ProcessStart += (ProcessTraceData data) => { ProcessCreated?.Invoke(data); }; session.Source.Kernel.ProcessStop += (ProcessTraceData data) => { ProcessExited?.Invoke(data); }; new Thread(() => session.Source.Process()).Start(); }
static void Main(string[] args) { var processes = Process.GetProcesses().Select(p => new ProcessInfo { Name = p.ProcessName, Id = p.Id }).ToDictionary(p => p.Id); using (var session = new TraceEventSession(Environment.OSVersion.Version.Build >= 9200 ? "MyKernelSession" : KernelTraceEventParser.KernelSessionName)) { session.EnableKernelProvider(KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.ImageLoad); var parser = session.Source.Kernel; parser.ProcessStart += e => { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{e.TimeStamp}.{e.TimeStamp.Millisecond:D3}: Process {e.ProcessID} ({e.ProcessName}) Created by {e.ParentID}: {e.CommandLine}"); processes.Add(e.ProcessID, new ProcessInfo { Id = e.ProcessID, Name = e.ProcessName }); }; parser.ProcessStop += e => { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"{e.TimeStamp}.{e.TimeStamp.Millisecond:D3}: Process {e.ProcessID} {TryGetProcessName(e)} Exited"); }; parser.ImageLoad += e => { Console.ForegroundColor = ConsoleColor.Yellow; var name = TryGetProcessName(e); Console.WriteLine($"{e.TimeStamp}.{e.TimeStamp.Millisecond:D3}: Image Loaded: {e.FileName} into process {e.ProcessID} ({name}) Size=0x{e.ImageSize:X}"); }; parser.ImageUnload += e => { Console.ForegroundColor = ConsoleColor.DarkYellow; var name = TryGetProcessName(e); Console.WriteLine($"{e.TimeStamp}.{e.TimeStamp.Millisecond:D3}: Image Unloaded: {e.FileName} from process {e.ProcessID} ({name})"); }; Task.Run(() => session.Source.Process()); Thread.Sleep(TimeSpan.FromSeconds(60)); } string TryGetProcessName(TraceEvent evt) { if (!string.IsNullOrEmpty(evt.ProcessName)) { return(evt.ProcessName); } return(processes.TryGetValue(evt.ProcessID, out var info) ? info.Name : string.Empty); } }
public static void Run() { var monitoringTimeSec = 10; Out.WriteLine("******************** ModuleLoadMonitor DEMO ********************"); Out.WriteLine("Monitoring DLL Loads and Process Starts/Stops system wide"); Out.WriteLine("The monitor will run for a maximum of {0} seconds", monitoringTimeSec); Out.WriteLine("Press Ctrl-C to stop monitoring early."); Out.WriteLine(); Out.WriteLine("Start a program to see some events!"); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this program."); Debugger.Break(); return; } // Start the session as a real time monitoring session, // Before windows 8, there is a restriction that if you wanted kernel events you must name your session // 'NT Kernel Logger' (the value of KernelSessionName) and there can only be one such session and no // other ETW providers can be enabled for that session (thus you need two sessions if you want both // kernel and non-kernel events (fixed in Win 8). We want this to work on Win 7 so we live with those // restrictions. using (TraceEventSession session = new TraceEventSession(KernelTraceEventParser.KernelSessionName)) { /* BY DEFAULT ETW SESSIONS SURVIVE THE DEATH OF THE PROESS THAT CREATES THEM! */ // Unlike most other resources on the system, ETW session live beyond the lifetime of the // process that created them. This is very useful in some scenarios, but also creates the // very real possibility of leaving 'orphan' sessions running. // // To help avoid this by default TraceEventSession sets 'StopOnDispose' so that it will stop // the ETW session if the TraceEventSession dies. Thus executions that 'clean up' the TraceEventSession // will clean up the ETW session. This covers many cases (including throwing exceptions) // // However if the process is killed manually (including control C) this cleanup will not happen. // Thus best practices include // // * Add a Control C handler that calls session.Dispose() so it gets cleaned up in this common case // * use the same session name run-to-run so you don't create many orphans. // // By default TraceEventSessions are in 'create' mode where it assumes you want to create a new session. // In this mode if a session already exists, it is stopped and the new one is created. // // Here we install the Control C handler. Console.CancelKeyPress += new ConsoleCancelEventHandler((object sender, ConsoleCancelEventArgs cancelArgs) => { Out.WriteLine("Control C pressed"); // Note that if you hit Ctrl-C twice rapidly you may be called concurrently. session.Dispose(); // Note that this causes Process() to return. cancelArgs.Cancel = true; // This says don't abort, since Process() will return we can terminate nicely. }); // Enable the Kernel events that we want. At this point data is being collected (but being buffered since we are not reading it) // See KernelTraceEventParser.Keywords for what else can be turned on and KernelTraceEventParser for a description // of the events that you get when you turn on the various kernel keywords. Many kernel events will also log a stack // when they fire see EnableKernelProvider for more on that. session.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process); // .Source will auto-create a TraceEventSource reading the data from the session // .Kernel will auto-create a KernelTraceEventParser getting its events from the source // .ImageLoad is an event that you can subscribe to that will be called back when Image load events happen (complete with parsed event) session.Source.Kernel.ImageLoad += delegate(ImageLoadTraceData data) { Out.WriteLine("Process {0,16} At 0x{1,8:x} Loaded {2}", data.ProcessName, data.ImageBase, data.FileName); }; // Subscribe to more events (process start) session.Source.Kernel.ProcessStart += delegate(ProcessTraceData data) { Out.WriteLine("Process Started {0,6} Parent {1,6} Name {2,8} Cmd: {3}", data.ProcessID, data.ParentID, data.ProcessName, data.CommandLine); }; // Subscribe to more events (process end) session.Source.Kernel.ProcessStop += delegate(ProcessTraceData data) { Out.WriteLine("Process Ending {0,6} ", data.ProcessID); }; // Set up a timer to stop processing after monitoringTimeSec var timer = new Timer(delegate(object state) { Out.WriteLine("Stopped after {0} sec", monitoringTimeSec); session.Source.StopProcessing(); }, null, monitoringTimeSec * 1000, Timeout.Infinite); // Start listening for events, will end if session.Source.StopProcessing() is called or session.Dispose() is called. // Here we never do either of these and thus will only stop when Ctrl-C is hit (but it will clean up because of // our control C handler). session.Source.Process(); timer.Dispose(); // Done with the timer. } Out.WriteLine("Stopping monitor"); }
public static void Run() { var monitoringTimeSec = 10; Out.WriteLine("******************** RealTimeTraceLog DEMO (win8) ********************"); Out.WriteLine("This program Shows how to use the real-time support in TraceLog"); Out.WriteLine("We do this by showing how to monitor exceptions in real time "); Out.WriteLine(); Out.WriteLine("This code depends on a Feature of Windows 8.1 (combined user and kernel sessions)"); Out.WriteLine(); Out.WriteLine("Note that this support is currently experimental and subject to change"); Out.WriteLine(); Out.WriteLine("Monitoring .NET Module load and Exception events (with stacks)."); Out.WriteLine("Run some managed code (ideally that has exceptions) while the monitor is running."); Out.WriteLine(); if (Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor < 62) { Out.WriteLine("This demo only works on Win8 / Win 2012 and above)"); return; } TraceEventSession session = null; // Set up Ctrl-C to stop both user mode and kernel mode sessions Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs cancelArgs) => { if (session != null) session.Dispose(); cancelArgs.Cancel = true; }; // Cause an exception to be thrown a few seconds in (so we have something interesting to look at) var exceptionGeneationTask = Task.Factory.StartNew(delegate { Thread.Sleep(3000); ThrowException(); }); Timer timer = null; // Create the new session to receive the events. // Because we are on Win 8 this single session can handle both kernel and non-kernel providers. using (session = new TraceEventSession("TraceLogSession")) { // Enable the events we care about for the kernel // For this instant the session will buffer any incoming events. // Enabling kernel events must be done before anything else. // This will fail on Win7. // // Note that if you turn on the KernelTraceEventParser.Keywords.Profile, you can also get stacks for CPU sampling // (every millisecond). (You can use the traceLogSource.Kernel.PerfInfoSample callback). Out.WriteLine("Enabling Image load, Process and Thread events. These are needed to look up native method names."); session.EnableKernelProvider( // KernelTraceEventParser.Keywords.Profile | // If you want CPU sampling events // KernelTraceEventParser.Keywords.ContextSwitch | // If you want context switch events // KernelTraceEventParser.Keywords.Thread | // If you want context switch events you also need thread start events. KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process, /****** The second parameter indicates which kernel events should have stacks *****/ // KernelTraceEventParser.Keywords.ImageLoad | // If you want Stacks image load (load library) events // KernelTraceEventParser.Keywords.Profile | // If you want Stacks for CPU sampling events // KernelTraceEventParser.Keywords.ContextSwitch | // If you want Stacks for context switch events KernelTraceEventParser.Keywords.None ); Out.WriteLine("Enabling CLR Exception and Load events (and stack for those events)"); // We are monitoring exception events (with stacks) and module load events (with stacks) session.EnableProvider( ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.Jit | // Turning on JIT events is necessary to resolve JIT compiled code ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | // This is needed if you want line number information in the stacks ClrTraceEventParser.Keywords.Loader | // You must include loader events as well to resolve JIT compiled code. ClrTraceEventParser.Keywords.Exception | // We want to see the exception events. ClrTraceEventParser.Keywords.Stack)); // And stacks on all CLR events where it makes sense. // The CLR events turned on above will let you resolve JIT compiled code as long as the JIT compilation // happens AFTER the session has started. To handle the case for JIT compiled code that was already // compiled we need to tell the CLR to dump 'Rundown' events for all existing JIT compiled code. We // do that here. Out.WriteLine("Enabling CLR Events to 'catch up' on JIT compiled code in running processes."); session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.Jit | // We need JIT events to be rundown to resolve method names ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | // This is needed if you want line number information in the stacks ClrTraceEventParser.Keywords.Loader | // As well as the module load events. ClrTraceEventParser.Keywords.StartEnumeration)); // This indicates to do the rundown now (at enable time) // Because we care about symbols in native code or NGEN images, we need a SymbolReader to decode them. // There is a lot of messages associated with looking up symbols, but we don't want to clutter up // The output by default, so we save it to an internal buffer you can ToString in debug code. // A real app should make this available somehow to the user, because sooner or later you DO need it. TextWriter SymbolLookupMessages = new StringWriter(); // TextWriter SymbolLookupMessages = Out; // If you want the symbol debug spew to go to the output, use this. // By default a symbol Reader uses whatever is in the _NT_SYMBOL_PATH variable. However you can override // if you wish by passing it to the SymbolReader constructor. Since we want this to work even if you // have not set an _NT_SYMBOL_PATH, so we add the Microsoft default symbol server path to be sure/ var symbolPath = new SymbolPath(SymbolPath.SymbolPathFromEnvironment).Add(SymbolPath.MicrosoftSymbolServerPath); SymbolReader symbolReader = new SymbolReader(SymbolLookupMessages, symbolPath.ToString()); Out.WriteLine("Open a real time TraceLog session (which understands how to decode stacks)."); using (TraceLogEventSource traceLogSource = TraceLog.CreateFromTraceEventSession(session)) { // We use this action in the particular callbacks below. Basically we pass in a symbol reader so we can decode the stack. // Often the symbol reader is a global variable instead. Action<TraceEvent> PrintEvent = ((TraceEvent data) => Print(data, symbolReader)); // We will print Exceptions and ModuleLoad events. (with stacks). traceLogSource.Clr.ExceptionStart += PrintEvent; traceLogSource.Clr.LoaderModuleLoad += PrintEvent; // traceLogSource.Clr.All += PrintEvent; // If you want to see stacks for various other kernel events, uncomment these (you also need to turn on the events above) traceLogSource.Kernel.PerfInfoSample += ((SampledProfileTraceData data) => Print(data, symbolReader)); // traceLogSource.Kernel.ImageLoad += ((ImageLoadTraceData data) => Print(data, symbolReader)); // process events until Ctrl-C is pressed or timeout expires Out.WriteLine("Waiting {0} sec for Events. Run managed code to see data. ", monitoringTimeSec); Out.WriteLine("Keep in mind there is a several second buffering delay"); // Set up a timer to stop processing after monitoringTimeSec timer = new Timer(delegate(object state) { Out.WriteLine("Stopped Monitoring after {0} sec", monitoringTimeSec); if (session != null) session.Dispose(); session = null; }, null, monitoringTimeSec * 1000, Timeout.Infinite); traceLogSource.Process(); } } Out.WriteLine("Finished"); if (timer != null) timer.Dispose(); // Turn off the timer. }
public static void Run() { var monitoringTimeSec = 10; Out.WriteLine("******************** KernelAndClrMonitor DEMO (Win7) ********************"); Out.WriteLine("Printing both Kernel and CLR (user mode) events simultaneously"); Out.WriteLine("The monitor will run for a maximum of {0} seconds", monitoringTimeSec); Out.WriteLine("Press Ctrl-C to stop monitoring early."); Out.WriteLine(); Out.WriteLine("Start a .NET program to see some events!"); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this program."); Debugger.Break(); return; } // Set up Ctrl-C to stop both user mode and kernel mode sessions Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs cancelArgs) => { StopSessions(); cancelArgs.Cancel = true; }; // Note that because there are different sessions, the events may not be printed // in time order since the different sessions may flush their buffers at different // times. If you care, you must buffer the events and order them by event // timestamp. Note that the timestamps however ARE accurate. Out.WriteLine("Setup up threads to process the events"); // start processing kernel events on a thread pool thread var task1 = Task.Run(() => { // Note that TraceEventSession and EtwTraceEventParser are IN GENERAL NOT THREADSAFE, // Normally this is not a hardship you just set up the session TraceDispacher on one // thread. It is OK to call a session's Dispose() and 'Enable and Disable provider APIS // from another thread, but things associated with ETWTraceEventSource and TraceEventParsers // should all be on the same thread. Out.WriteLine("Kernel event Thread Starting"); Out.WriteLine("Enabling Image load, thread and process kernel events."); using (s_kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName)) { // Enable the events we care about for the kernel in the kernel session // For this instant the session will buffer any incoming events. // If you only have to run on Win8 systems you can use one session for both. // Here we turn in process, thread and Image load events. s_kernelSession.EnableKernelProvider( KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread); // You should do all processing from a single source on a single thread. // Thus call calls to .Source, or Process() should be on the same thread. s_kernelSession.Source.Kernel.All += Print; #if DEBUG // in debug builds it is useful to see any unhandled events because they could be bugs. s_kernelSession.Source.UnhandledEvents += Print; #endif // process events until Ctrl-C is pressed Out.WriteLine("Waiting on kernel events."); s_kernelSession.Source.Process(); } Out.WriteLine("Thread 1 dieing"); }); // start processing CLR events on a thread pool thread var task2 = Task.Run(() => { // Note that TraceEventSession and EtwTraceEventParser are IN GENERAL NOT THREADSAFE, // Normally this is not a hardship you just set up the session TraceDispacher on one // thread. It is OK to call a session's Dispose() and 'Enable and Disable provider APIS // from another thread, but things associated with ETWTraceEventSource and TraceEventParsers // should all be on the same thread. using (s_userSession = new TraceEventSession("MonitorKernelAndClrEventsSession")) { Out.WriteLine("Enabling CLR GC and Exception events."); // Enable the events we care about for the CLR (in the user session). // unlike the kernel session, you can call EnableProvider on other things too. // For this instant the ;session will buffer any incoming events. s_userSession.EnableProvider( ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Exception)); // s_userSession.Source.Clr.GCHeapStats += (GCHeapStatsTraceData data) => Out.WriteLine(" ", data.GenerationSize0); Out.WriteLine("User event Thread Starting"); s_userSession.Source.Clr.All += Print; #if DEBUG // in debug builds it is useful to see any unhandled events because they could be bugs. s_userSession.Source.UnhandledEvents += Print; #endif // process events until Ctrl-C is pressed or timeout expires Out.WriteLine("Waiting on user events."); s_userSession.Source.Process(); } Out.WriteLine("Thread 2 dieing"); }); // Set up a timer to stop processing after monitoringTimeSec var timer = new Timer(delegate(object state) { Out.WriteLine("Stopped Monitoring after {0} sec", monitoringTimeSec); StopSessions(); }, null, monitoringTimeSec * 1000, Timeout.Infinite); // Wait until tasks are complete Out.WriteLine("Waiting for processing tasks to complete"); Task.WaitAll(task1, task2); Out.WriteLine("Monitoring stopped"); timer.Dispose(); }
public static void Run() { var monitoringTimeSec = 10; if (Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor < 62) { Out.WriteLine("This demo only works on Win8 / Win 2012 an above)"); return; } Out.WriteLine("******************** KernelAndClrMonitor DEMO (Win 8) ********************"); Out.WriteLine("Printing both Kernel and CLR (user mode) events simultaneously"); Out.WriteLine("The monitor will run for a maximum of {0} seconds", monitoringTimeSec); Out.WriteLine("Press Ctrl-C to stop monitoring early."); Out.WriteLine(); Out.WriteLine("Start a .NET program to see some events!"); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this program."); Debugger.Break(); return; } TraceEventSession session = null; // Set up Ctrl-C to stop both user mode and kernel mode sessions Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs cancelArgs) => { if (session != null) session.Dispose(); cancelArgs.Cancel = true; }; // Set up a timer to stop processing after monitoringTimeSec var timer = new Timer(delegate(object state) { Out.WriteLine("Stopped Monitoring after {0} sec", monitoringTimeSec); if (session != null) session.Dispose(); }, null, monitoringTimeSec * 1000, Timeout.Infinite); // Create the new session to receive the events. // Because we are on Win 8 this single session can handle both kernel and non-kernel providers. using (session = new TraceEventSession("MonitorKernelAndClrEventsSession")) { // Enable the events we care about for the kernel // For this instant the session will buffer any incoming events. // Enabling kernel events must be done before anything else. // This will fail on Win7. Out.WriteLine("Enabling Image load, Process and Thread events."); session.EnableKernelProvider( KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread); // Subscribe the events of interest. In this case we just print all events. session.Source.Kernel.All += Print; Out.WriteLine("Enabling CLR GC and Exception events."); // Enable the events we care about for the CLR (in the user session). // unlike the kernel session, you can call EnableProvider on other things too. // For this instant the ;session will buffer any incoming events. session.EnableProvider( ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)(ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Exception)); session.Source.Clr.All += Print; #if DEBUG // in debug builds it is useful to see any unhandled events because they could be bugs. session.Source.UnhandledEvents += Print; #endif // process events until Ctrl-C is pressed or timeout expires Out.WriteLine("Waiting for Events."); session.Source.Process(); } timer.Dispose(); // Turn off the timer. }
public static void Run() { int monitoringTimeSec = 10; if (Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor < 62) { Out.WriteLine("This demo only works on Win8 / Win 2012 an above)"); return; } // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); Debugger.Break(); return; } string outputFileName = "ReloggerMonitorOutput.etl"; if (File.Exists(outputFileName)) File.Delete(outputFileName); Out.WriteLine("******************** Simple Relogger DEMO ********************"); Out.WriteLine("This program shows how you can monitor an ETW stream in real time."); Out.WriteLine("And conditionally pass the events on to a ETL file"); Out.WriteLine("Ctrl-C will end earlier"); Out.WriteLine(); Out.WriteLine("Please run some managed code while collection is happening..."); Out.WriteLine(); // To listen to ETW events you need a session, which allows you to control which events will be produced // Note that it is the session and not the source that buffers events, and by default sessions will buffer // 64MB of events before dropping events. Thus even if you don't immediately connect up the source and // read the events you should not lose them. // // As mentioned below, sessions can outlive the process that created them. Thus you may need a way of // naming the session so that you can 'reconnect' to it from another process. This is what the name // is for. It can be anything, but it should be descriptive and unique. If you expect multiple versions // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix) // however this is dangerous because you can leave data collection on if the program ends unexpectedly. var sessionName = "SimpleMontitorSession"; Out.WriteLine("Creating a '{0}' session", sessionName); using (var session = new TraceEventSession(sessionName)) { // Enable the events we care about for the kernel in the kernel session // For this instant the session will buffer any incoming events. // This has to be first, and it will fail if you are not on Win8. session.EnableKernelProvider( KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread); // A relogger is a TraceEventSource and acts much like an ETWTraceEventSource, with extra Write APIS. // Thus you get a callback on any event you want. // Only things that you call 'WriteEvent' on will end up in the output file. var relogger = new ETWReloggerTraceEventSource(sessionName, TraceEventSourceType.Session, outputFileName); // Here we set up the callbacks we want in the output file. In this case all GC allocation Tick // events for 'String' as well as any ExceptionStart events. relogger.Clr.GCAllocationTick += delegate(GCAllocationTickTraceData data) { if (data.TypeName == "System.String") relogger.WriteEvent(data); }; relogger.Clr.ExceptionStart += delegate(ExceptionTraceData data) { relogger.WriteEvent(data); }; // We also keep the image load events for DLL with 'clr' in their name. relogger.Kernel.ImageGroup += delegate(ImageLoadTraceData data) { if (0 <= data.FileName.IndexOf("clr", StringComparison.OrdinalIgnoreCase)) relogger.WriteEvent(data); }; #if false // Turn on to get debugging on unhandled events. relogger.UnhandledEvents += delegate(TraceEvent data) { Console.WriteLine("Unknown Event " + data); }; #endif // Allow the test to be terminated with Ctrl-C cleanly. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); }; // Set up a timer to stop processing after monitoringTimeSec var timer = new Timer(delegate(object state) { Out.WriteLine("Stopped after {0} sec", monitoringTimeSec); session.Dispose(); }, null, monitoringTimeSec * 1000, Timeout.Infinite); // Turn on the events to the provider. In this case most CLR events Out.WriteLine("**** Turn on CLR Etw Providers. Run managed code to see events."); session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.Default); // go into a loop processing events can calling the callbacks. Because this is live data (not from a file) // processing never completes by itself, but only because someone called 'source.Dispose()'. Out.WriteLine("**** Start listening for events from the Microsoft-Demos-SimpleMonitor provider."); Out.WriteLine("The monitor will run for a maximum of {0} seconds. Run managed code for more output.", monitoringTimeSec); relogger.Process(); Out.WriteLine(); Out.WriteLine("Stopping the collection of events."); timer.Dispose(); // Turn off the timer. } Out.WriteLine("Monitoring complete, only certain CLR events put in the output file."); Out.WriteLine("The output ETL file: {0}", Path.GetFullPath(outputFileName)); Out.WriteLine(); if (!File.Exists(outputFileName)) { Out.WriteLine("Error: No output file was generated (did you run anything during the collection?"); return; } // Show what was actually produced in the filtered file. DataProcessing(outputFileName); }
/// <summary> /// CollectData doe will turn on logging of data from 'eventSourceName' to the file 'dataFileName'. /// It will then call EventGenerator.CreateEvents and wait 12 seconds for it to generate some data. /// </summary> static void CollectData(string eventSourceName, string dataFileName) { // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); Debugger.Break(); return; } // As mentioned below, sessions can outlive the process that created them. Thus you need a way of // naming the session so that you can 'reconnect' to it from another process. This is what the name // is for. It can be anything, but it should be descriptive and unique. If you expect multiple versions // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix) // however this is dangerous because you can leave data collection on if the program ends unexpectedly. // // In this case we tell the session to place the data in MonitorToFileData.etl. var sessionName = "SimpleTraceLogSession"; Out.WriteLine("Creating a '{0}' session writing to {1}", sessionName, dataFileName); Out.WriteLine("Use 'logman query -ets' to see active sessions."); Out.WriteLine("Use 'logman stop {0} -ets' to manually stop orphans.", sessionName); using (var session = new TraceEventSession(sessionName, dataFileName)) // Since we give it a file name, the data goes there. using (var kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, Path.ChangeExtension(dataFileName, ".kernel.etl"))) { /* BY DEFAULT ETW SESSIONS SURVIVE THE DEATH OF THE PROESS THAT CREATES THEM! */ // Unlike most other resources on the system, ETW session live beyond the lifetime of the // process that created them. This is very useful in some scenarios, but also creates the // very real possibility of leaving 'orphan' sessions running. // // To help avoid this by default TraceEventSession sets 'StopOnDispose' so that it will stop // the ETW session if the TraceEventSession dies. Thus executions that 'clean up' the TraceEventSession // will clean up the ETW session. This covers many cases (including throwing exceptions) // // However if the process is killed manually (including control C) this cleanup will not happen. // Thus best practices include // // * Add a Control C handler that calls session.Dispose() so it gets cleaned up in this common case // * use the same session name run-to-run so you don't create many orphans. // // By default TraceEventSessions are in 'create' mode where it assumes you want to create a new session. // In this mode if a session already exists, it is stopped and the new one is created. // // Here we install the Control C handler. It is OK if Dispose is called more than once. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); kernelSession.Dispose(); }; // Enable kernel events. kernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread); // Enable my provider, you can call many of these on the same session to get events from other providers // Turn on the eventSource given its name. // Note we turn on Verbose level all keywords (ulong.MaxValue == 0xFFF....) and turn on stacks for // this provider (for all events, until Windows 8.1 you can only turn on stacks for every event // for a particular provider or no stacks) var options = new TraceEventProviderOptions() { StacksEnabled = true }; var restarted = session.EnableProvider(eventSourceName, TraceEventLevel.Verbose, ulong.MaxValue, options); if (restarted) // Generally you don't bother with this warning, but for the demo we do. Out.WriteLine("The session {0} was already active, it has been restarted.", sessionName); // We also turn on CLR events because we need them to decode Stacks and we also get exception events (and their stacks) session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.Default); // Start another thread that Causes MyEventSource to create some events // Normally this code as well as the EventSource itself would be in a different process. EventGenerator.CreateEvents(); // Also generate some exceptions so we have interesting stacks to look at Thread.Sleep(100); EventGenerator.GenerateExceptions(); Out.WriteLine("Waiting 12 seconds for events to come in."); Thread.Sleep(12000); // Because the process in question (this process) lives both before and after the time the events were // collected, we don't have complete information about JIT compiled methods in that method. There are // some methods that were JIT compiled before the session started (e.g. SimpleTraceLog.Main) for which // we do not have information. We collect this by forcing a CLR 'rundown' which will dump method information // for JIT compiled methods that were not present. If you know that the process of interest ends before // data collection ended or that data collection started before the process started, then this is not needed. Out.WriteLine("Forcing rundown of JIT methods."); var rundownFileName = Path.ChangeExtension(dataFileName, ".clrRundown.etl"); using (var rundownSession = new TraceEventSession(sessionName + "Rundown", rundownFileName)) { rundownSession.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrRundownTraceEventParser.Keywords.Default); // Poll until 2 second goes by without growth. for (var prevLength = new FileInfo(rundownFileName).Length; ; ) { Thread.Sleep(2000); var newLength = new FileInfo(rundownFileName).Length; if (newLength == prevLength) break; prevLength = newLength; } } Out.WriteLine("Done with rundown."); } Out.WriteLine("Merging the raw files into a single '{0}' file.", dataFileName); TraceEventSession.MergeInPlace(dataFileName, Out); Out.WriteLine("Merge complete."); }