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> /// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to /// the MyEventSource above. Normally this event source would be in a different process, but /// it also works if this process generate the events and I do that here for simplicity. /// </summary> public static int Run() { // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); Debugger.Break(); return(-1); } // 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"; using (var session = new TraceEventSession(sessionName)) { /* 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 (say your program 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(); }; /*****************************************************************************************************/ // Hook up events. To so this first we need a 'Parser. which knows how to part the events of a particular Event Provider. // In this case we get a DynamicTraceEventSource, which knows how to parse any EventSource provider. This parser // is so common, that TraceEventSource as a shortcut property called 'Dynamic' that fetches this parsers. // For debugging, and demo purposes, hook up a callback for every event that 'Dynamic' knows about (this is not EVERY // event only those know about by RegisteredTraceEventParser). However the 'UnhandledEvents' handler below will catch // the other ones. Dictionary <int, DownloadSession> data = new Dictionary <int, DownloadSession>(); session.Source.Dynamic.All += delegate(TraceEvent e) { // ETW buffers events and only delivers them after buffering up for some amount of time. Thus // there is a small delay of about 2-4 seconds between the timestamp on the event (which is very // accurate), and the time we actually get the event. We measure that delay here. int sizeIndex = e.PayloadIndex("size"); if (sizeIndex >= 0) { try { int pidIndex = e.PayloadIndex("PID"); int pid = (int)e.PayloadValue(pidIndex); int bytes = (int)e.PayloadValue(sizeIndex); lock (data) { if (data.TryGetValue(pid, out DownloadSession info)) { info.AddTotal(bytes); } else { data[pid] = new DownloadSession(pid, (int)e.PayloadValue(sizeIndex)); } } } catch { } } }; bool running = true; Task.Run(async() => { while (running) { try { await Task.Delay(1000); lock (data) { Console.Clear(); Console.SetCursorPosition(0, 0); Console.WriteLine("{0,-6} {1,11} {2,11} {3,-30}", "PID", "Total Bytes", "Bytes/sec", "Name"); foreach (var datum in data.OrderByDescending(x => x.Value.Difference).Take(5)) { Console.WriteLine("{0,-6} {1,11} {2,11} {3,-30}", datum.Value.PID, DownloadSession.FormatBytes(datum.Value.Total), DownloadSession.FormatBytes(datum.Value.Difference, 1, "/s"), datum.Value.ProcessName); datum.Value.Update(); } } } catch { } } }); // At this point we have created a TraceEventSession, hooked it up to a TraceEventSource, and hooked the // TraceEventSource to a TraceEventParser (you can do several of these), and then hooked up callbacks // up to the TraceEventParser (again you can have several). However we have NOT actually told any // provider (EventSources) to actually send any events to our TraceEventSession. // We do that now. // Enable my provider, you can call many of these on the same session to get events from other providers. // Because this EventSource did not define any keywords, I can only turn on all events or none. var restarted = session.EnableProvider("Microsoft-Windows-Kernel-Network"); if (restarted) // Generally you don't bother with this warning, but for the demo we do. { Console.WriteLine("The session {0} was already active, it has been restarted.", sessionName); } Console.Clear(); Console.SetCursorPosition(0, 0); Console.WriteLine("{0,-6} {1,11} {2,11} {3,-30}", "PID", "Total Bytes", "Bytes/sec", "Name"); // 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()'. session.Source.Process(); running = false; Console.WriteLine(); Console.WriteLine("Stopping the collection of events."); Console.ReadLine(); } return(0); }
/// <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("Zipping the raw files into a single '{0}' file.", dataFileName); // At this point you have multiple ETL files that don't have all the information // inside them necessary for analysis off the currentn machine. To do analsysis // of the machine you need to merge the ETL files (which can be done with // TraceEventSession.MergeInPlace(dataFileName, Out); // However this does not get the symbolic information (NGEN PDBS) needed to // decode the stacks in the .NET managed framewrok on another machine. // To do the merging AND generate these NGEN images it is best to us ethe // ZipppedETLWriter that does all this (and compresses all the files in a ZIP archive). ZippedETLWriter writer = new ZippedETLWriter(dataFileName, Out); writer.WriteArchive(); Out.WriteLine("Zip complete, output file = {0}", writer.ZipArchivePath); }
static void DoMain(string[] args) { if (TraceEventSession.IsElevated() != true) { Console.Error.WriteLine("Must be elevated (Admin) to run this program."); return; } List <string> procargs = null; bool showhelp = false, spawnNewConsoleWindow = false; TraceOutputOptions options = TraceOutputOptions.TracesAndSummary; int pid = 0; var p = new OptionSet { { "newconsole", "Start the process in a new console window.", v => { spawnNewConsoleWindow = v != null; } }, { "summary", "Prints only a summary of the collected trace.", v => { if (v != null) { options = TraceOutputOptions.OnlySummary; } } }, { "nosummary", "Prints only ETW events - no summary at the end.", v => { if (v != null) { options = TraceOutputOptions.NoSummary; } } }, { "h|help", "Show this message and exit", v => showhelp = v != null }, { "?", "Show this message and exit", v => showhelp = v != null } }; try { procargs = p.Parse(args); } catch (OptionException ex) { Console.Error.Write("ERROR: invalid argument"); Console.Error.WriteLine(ex.Message); Console.Error.WriteLine(); showhelp = true; } catch (FormatException) { Console.Error.WriteLine("ERROR: invalid number in one of the constraints"); Console.Error.WriteLine(); showhelp = true; } Debug.Assert(procargs != null); if (!showhelp && procargs.Count == 0) { Console.Error.WriteLine("ERROR: please provide either process name or PID of the already running process"); Console.Error.WriteLine(); showhelp = true; } if (showhelp) { ShowHelp(p); return; } try { if (!int.TryParse(procargs[0], out pid)) { TraceNewProcess(procargs, spawnNewConsoleWindow, options); } else { TraceRunningProcess(pid, options); } } catch (COMException ex) { if ((uint)ex.HResult == 0x800700B7) { Console.Error.WriteLine("ERROR: could not start the kernel logger - make sure it is not running."); } } catch (Win32Exception ex) { Console.Error.WriteLine( $"ERROR: an error occurred while trying to start or open the process, hr: 0x{ex.HResult:X8}, " + $"code: 0x{ex.NativeErrorCode:X8} ({ex.Message})."); } catch (Exception ex) { Console.Error.WriteLine($"ERROR: severe error happened when starting application: {ex.Message}"); } }
/// <summary> /// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to /// the MyEventSource above. Normally this event source would be in a different process, but /// it also works if this process generate the events and I do that here for simplicity. /// </summary> public static int Run() { Out.WriteLine("******************** SimpleEventSourceMonitor DEMO ********************"); Out.WriteLine("This program generates processes and displays EventSource events"); Out.WriteLine("using the ETW REAL TIME pipeline. (thus no files are created)"); Out.WriteLine(); // 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(-1); } // 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 = "SimpleMonitorSession"; Out.WriteLine("Creating a '{0}' session", sessionName); 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)) { /* BY DEFAULT ETW SESSIONS SURVIVE THE DEATH OF THE PROCESS 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 (say your program 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(); }; // To demonstrate non-trivial event manipulation, we calculate the time delta between 'MyFirstEvent and 'MySecondEvent' // firstEventTimeMSec remembers all the 'MyFirstEvent' arrival times (indexed by their ID) var firstEventTimeMSec = new Dictionary <int, double>(); /*****************************************************************************************************/ // Hook up events. To so this first we need a 'Parser. which knows how to part the events of a particular Event Provider. // In this case we get a DynamicTraceEventSource, which knows how to parse any EventSource provider. This parser // is so common, that TraceEventSource as a shortcut property called 'Dynamic' that fetches this parsers. // For debugging, and demo purposes, hook up a callback for every event that 'Dynamic' knows about (this is not EVERY // event only those know about by DynamicTraceEventParser). However the 'UnhandledEvents' handler below will catch // the other ones. session.Source.Dynamic.All += delegate(TraceEvent data) { // ETW buffers events and only delivers them after buffering up for some amount of time. Thus // there is a small delay of about 2-4 seconds between the timestamp on the event (which is very // accurate), and the time we actually get the event. We measure that delay here. var delay = (DateTime.Now - data.TimeStamp).TotalSeconds; Out.WriteLine("GOT Event Delay={0:f1}sec: {1} ", delay, data.ToString()); }; // Add logic on what to do when we get "MyFirstEvent" session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-Demos-SimpleMonitor", "MyFirstEvent", delegate(TraceEvent data) { // On First Events, simply remember the ID and time of the event firstEventTimeMSec[(int)data.PayloadByName("MyId")] = data.TimeStampRelativeMSec; }); // Add logic on what to do when we get "MySecondEvent" session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-Demos-SimpleMonitor", "MySecondEvent", delegate(TraceEvent data) { // On Second Events, if the ID matches, compute the delta and display it. var myID = (int)data.PayloadByName("MyId"); double firstEventTime; if (firstEventTimeMSec.TryGetValue(myID, out firstEventTime)) { firstEventTimeMSec.Remove(myID); // We are done with the ID after matching it, so remove it from the table. Out.WriteLine(" >>> Time Delta from first Event = {0:f3} MSec", data.TimeStampRelativeMSec - firstEventTime); } else { Out.WriteLine(" >>> WARNING, Found a 'SecondEvent' without a corresponding 'FirstEvent'"); } }); // Add logic on what to do when we get "Stop" session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-Demos-SimpleMonitor", "Stop", delegate(TraceEvent data) { Out.WriteLine(" >>> Got a stop message"); // Stop processing after we we see the 'Stop' event, this will 'Process() to return. It is OK to call Dispose twice session.Source.Dispose(); }); #if DEBUG // The callback above will only be called for events the parser recognizes (in the case of DynamicTraceEventParser, EventSources) // It is sometimes useful to see the other events that are not otherwise being handled. The source knows about these and you // can ask the source to send them to you like this. session.Source.UnhandledEvents += delegate(TraceEvent data) { if ((int)data.ID != 0xFFFE) // The EventSource manifest events show up as unhanded, filter them out. { Out.WriteLine("GOT UNHANDLED EVENT: " + data.Dump()); } }; #endif // At this point we have created a TraceEventSession, hooked it up to a TraceEventSource, and hooked the // TraceEventSource to a TraceEventParser (you can do several of these), and then hooked up callbacks // up to the TraceEventParser (again you can have several). However we have NOT actually told any // provider (EventSources) to actually send any events to our TraceEventSession. // We do that now. // Enable my provider, you can call many of these on the same session to get events from other providers. // Because this EventSource did not define any keywords, I can only turn on all events or none. var restarted = session.EnableProvider("Microsoft-Demos-SimpleMonitor"); 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); } // 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(); Out.WriteLine("**** Start listening for events from the Microsoft-Demos-SimpleMonitor provider."); // 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()'. session.Source.Process(); Out.WriteLine(); Out.WriteLine("Stopping the collection of events."); } return(0); }
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"); }
/// <summary> /// Sample function demonstrating how to match pairs of events in a live ETW stream, /// and compute a duration based on the start and end events. It uses CLR's JIT events /// MethodJittingStarted, ModuleLoadUnload, and ModuleLoadUnloadVerbose. /// for this but the principle applies to most events that mark a duration. /// </summary> public static void Run() { Out.WriteLine("******************** ObserveJitEvents DEMO ********************"); Out.WriteLine("This program Demos using the reactive framework (IObservable) to monitor"); Out.WriteLine(".NET Runtime JIT compiler events."); Out.WriteLine(); Out.WriteLine("This program shows how you can use the reactive framework to find pairs"); Out.WriteLine("of related events (in this the JIT start and stop events) and use them"); Out.WriteLine("to calculate values (in this case the time spent JIT compiling. "); Out.WriteLine(); Out.WriteLine("The program also shows how to create on the fly aggregate statistics using"); Out.WriteLine("the reactive framework. "); Out.WriteLine(); Out.WriteLine("The program will print a line every time a .NET method is JIT compiled"); Out.WriteLine("in any process on the machine and will print stats every 8 methods."); Out.WriteLine(); Out.WriteLine("Start a .NET Program while the monitoring is active to see the JIT events."); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this method."); Debugger.Break(); return; } var monitoringTimeSec = 10; Out.WriteLine("The monitor will run for a maximum of {0} seconds", monitoringTimeSec); Out.WriteLine("Press Ctrl-C to stop monitoring early."); // create a real time user mode session using (var userSession = new TraceEventSession("ObserveJitEvents1")) { // Set up Ctrl-C to stop both user mode and kernel mode sessions SetupCtrlCHandler(() => { if (userSession != null) { userSession.Stop(); } }); // enable the CLR JIT compiler events. userSession.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)(ClrTraceEventParser.Keywords.Default)); // Get the stream of starts. IObservable <MethodJittingStartedTraceData> jitStartStream = userSession.Source.Clr.Observe <MethodJittingStartedTraceData>("Method/JittingStarted"); // And the stream of ends. IObservable <MethodLoadUnloadVerboseTraceData> jitEndStream = userSession.Source.Clr.Observe <MethodLoadUnloadVerboseTraceData>("Method/LoadVerbose"); // Compute the stream of matched-up pairs, and for each create a tuple of the start event and the time between the pair of events. // Note that the 'Take(1)' is pretty important because a nested 'from' statement logically creates the 'cross product' of a two streams // In this case the stream of starts and the stream of ends). Because we filter this stream only to matching entities and then only // take the first entry, we stop waiting. Thus we only 'remember' those 'starts' that are not yet matched, which is very important // for efficiency. Note that any 'lost' end events will never be matched and will accumulate over time, slowing things down. // We should put a time window on it as well to 'forget' old start events. var jitTimes = from start in jitStartStream from end in jitEndStream.Where(e => start.MethodID == e.MethodID && start.ProcessID == e.ProcessID).Take(1) select new { Name = GetName(start), ProcessID = start.ProcessID, JitTIme = end.TimeStampRelativeMSec - start.TimeStampRelativeMSec }; // Create a stream of just the JIT times and compute statistics every 8 methods that are JIT compiled. IObservable <Statistics> jitStats = ComputeRunningStats(jitTimes, jitData => jitData.JitTIme, windowSize: 8); // Print every time you compile a method jitTimes.Subscribe(onNext: jitData => Out.WriteLine("JIT_TIME: {0,7:f2} PROC: {1,10} METHOD: {2}", jitData.JitTIme, GetProcessName(jitData.ProcessID), jitData.Name)); // Also output the statistics. jitStats.Subscribe(onNext: Out.WriteLine); // print some aggregation stats // for debugging purposes to see any events that entered by were not handled by any parser. These can be bugs. // IObservable<TraceEvent> unhandledEventStream = userSession.Source.ObserveUnhandled(); // unhandledEventStream.Subscribe(onNext: ev => Out.WriteLine("UNHANDLED : PID: {0,5} {1}/{2} ", ev.ProcessID, ev.ProviderName, ev.EventName)); // Set up a timer to stop processing after monitoringTimeSec IObservable <long> timer = Observable.Timer(new TimeSpan(0, 0, monitoringTimeSec)); timer.Subscribe(delegate { Out.WriteLine("Stopped after {0} sec", monitoringTimeSec); userSession.Stop(); }); // OK we are all set up, time to listen for events and pass them to the observers. userSession.Source.Process(); } }
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 bool RealTimeTraceEventSession ( string providerName , string sessionName , string tracingFileName = null , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { var r = false; if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return(r); } var needTracingFile = !string.IsNullOrEmpty(tracingFileName); if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { using ( var session = ( needTracingFile ? new TraceEventSession ( sessionName , tracingFileName , traceEventSessionOptions ) { StopOnDispose = true } : new TraceEventSession ( sessionName , traceEventSessionOptions ) { StopOnDispose = true } ) ) { using ( var source = ( needTracingFile ? new ETWTraceEventSource(tracingFileName) : session.Source ) ) { long sequence = 0; RegisterCallbacks ( providerName , traceEvents , source , session , (x, y, z) => { long id = 0; if (needCountHits) { id = Interlocked.Increment(ref sequence); } onOneEventTracedOnceProcessAction ( id , x , y , z ); } ); var restarted = session .EnableProvider ( providerName , traceEventLevel , matchKeywords , traceEventProviderOptions ); source .Process(); r = true; } } } return(r); }
public static void StartTrace(CollectorParameters Collector) { void RetargetEventSource(String LegacySource) { // This is a fix for: https://github.com/fireeye/SilkETW/issues/4 // When both SilkETW and SilkService are used on the same host // eventlog logging would fail for one or the other as they had // the same source. This function will retarget the source. if (EventLog.SourceExists(LegacySource)) { EventLog.DeleteEventSource(LegacySource); } } Boolean WriteEventLogEntry(String Message, EventLogEntryType Type, EventIds EventId, String Path) { //--[Event ID's] // 0 == Collector start // 1 == Collector terminated -> by user // 2 == Collector terminated -> by error // 3 == Event recorded //-- try { // Fix legacy collector source RetargetEventSource("ETW Collector"); // Event log properties String Source = "SilkService Collector"; // If the source doesn't exist we have to create it first if (!EventLog.SourceExists(Source)) { EventLog.CreateEventSource(Source, Path); } // Write event using (EventLog Log = new EventLog(Path)) { Log.Source = Source; Log.MaximumKilobytes = 99968; // Max ~100mb size -> needs 64kb increments Log.ModifyOverflowPolicy(OverflowAction.OverwriteAsNeeded, 10); // Always overwrite oldest Log.WriteEntry(Message, Type, (int)EventId); } return(true); } catch { return(false); } } int ProcessJSONEventData(String JSONData, OutputType OutputType, String Path, String YaraScan, YaraOptions YaraOptions, YSInstance YaraInstance, YSRules YaraRules) { // Yara matches List <String> YaraRuleMatches = new List <String>(); // Yara options if (YaraScan != String.Empty) { byte[] JSONByteArray = Encoding.ASCII.GetBytes(JSONData); List <YSMatches> Matches = YaraInstance.ScanMemory(JSONByteArray, YaraRules, null, 0); YaraRuleMatches.Clear(); if (Matches.Count != 0) { foreach (YSMatches Match in Matches) { YaraRuleMatches.Add(Match.Rule.Identifier); } // Dynamically update the JSON object -> List<String> YaraRuleMatches JObject obj = JObject.Parse(JSONData); ((JArray)obj["YaraMatch"]).Add(YaraRuleMatches); JSONData = obj.ToString(Newtonsoft.Json.Formatting.None); } } if (YaraOptions == YaraOptions.All || YaraOptions == YaraOptions.None || (YaraScan != String.Empty && YaraRuleMatches.Count > 0)) { //--[Return Codes] // 0 == OK // 1 == File write failed // 2 == URL POST request failed // 3 == Eventlog write failed //-- // Process JSON if (OutputType == OutputType.file) { try { if (!File.Exists(Path)) { File.WriteAllText(Path, (JSONData + Environment.NewLine)); } else { File.AppendAllText(Path, (JSONData + Environment.NewLine)); } return(0); } catch { return(1); } } else if (OutputType == OutputType.url) { try { string responseFromServer = string.Empty; HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(Path); webRequest.Timeout = 10000; // 10 second timeout webRequest.Method = "POST"; webRequest.ContentType = "application/json"; webRequest.Accept = "application/json"; using (var streamWriter = new StreamWriter(webRequest.GetRequestStream())) { streamWriter.Write(JSONData); streamWriter.Flush(); streamWriter.Close(); } var httpResponse = (HttpWebResponse)webRequest.GetResponse(); using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) { var result = streamReader.ReadToEnd(); } return(0); } catch { return(2); } } else { Boolean WriteEvent = WriteEventLogEntry(JSONData, EventLogEntryType.Information, EventIds.Event, Path); if (WriteEvent) { return(0); } else { return(3); } } } else { return(0); } } // Local variables for StartTrace String EventParseSessionName; Boolean ProcessEventData; // Is elevated? While running as a service this should always be true but // this is kept for edge-case user-fail. if (TraceEventSession.IsElevated() != true) { SilkUtility.WriteCollectorGuidMessageToServiceTextLog(Collector.CollectorGUID, "The collector must be run elevated", true); return; } // Print status SilkUtility.WriteCollectorGuidMessageToServiceTextLog(Collector.CollectorGUID, "Starting trace collector", false); // We tag event sessions with a unique name // While running these are observable with => logman -ets if (Collector.CollectorType == CollectorType.Kernel) { EventParseSessionName = KernelTraceEventParser.KernelSessionName; } else { String RandId = Guid.NewGuid().ToString(); EventParseSessionName = ("SilkServiceUserCollector_" + RandId); } // Create trace session using (var TraceSession = new TraceEventSession(EventParseSessionName)) { // The collector cannot survive process termination (safeguard) TraceSession.StopOnDispose = true; // Create event source using (var EventSource = new ETWTraceEventSource(EventParseSessionName, TraceEventSourceType.Session)) { // A DynamicTraceEventParser can understand how to read the embedded manifests that occur in the dataStream var EventParser = new DynamicTraceEventParser(EventSource); // Loop events as they arrive EventParser.All += delegate(TraceEvent data) { // It's a bit ugly but ... ¯\_(ツ)_/¯ if (Collector.FilterOption != FilterOption.None) { if (Collector.FilterOption == FilterOption.Opcode && (byte)data.Opcode != (byte)Collector.FilterValue) { ProcessEventData = false; } else if (Collector.FilterOption == FilterOption.ProcessID && data.ProcessID != (UInt32)Collector.FilterValue) { ProcessEventData = false; } else if (Collector.FilterOption == FilterOption.ProcessName && data.ProcessName != (String)Collector.FilterValue) { ProcessEventData = false; } else if (Collector.FilterOption == FilterOption.EventName && data.EventName != (String)Collector.FilterValue) { ProcessEventData = false; } else { ProcessEventData = true; } } else { ProcessEventData = true; } // Only process/serialize events if they match our filter if (ProcessEventData) { var eRecord = new EventRecordStruct { ProviderGuid = data.ProviderGuid, YaraMatch = new List <String>(), ProviderName = data.ProviderName, EventName = data.EventName, Opcode = data.Opcode, OpcodeName = data.OpcodeName, TimeStamp = data.TimeStamp, ThreadID = data.ThreadID, ProcessID = data.ProcessID, ProcessName = data.ProcessName, PointerSize = data.PointerSize, EventDataLength = data.EventDataLength }; // Populate Proc name if undefined if (String.IsNullOrEmpty(eRecord.ProcessName)) { try { eRecord.ProcessName = Process.GetProcessById(eRecord.ProcessID).ProcessName; } catch { eRecord.ProcessName = "N/A"; } } var EventProperties = new Hashtable(); // Try to parse event XML try { StringReader XmlStringContent = new StringReader(data.ToString()); XmlTextReader EventElementReader = new XmlTextReader(XmlStringContent); while (EventElementReader.Read()) { for (int AttribIndex = 0; AttribIndex < EventElementReader.AttributeCount; AttribIndex++) { EventElementReader.MoveToAttribute(AttribIndex); // Cap maxlen for eventdata elements to 10k if (EventElementReader.Value.Length > 10000) { String DataValue = EventElementReader.Value.Substring(0, Math.Min(EventElementReader.Value.Length, 10000)); EventProperties.Add(EventElementReader.Name, DataValue); } else { EventProperties.Add(EventElementReader.Name, EventElementReader.Value); } } } } catch { // For debugging (?), never seen this fail EventProperties.Add("XmlEventParsing", "false"); } eRecord.XmlEventData = EventProperties; // Serialize to JSON String JSONEventData = Newtonsoft.Json.JsonConvert.SerializeObject(eRecord); int ProcessResult = ProcessJSONEventData(JSONEventData, Collector.OutputType, Collector.Path, Collector.YaraScan, Collector.YaraOptions, Collector.YaraInstance, Collector.YaraRules); // Verify that we processed the result successfully if (ProcessResult != 0) { if (ProcessResult == 1) { SilkUtility.WriteCollectorGuidMessageToServiceTextLog(Collector.CollectorGUID, "The collector failed to write to file", true); } else if (ProcessResult == 2) { SilkUtility.WriteCollectorGuidMessageToServiceTextLog(Collector.CollectorGUID, "The collector failed to POST the result", true); } else { SilkUtility.WriteCollectorGuidMessageToServiceTextLog(Collector.CollectorGUID, "The collector failed write to the eventlog", true); } // Write status to eventlog if dictated by the output type if (Collector.OutputType == OutputType.eventlog) { WriteEventLogEntry($"{{\"Collector\":\"Stop\",\"Error\":true,\"ErrorCode\":{ProcessResult}}}", EventLogEntryType.Error, EventIds.StopError, Collector.Path); } // This collector encountered an error, terminate the service TerminateCollector(); } } }; // Specify the providers details if (Collector.CollectorType == CollectorType.Kernel) { TraceSession.EnableKernelProvider((KernelTraceEventParser.Keywords)Collector.KernelKeywords); } else { // Note that the collector doesn't know if you specified a wrong provider name, // the only tell is that you won't get any events ;) TraceSession.EnableProvider(Collector.ProviderName, (TraceEventLevel)Collector.UserTraceEventLevel, (ulong)Collector.UserKeywords); } // Write status to eventlog if dictated by the output type if (Collector.OutputType == OutputType.eventlog) { String ConvertKeywords; if (Collector.CollectorType == CollectorType.Kernel) { ConvertKeywords = Enum.GetName(typeof(KernelTraceEventParser.Keywords), Collector.KernelKeywords); } else { ConvertKeywords = "0x" + String.Format("{0:X}", (ulong)Collector.UserKeywords); } String Message = $"{{\"Collector\":\"Start\",\"Data\":{{\"Type\":\"{Collector.CollectorType}\",\"Provider\":\"{Collector.ProviderName}\",\"Keywords\":\"{ConvertKeywords}\",\"FilterOption\":\"{Collector.FilterOption}\",\"FilterValue\":\"{Collector.FilterValue}\",\"YaraPath\":\"{Collector.YaraScan}\",\"YaraOption\":\"{Collector.YaraOptions}\"}}}}"; WriteEventLogEntry(Message, EventLogEntryType.SuccessAudit, EventIds.Start, Collector.Path); } // Populate the trace bookkeeper var CollectorInstance = new CollectorInstance { CollectorGUID = Collector.CollectorGUID, EventSource = EventSource, EventParseSessionName = EventParseSessionName, }; SilkUtility.CollectorTaskList.Add(CollectorInstance); // Signal the ManualResetEvent SilkUtility.SignalThreadStarted.Set(); // Continuously process all new events in the data source EventSource.Process(); void TerminateCollector() { EventSource.StopProcessing(); TraceSession?.Stop(); SilkUtility.WriteCollectorGuidMessageToServiceTextLog(Collector.CollectorGUID, "Collector terminated", false); return; } } } }
public static bool TraceETWTraceEventSource ( string providerName , string tracedFileName , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { var r = false; if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return(r); } if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { using (var source = new ETWTraceEventSource(tracedFileName, traceEventSourceType)) { //闭包 long sequence = 0; RegisterCallbacks ( providerName , traceEvents , source , null , (x, y, z) => { long id = 0; if (needCountHits) { id = Interlocked.Increment(ref sequence); } onOneEventTracedOnceProcessAction ( id , x , y , z ); } ); source.Process(); // call the callbacks for each event } } return(true); }
public static void Run() { Out.WriteLine("******************** ObserveGCEvents DEMO ********************"); Out.WriteLine("This program Demos using the reactive framework (IObservable) to monitor"); Out.WriteLine(".NET Garbage collector (GC) events."); Out.WriteLine(); Out.WriteLine("The program will print a line every time 100K of memory was allocated"); Out.WriteLine("on the GC heap along with the type of the object that 'tripped' the 100K"); Out.WriteLine("sample. It will also print a line every time a GC happened and show "); Out.WriteLine("the sizes of each generation after the GC."); Out.WriteLine(); Out.WriteLine("Run a .NET Program while the monitoring is active to see GC events."); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this method."); Debugger.Break(); return; } var monitoringTimeSec = 10; Out.WriteLine("The monitor will run for a maximum of {0} seconds", monitoringTimeSec); Out.WriteLine("Press Ctrl-C to stop monitoring of GC Allocs"); // create a real time user mode session using (var userSession = new TraceEventSession("ObserveGCAllocs")) { // Set up Ctrl-C to stop the session SetupCtrlCHandler(() => { userSession.Stop(); }); // enable the CLR provider with default keywords (minus the rundown CLR events) userSession.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)(ClrTraceEventParser.Keywords.GC)); // Create a stream of GC Allocation events (happens every time 100K of allocations happen) IObservable <GCAllocationTickTraceData> gcAllocStream = userSession.Source.Clr.Observe <GCAllocationTickTraceData>(); // Print the outgoing stream to the console gcAllocStream.Subscribe(allocData => Out.WriteLine("GC Alloc : Proc: {0,10} Amount: {1,6:f1}K TypeSample: {2}", GetProcessName(allocData.ProcessID), allocData.AllocationAmount / 1000.0, allocData.TypeName)); // Create a stream of GC Collection events IObservable <GCHeapStatsTraceData> gcCollectStream = userSession.Source.Clr.Observe <GCHeapStatsTraceData>(); // Print the outgoing stream to the console gcCollectStream.Subscribe(collectData => Out.WriteLine("GC Collect: Proc: {0,10} Gen0: {1,6:f1}M Gen1: {2,6:f1}M Gen2: {3,6:f1}M LargeObj: {4,6:f1}M", GetProcessName(collectData.ProcessID), collectData.GenerationSize0 / 1000000.0, collectData.GenerationSize1 / 1000000.0, collectData.GenerationSize2 / 1000000.0, collectData.GenerationSize3 / 1000000.0)); IObservable <long> timer = Observable.Timer(new TimeSpan(0, 0, monitoringTimeSec)); timer.Subscribe(delegate { Out.WriteLine("Stopped after {0} sec", monitoringTimeSec); userSession.Dispose(); }); // OK we are all set up, time to listen for events and pass them to the observers. userSession.Source.Process(); } Out.WriteLine("Done with program."); }
static void DoMain(string[] args) { if (TraceEventSession.IsElevated() != true) { Console.Error.WriteLine("Must be elevated (Admin) to run this program."); return; } List <string> procargs = null; bool showhelp = false, spawnNewConsoleWindow = false, collectSystemStats = false, printSummary = true, traceChildProcesses = false; string eventNameFilter = null, outputFilename = null; int pid = 0; var p = new OptionSet { { "f|filter=", "Display only events which names contain the given keyword " + "(case insensitive). Does not impact the summary.", v => { eventNameFilter = v; } }, { "s|system", "Collect system statistics (DPC/ISR) - shown in the summary.", v => { collectSystemStats = v != null; } }, { "c|children", "Trace process and all its children.", v => { traceChildProcesses = v != null; } }, { "newconsole", "Start the process in a new console window.", v => { spawnNewConsoleWindow = v != null; } }, { "nosummary", "Prints only ETW events - no summary at the end.", v => { printSummary = v == null; } }, { "h|help", "Show this message and exit.", v => showhelp = v != null }, { "w|writefile=", "Write the output to a logfile", v => { outputFilename = v; } }, { "?", "Show this message and exit.", v => showhelp = v != null } }; try { procargs = p.Parse(args); } catch (OptionException ex) { Console.Error.Write("ERROR: invalid argument"); Console.Error.WriteLine(ex.Message); Console.Error.WriteLine(); showhelp = true; } catch (FormatException) { Console.Error.WriteLine("ERROR: invalid number in one of the constraints"); Console.Error.WriteLine(); showhelp = true; } Debug.Assert(procargs != null); if (!showhelp && !collectSystemStats && procargs.Count == 0) { Console.Error.WriteLine("ERROR: please provide either process name, PID, or turn on system tracing (-s)"); Console.Error.WriteLine(); showhelp = true; } if (showhelp) { ShowHelp(p); return; } // for diagnostics information #if DEBUG Trace.Listeners.Add(new ConsoleTraceListener()); #endif var traceSession = new TraceSession(new WTraceOutput(eventNameFilter, outputFilename), printSummary); SetConsoleCtrlCHook(traceSession); try { if (procargs.Count == 0) { Console.WriteLine("System tracing has started. Press Ctrl + C to stop..."); traceSession.TraceSystemOnly(); } else if (!int.TryParse(procargs[0], out pid)) { traceSession.TraceNewProcess(procargs, spawnNewConsoleWindow, traceChildProcesses, collectSystemStats); } else { traceSession.TraceRunningProcess(pid, traceChildProcesses, collectSystemStats); } } catch (COMException ex) { if ((uint)ex.HResult == 0x800700B7) { Console.Error.WriteLine("ERROR: could not start the kernel logger - make sure it is not running."); } } catch (Win32Exception ex) { Console.Error.WriteLine( $"ERROR: an error occurred while trying to start or open the process, hr: 0x{ex.HResult:X8}, " + $"code: 0x{ex.NativeErrorCode:X8} ({ex.Message})."); } #if !DEBUG catch (Exception ex) { Console.Error.WriteLine($"ERROR: severe error happened when starting application: {ex.Message}"); } #endif }
static Helper() { AvailablePreciseMachineCounters = TraceEventProfileSources.GetInfo(); CanEnableKernelProvider = TraceEventSession.IsElevated() == true; }
/// <summary> /// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to /// the MyEventSource above. Normally this event source would be in a different process, but /// it also works if this process generate the events and I do that here for simplicity. /// </summary> public int Run() { // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { ExceptionHelper.WriteFile("EtwErrors", "Not running as admin"); return(-1); } // 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 = "SellyServiceEtwMonitor"; using (session = new TraceEventSession(sessionName)) { /* 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 (say your program 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(); }; /*****************************************************************************************************/ // Hook up events. To so this first we need a 'Parser. which knows how to part the events of a particular Event Provider. // In this case we get a DynamicTraceEventSource, which knows how to parse any EventSource provider. This parser // is so common, that TraceEventSource as a shortcut property called 'Dynamic' that fetches this parsers. // For debugging, and demo purposes, hook up a callback for every event that 'Dynamic' knows about (this is not EVERY // event only those know about by RegisteredTraceEventParser). However the 'UnhandledEvents' handler below will catch // the other ones. session.Source.Dynamic.All += EtwCallback.EventOccured; // At this point we have created a TraceEventSession, hooked it up to a TraceEventSource, and hooked the // TraceEventSource to a TraceEventParser (you can do several of these), and then hooked up callbacks // up to the TraceEventParser (again you can have several). However we have NOT actually told any // provider (EventSources) to actually send any events to our TraceEventSession. // We do that now. // Enable my provider, you can call many of these on the same session to get events from other providers. // Because this EventSource did not define any keywords, I can only turn on all events or none. var restarted = session.EnableProvider("Microsoft-Windows-WFP", matchAnyKeywords: 0x0000010000000000); // Generally you don't bother with this warning, but for the demo we do. if (restarted) { ToastHelper.PopToast("Restarting ETW session"); } // 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()'. session.Source.Process(); } return(0); }
private static void DoMain(string[] args) { List <string> procargs = null; var showhelp = false; var spawnNewConsoleWindow = false; var traceOptions = new TraceSession.TraceOptions(); string eventNameFilter = null; var p = new OptionSet { { "f|filter=", "Display only events which names contain the given keyword " + "(case insensitive). Does not impact the summary.", v => { eventNameFilter = v; } }, { "b|bytes", "Dump packet bytes to the console.", v => { traceOptions.PrintPacketBytes = v != null; } }, { "c|children", "Trace process and all its children.", v => { traceOptions.TraceChildProcesses = v != null; } }, { "newconsole", "Start the process in a new console window.", v => { spawnNewConsoleWindow = v != null; } }, { "h|help", "Show this message and exit.", v => showhelp = v != null }, { "?", "Show this message and exit.", v => showhelp = v != null } }; try { procargs = p.Parse(args); } catch (OptionException ex) { Console.Error.Write("ERROR: invalid argument"); Console.Error.WriteLine(ex.Message); Console.Error.WriteLine(); showhelp = true; } catch (FormatException) { Console.Error.WriteLine("ERROR: invalid number in one of the constraints"); Console.Error.WriteLine(); showhelp = true; } if (traceOptions.TraceChildProcesses && TraceEventSession.IsElevated() != true) { Console.Error.WriteLine("Must run elevated (Admin) to trace process children."); return; } if (!showhelp && procargs != null && procargs.Count == 0) { Console.Error.WriteLine("ERROR: please provide either process name or PID"); Console.Error.WriteLine(); showhelp = true; } if (showhelp) { ShowHelp(p); return; } // for diagnostics information #if DEBUG Trace.Listeners.Add(new ConsoleTraceListener()); #endif var traceSession = new TraceSession(new ConsoleTraceOutput(eventNameFilter)); SetConsoleCtrlCHook(traceSession); try { if (!int.TryParse(procargs[0], out var pid)) { traceSession.TraceNewProcess(procargs, spawnNewConsoleWindow, traceOptions); } else { traceSession.TraceRunningProcess(pid, traceOptions); } } catch (COMException ex) { if ((uint)ex.HResult == 0x800700B7) { Console.Error.WriteLine("ERROR: could not start the kernel logger - make sure it is not running."); } } catch (Win32Exception ex) { Console.Error.WriteLine( $"ERROR: an error occurred while trying to start or open the process, hr: 0x{ex.HResult:X8}, " + $"code: 0x{ex.NativeErrorCode:X8} ({ex.Message})."); } #if !DEBUG catch (Exception ex) { Console.Error.WriteLine($"ERROR: severe error happened when starting application: {ex.Message}"); } #endif }
public static void Main() { if (TraceEventSession.IsElevated() != true) { Console.WriteLine("Must be elevated (Admin) to run this program."); Console.ReadKey(); //Debugger.Break(); return; } string FileLocation = @"C:\Users\Sam\Desktop\Events.csv"; StreamWriter sw = File.AppendText(FileLocation); using (TraceEventSession session = new TraceEventSession(KernelTraceEventParser.KernelSessionName)) { session.EnableKernelProvider( KernelTraceEventParser.Keywords.Registry | KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.FileIOInit | KernelTraceEventParser.Keywords.NetworkTCPIP | KernelTraceEventParser.Keywords.Process); // Subscribe to RegistryOpen //session.Source.Kernel.AddCallbackForEvents<RegistryTraceData>(processEvent); session.Source.Kernel.RegistrySetValue += delegate(RegistryTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); //sw.WriteLine(RegistryOBJ.Registry(data.ProcessID,null,data.ProcessName,data.EventName,getStatus(data.Status),data.KeyName,data.KeyHandle,null,0)); //sw.WriteLine(data.ProcessID+","+null+"," +data.ProcessName+","+ data.EventName+"," +getStatus(data.Status)+","+ data.KeyName+","+ data.KeyHandle+"," +null+","+ 0); }; session.Source.Kernel.RegistryOpen += delegate(RegistryTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); //sw.WriteLine(data.ProcessID + "," + null + "," + data.ProcessName + "," + data.EventName + "," + getStatus(data.Status) + "," + data.KeyName + "," + data.KeyHandle + "," + null + "," + 0); }; session.Source.Kernel.FileIOFileDelete += delegate(FileIONameTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); }; session.Source.Kernel.RegistryDelete += delegate(RegistryTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); //sw.WriteLine(data.ProcessID + "," + null + "," + data.ProcessName + "," + data.EventName + "," + getStatus(data.Status) + "," + data.KeyName + "," + data.KeyHandle + "," + null + "," + 0); }; session.Source.Kernel.RegistryKCBDelete += delegate(RegistryTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); //sw.WriteLine(data.ProcessID + "," + null + "," + data.ProcessName + "," + data.EventName + "," + getStatus(data.Status) + "," + data.KeyName + "," + data.KeyHandle + "," + null + "," + 0); }; session.Source.Kernel.RegistryKCBCreate += delegate(RegistryTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); //sw.WriteLine(data.ProcessID + "," + null + "," + data.ProcessName + "," + data.EventName + "," + getStatus(data.Status) + "," + data.KeyName + "," + data.KeyHandle + "," + null + "," + 0); }; session.Source.Kernel.FileIOCreate += delegate(FileIOCreateTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); }; session.Source.Kernel.ImageLoad += delegate(ImageLoadTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); }; session.Source.Kernel.FileIORename += delegate(FileIOInfoTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); }; session.Source.Kernel.ProcessStart += delegate(ProcessTraceData data) { Console.WriteLine(data); //sw.WriteLine(data); }; session.Source.Kernel.ProcessStop += delegate(ProcessTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); }; session.Source.Kernel.TcpIpConnect += delegate(TcpIpConnectTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); //sw.WriteLine(data.ProcessID + "," + null + "," + data.ProcessName + "," + data.EventName + "," + 0 + "," + 0 + "," + 0 + "," + data.daddr + "," + data.dport); }; session.Source.Kernel.UdpIpSend += delegate(UdpIpTraceData data) { //Console.WriteLine(data); //sw.WriteLine(data); //sw.WriteLine(data.ProcessName +","+ data.dport); }; session.Source.Process(); } }
public static void Run() { Out.WriteLine("******************** ObserveEventSource DEMO ********************"); Out.WriteLine("This program Demos using the reactive framework (IObservable) to monitor"); Out.WriteLine("EventSource events in real time, parsing their payloads dynamically."); Out.WriteLine(); Out.WriteLine("The program has an EventSource that generates two kinds of events for 10 secs."); Out.WriteLine("while another part of the program reads them using IObservables and prints"); Out.WriteLine("their parsed payload values. "); Out.WriteLine(); if (TraceEventSession.IsElevated() != true) { Out.WriteLine("Must be elevated (Admin) to run this method."); Debugger.Break(); return; } var monitoringTimeSec = 15; Out.WriteLine("The monitor will run for a maximum of {0} seconds", monitoringTimeSec); Out.WriteLine("Press Ctrl-C to stop monitoring."); // create a real time user mode session using (var userSession = new TraceEventSession("ObserveEventSource")) { // Set up Ctrl-C to stop both user mode and kernel mode sessions SetupCtrlCHandler(() => { if (userSession != null) { userSession.Stop(); } }); // Turn on the Microsoft-Demos-SimpleMonitor provider userSession.EnableProvider("Microsoft-Demos-SimpleMonitor"); // Create a stream of the 'MyFirstEvent' event source events and print their payloads IObservable <TraceEvent> firstEventStream = userSession.Source.Dynamic.Observe("Microsoft-Demos-SimpleMonitor", "MyFirstEvent"); firstEventStream.Subscribe(onNext: ev => Out.WriteLine("FIRST_EVENTS : MyName: '{0}' MyId: {1}", ev.PayloadByName("MyName"), ev.PayloadByName("MyId"))); // Create a stream of the 'MySecond'Event' event source events and print their payloads IObservable <TraceEvent> secondEventStream = userSession.Source.Dynamic.Observe("Microsoft-Demos-SimpleMonitor", "MySecondEvent"); secondEventStream.Subscribe(onNext: ev => Out.WriteLine("SECOND_EVENTS : MyId: {0}", ev.PayloadByName("MyId"))); // For debugging purposes, print every event from the SimpleMonitor stream IObservable <TraceEvent> allEventStream = userSession.Source.Dynamic.Observe(null); allEventStream.Subscribe(onNext: ev => Out.WriteLine("FROM_EVENTSOURCE: {0}/{1} ", ev.ProviderName, ev.EventName)); // It is also useful for debugging purposes to see any events that entered by were not handled by any parser. These can be bugs. IObservable <TraceEvent> unhandledEventStream = userSession.Source.ObserveUnhandled(); unhandledEventStream.Subscribe(onNext: ev => Out.WriteLine("UNHANDLED : {0}/{1} ", ev.ProviderName, ev.EventName)); // Start Generate some events so we have something to see Producer.EventGenerator.CreateEvents(); // Set up a timer to stop processing after monitoringTimeSec IObservable <long> timer = Observable.Timer(new TimeSpan(0, 0, monitoringTimeSec)); timer.Subscribe(delegate { Out.WriteLine("Stopped after {0} sec", monitoringTimeSec); userSession.Dispose(); }); // OK we are all set up, time to listen for events and pass them to the observers. userSession.Source.Process(); } Out.WriteLine("Done with program."); }
static int Main(string[] args) { bool bNetConnect = false, bNetTransfer = false, bProcess = false, bThread = false, bImageLoad = false; bool bDns = false, bSysmon = false, bRegistry = false, bFile = false; if (args.Length == 0) { Console.WriteLine("\nUsage: ETWMonitor [net_connect | net_transfer | process | thread | imageload | memory | registry | dns | sysmon]\n"); Console.WriteLine("net_connect : Show new TCP connections"); Console.WriteLine("net_transfer : Show network transfers"); Console.WriteLine("process : Show process creations and exits"); Console.WriteLine("thread : Show suspicious thread creation (cross-process)"); Console.WriteLine("imageload : Show image loading"); Console.WriteLine("file : Show file activity"); Console.WriteLine("registry : Show registry details"); Console.WriteLine("dns : Show DNS requests"); Console.WriteLine("sysmon : Show entries from Sysmon"); return(1); } if (args[0] == "net_connect") { Console.WriteLine("\nShowing new network connections"); bNetConnect = true; } else if (args[0] == "net_transfer") { Console.WriteLine("\nShowing network transfers"); bNetTransfer = true; } else if (args[0] == "process") { Console.WriteLine("\nShowing process creation and exits"); bProcess = true; } else if (args[0] == "thread") { Console.WriteLine("\nShowing suspicious thread creations (cross-process)"); bThread = true; } else if (args[0] == "imageload") { Console.WriteLine("\nShowing image loads"); bImageLoad = true; } else if (args[0] == "file") { Console.WriteLine("\nShowing file system activity"); bFile = true; } else if (args[0] == "registry") { Console.WriteLine("\nShowing registry details"); bRegistry = true; } else if (args[0] == "dns") { Console.WriteLine("\nShowing DNS requests"); bDns = true; } else if (args[0] == "sysmon") { Console.WriteLine("\nShowing Sysmon entries"); bSysmon = true; } else { Console.WriteLine("\nInvalid option"); return(1); } // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return(-1); } var sessionName = ""; if (bNetConnect) { sessionName = "NetConnectSession"; } else if (bNetTransfer) { sessionName = "NetTransferSession"; } else if (bProcess) { sessionName = "ProcessSession"; } else if (bThread) { sessionName = "ThreadSession"; } else if (bImageLoad) { sessionName = "ImageLoadSession"; } else if (bFile) { sessionName = "FileSession"; } else if (bRegistry) { sessionName = "RegistrySession"; } else if (bDns) { sessionName = "DnsSession"; } else if (bSysmon) { sessionName = "SysmonSession"; } else { Console.WriteLine("Error"); return(-1); } using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session' { session.StopOnDispose = true; Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); }; // prepare to read from the session, connect the ETWTraceEventSource to the session using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session)) { Action <TraceEvent> action = delegate(TraceEvent data) { var taskName = data.TaskName; var EventName = data.EventName; if (bProcess) { if (EventName == "Process/DCStart" || EventName == "Process/Start") { ProcessTraceData startprocdata = (ProcessTraceData)data; string exe = (string)data.PayloadByName("ImageFileName"); Console.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + EventName); Console.Write(" PPID: " + startprocdata.ParentID.ToString() + " PID: " + startprocdata.ProcessID.ToString()); Console.Write(" Name: " + exe); Process PName = GetProcByID2(startprocdata.ParentID); if (PName != null) { Console.Write(" ParentName: " + PName.ProcessName); } Console.WriteLine(" CommandLine: " + startprocdata.CommandLine); } else if (EventName == "Process/End") { ProcessTraceData exitprocdata = (ProcessTraceData)data; string exe = (string)data.PayloadByName("ImageFileName"); Console.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + EventName); Console.WriteLine(" PPID: " + exitprocdata.ParentID.ToString() + " PID: " + exitprocdata.ProcessID.ToString() + " " + exe); } } else if (bNetConnect) { if (taskName == "TcpRequestConnect") { int threadID = data.ThreadID; int processID = data.ProcessID; byte[] local_addr = (byte[])data.PayloadByName("LocalAddress"); byte[] remote_addr = (byte[])data.PayloadByName("RemoteAddress"); if (local_addr == null || remote_addr == null) { Console.WriteLine("Null addr!"); } // First two bytes are address family: 2 for ipv4, 10 for ipv6 // Next two bytes are the port var family = new byte[2]; family[0] = local_addr[0]; family[1] = local_addr[1]; ushort family_nr = BitConverter.ToUInt16(family, 0); if (family_nr == 2) { var local_port = new byte[2]; var remote_port = new byte[2]; local_port[0] = local_addr[2]; local_port[1] = local_addr[3]; Array.Reverse(local_port); // Need to reverse port remote_port[0] = remote_addr[2]; remote_port[1] = remote_addr[3]; Array.Reverse(remote_port); // Need to reverse port ushort local_port_nr = BitConverter.ToUInt16(local_port, 0); ushort remote_port_nr = BitConverter.ToUInt16(remote_port, 0); Process proc; string processname = "<not found>"; if (processID > 0) { proc = GetProcByID(processID); if (proc != null) { processname = proc.MainModule.ModuleName; } } Console.Write(data.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss") + " " + taskName + " PID: " + processID); int parentid = GetParentProcessID(processID); if (parentid != 0) { Console.Write(" PPID: " + parentid); } else { Console.Write(" PPID: (null)"); } Console.Write(" TID: " + threadID + " Name: " + processname); proc = GetProcByID(parentid); if (proc != null) { Console.Write(" Parent Name: " + proc.ProcessName); } else { Console.Write(" Parent Name: (null)"); } string local_ip = local_addr[4] + "." + local_addr[5] + "." + local_addr[6] + "." + local_addr[7]; string remote_ip = remote_addr[4] + "." + remote_addr[5] + "." + remote_addr[6] + "." + remote_addr[7]; Console.Write(" " + local_ip + ":" + local_port_nr + "->"); Console.WriteLine(remote_ip + ":" + remote_port_nr + " "); } else if (family_nr == 0x10) { Console.WriteLine("IPV6"); } } } else if (bNetTransfer) { if (EventName == "TcpIp/Send" || EventName == "TcpIp/Recv") { Console.Write(data.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss") + " " + EventName + " PID: " + data.ProcessID); Process processname; processname = GetProcByID(data.ProcessID); if (processname != null) { Console.Write(" Name: " + processname.ProcessName); } else { Console.Write(" Name: (null)"); } IPAddress saddr = (IPAddress)data.PayloadByName("saddr"); IPAddress daddr = (IPAddress)data.PayloadByName("daddr"); int sport = (int)data.PayloadByName("sport"); int dport = (int)data.PayloadByName("dport"); int size = (int)data.PayloadByName("size"); Console.Write(" Src: " + saddr.ToString() + ":" + sport + " Dst: " + daddr.ToString() + ":" + dport); Console.WriteLine(" Size: " + size); } } else if (bThread) // try to catch remote thread injections only { if (taskName == "ThreadStart") { int destProcessID = (int)data.PayloadByName("ProcessID"); int parentid; Process processname; if (data.ProcessID != destProcessID && data.ProcessID != 4) { // check if destpid is not a child of srcpid, otherwise we have a problem! // check the parentpid of srcpid int destThreadID = (int)data.PayloadByName("ThreadID"); int srcThreadID = data.ThreadID; parentid = GetParentProcessID(destProcessID); if (parentid != 0 && parentid != data.ProcessID) { Console.Write(data.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss") + " POSSIBLE THREAD INJECTION: "); Console.Write(taskName + " TID: " + destThreadID + " SrcTID: " + srcThreadID + " SrcPID: " + data.ProcessID); Console.Write(" DestPID: " + destProcessID); processname = GetProcByID(data.ProcessID); if (processname != null) { Console.Write(" SrcName: " + processname.ProcessName); } else { Console.Write(" SrcName: (null)"); } processname = GetProcByID(destProcessID); if (processname != null) { Console.WriteLine(" DestName: " + processname.ProcessName); } else { Console.WriteLine(" DestName: (null)"); } Console.WriteLine("\nDetailed information:\n" + data.ToString()); } } } } else if (bImageLoad) { if (EventName == "Image/DCStart" || EventName == "Image/Load" || EventName == "Image/Unload") { int pid = (int)data.ProcessID; Console.Write(data.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss ") + EventName); Console.Write(" PID: " + pid); Process processname; processname = GetProcByID(pid); if (processname != null) { Console.Write(" Name: " + processname.ProcessName); } else { Console.Write(" Name: (null)"); } string filename = (string)data.PayloadByName("FileName"); Console.WriteLine(" FileName: " + filename); } } else if (bFile) { if (EventName == "CreateNewFile" || EventName == "DeletePath" || EventName == "NameDelete") { int pid = (int)data.ProcessID; Console.Write(data.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss ") + EventName); Console.Write(" PID: " + pid); Process processname; processname = GetProcByID(pid); if (processname != null) { Console.Write(" Name: " + processname.ProcessName); } else { Console.Write(" Name: (null)"); } string filename = ""; if (EventName == "DeletePath") { filename = (string)data.PayloadByName("FilePath"); } else { filename = (string)data.PayloadByName("FileName"); } Console.WriteLine(" File: " + filename); } } else if (bRegistry) { if (EventName == "EventID(1)/CreateKey" || EventName == "EventID(5)/SetValueKey" || EventName == "EventID(3)/DeleteKey" || EventName == "EventID(6)/DeleteValueKey") { int pid = (int)data.ProcessID; Console.Write(data.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss ") + EventName); Console.Write(" PID: " + pid); Process processname; processname = GetProcByID(pid); if (processname != null) { Console.Write(" Name: " + processname.ProcessName); } else { Console.Write(" Name: (null)"); } if (EventName == "EventID(1)/CreateKey") { string RelativeName = (string)data.PayloadByName("RelativeName"); int status = (int)data.PayloadByName("Status"); Console.WriteLine(" Status: " + status + " RelativeName: " + RelativeName); } else if (EventName == "EventID(3)/DeleteKey") { int status = (int)data.PayloadByName("Status"); Console.WriteLine(" Status: " + status); } else if (EventName == "EventID(5)/SetValueKey") { string ValueName = (string)data.PayloadByName("ValueName"); int status = (int)data.PayloadByName("Status"); Console.WriteLine(" Status: " + status + " ValueName: " + ValueName); } else if (EventName == "EventID(6)/DeleteValueKey") { string ValueName = (string)data.PayloadByName("ValueName"); int status = (int)data.PayloadByName("Status"); Console.WriteLine(" Status: " + status + " ValueName: " + ValueName); } } } else if (bDns) { int pid = (int)data.ProcessID; Console.Write(data.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss ") + EventName); Console.Write(" PID: " + pid); Process processname; processname = GetProcByID(pid); if (processname != null) { Console.Write(" Name: " + processname.ProcessName); } else { Console.Write(" Name: (null)"); } string QueryName = (string)data.PayloadByName("QueryName"); Console.WriteLine(" QueryName: " + QueryName); } else if (bSysmon) { Console.WriteLine(data.ToString()); } }; // You can also simply use 'logman query providers' to find out the GUID yourself and wire it in. Guid processProviderGuid; if (bProcess) { session.EnableKernelProvider(KernelTraceEventParser.Keywords.Process); } else if (bNetConnect) { processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-TCPIP"); session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x80); // ut:TcpipDiagnosis seems to do the job } else if (bNetTransfer) { session.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP); } else if (bThread) { processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-Kernel-Process"); session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x20); // WINEVENT_KEYWORD_THREAD } else if (bImageLoad) { session.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad); } else if (bFile) { processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-Kernel-File"); session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x0000000000001410); // KERNEL_FILE_KEYWORD_CREATE_NEW_FILE, KERNEL_FILE_KEYWORD_DELETE_PATH, KERNEL_FILE_KEYWORD_FILENAME } else if (bRegistry) { processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-Kernel-Registry"); session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x0000000000005300); // SetValueKey, CreateKey, DeleteKey, DeleteValueKey } else if (bDns) { processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-DNS-Client"); session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x8000000000000000); // } else if (bSysmon) { processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-Sysmon"); session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x8000000000000000); // KERNEL_MEM_KEYWORD_MEMINFO } else { Console.WriteLine("Error"); return(-1); } // We use different parsers depending on the events. For TCP/IP stuff we need to // use another since we want to register not only ProcessID but also ThreadID for each event if (bNetConnect || bThread || bFile || bRegistry || bDns || bSysmon) { // Hook up the parser that knows about Any EventSources regsitered with windows. (e.g. the OS ones). var registeredParser = new RegisteredTraceEventParser(source); registeredParser.All += action; } else { // Hook up the parser that knows about kernel traces var KernelParser = new KernelTraceEventParser(source); KernelParser.All += action; } Console.WriteLine("Starting Listening for events"); // 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.Close()'. source.Process(); Console.WriteLine(); Console.WriteLine("Stopping Listening for events"); } } return(0); }
/// <summary> /// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to /// the MyEventSource above. Normally this event source would be in a differnet process, but /// it also works if this process generate the evnets and I do that here for simplicity. /// </summary> static int Main(string[] args) { // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return(-1); } // 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 mulitple versions // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix) var sessionName = "ProessMonitorSession"; using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session' { // Note that sessions create a OS object (a session) that lives beyond the lifetime of the process // that created it (like Filles), thus you have to be more careful about always cleaning them up. // An importanty way you can do this is to set the 'StopOnDispose' property which will cause the session to // stop (and thus the OS object will die) when the TraceEventSession dies. Because we used a 'using' // statement, this means that any exception in the code below will clean up the OS object. session.StopOnDispose = true; // By default, if you hit Ctrl-C your .NET objects may not be disposed, so force it to. It is OK if dispose is called twice. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); }; // prepare to read from the session, connect the ETWTraceEventSource to the session using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session)) { Action <TraceEvent> action = delegate(TraceEvent data) { // Console.WriteLine("GOT EVENT: " + data.ToString()); var taskName = data.TaskName; if (taskName == "ProcessStart" || taskName == "ProcessStop") { string exe = (string)data.PayloadByName("ImageName"); string exeName = Path.GetFileNameWithoutExtension(exe); int processId = (int)data.PayloadByName("ProcessID"); if (taskName == "ProcessStart") { int parentProcessId = (int)data.PayloadByName("ParentProcessID"); Console.WriteLine("{0:HH:mm:ss.fff}: {1,-12}: {2} ID: {3} ParentID: {4}", data.TimeStamp, taskName, exeName, processId, parentProcessId); } else { int exitCode = (int)data.PayloadByName("ExitCode"); long cpuCycles = (long)data.PayloadByName("CPUCycleCount"); Console.WriteLine("{0:HH:mm:ss.fff}: {1,-12}: {2} ID: {3} EXIT: {4} CPU Cycles: {5:n0}", data.TimeStamp, taskName, exeName, processId, exitCode, cpuCycles); } } }; // Hook up the parser that knows about Any EventSources regsitered with windows. (e.g. the OS ones. var registeredParser = new RegisteredTraceEventParser(source); registeredParser.All += action; // You can also simply use 'logman query providers' to find out the GUID yourself and wire it in. var processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-Kernel-Process"); if (processProviderGuid == Guid.Empty) { Console.WriteLine("Error could not find Microsoft-Windows-Kernel-Process etw provider."); return(-1); } // Using logman query providers Microsoft-Windows-Kernel-Process I get // 0x0000000000000010 WINEVENT_KEYWORD_PROCESS // 0x0000000000000020 WINEVENT_KEYWORD_THREAD // 0x0000000000000040 WINEVENT_KEYWORD_IMAGE // 0x0000000000000080 WINEVENT_KEYWORD_CPU_PRIORITY // 0x0000000000000100 WINEVENT_KEYWORD_OTHER_PRIORITY // 0x0000000000000200 WINEVENT_KEYWORD_PROCESS_FREEZE // 0x8000000000000000 Microsoft-Windows-Kernel-Process/Analytic // So 0x10 is WINEVENT_KEYWORD_PROCESS session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x10); Console.WriteLine("Starting Listening for events"); // 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.Close()'. source.Process(); Console.WriteLine(); Console.WriteLine("Stopping Listening for events"); } } return(0); }
protected override void ProcessRecord() { ErrorRecord errorRecord = null; if (TraceEventSession.IsElevated() != true) { errorRecord = new ErrorRecord(new InvalidOperationException("Must be elevated (Admin) to run this cmdlet."), "MissingAdminRights", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); return; } processTraceRunner = new TraceSession(new PowerShellTraceOutput(eventQueue, Filter), !NoSummary); bool isMainThreadFinished = false; const bool collectSystemStats = false; // not available in PowerShell ThreadPool.QueueUserWorkItem((o) => { try { if (string.Equals(ParameterSetName, "StartNewProcess", StringComparison.Ordinal)) { var args = new List <string>() { FilePath }; if (ArgumentList != null) { args.AddRange(ArgumentList); } processTraceRunner.TraceNewProcess(args, NewConsole, TraceChildProcesses, collectSystemStats); } else { processTraceRunner.TraceRunningProcess(Pid, TraceChildProcesses, collectSystemStats); } isMainThreadFinished = true; } catch (Exception ex) { errorRecord = new ErrorRecord(ex, ex.GetType().FullName, ErrorCategory.InvalidOperation, null); isMainThreadFinished = true; } }); PowerShellWtraceEvent ev; while (!isMainThreadFinished) { while (eventQueue.TryDequeue(out ev)) { WriteObject(ev); } Thread.Sleep(100); } // the rest of the events while (eventQueue.TryDequeue(out ev)) { WriteObject(ev); } if (errorRecord != null) { WriteError(errorRecord); } }
public bool?IsElevated() { return(TraceEventSession.IsElevated()); }
public static void Run() { // Check OS version if (Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor < 62) { _out.WriteLine("The monitor only works on Win 8 / Win Server 2012 and above."); return; } // Check the process's privilege if (TraceEventSession.IsElevated() != true) { _out.WriteLine("You must be elevated as admin to run this program."); return; } TraceEventSession session = null; _out.WriteLine("************** KernelEventMonitor ***************"); _out.WriteLine("Press <C>-C to stop the monitor!"); _out.WriteLine(); // Set up <C>-C to stop kernel mode sessions Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs cancelArgs) { if (session != null) { session.Dispose(); } cancelArgs.Cancel = true; }; // Set up a timer to stop processing after monitorTime //var timer = new Timer(delegate (object state) //{ // Out.WriteLine("Stopped Monitoring after {0} sec", monitorTime); // if (session != null) // session.Dispose(); //}, null, monitorTime * 1000, Timeout.Infinite); // Create new session to receive kernel evetn using (session = new TraceEventSession("MonitorKernelEventSession")) { //Enable the kernel events we interest _out.WriteLine("Enabling Image Load, Process, FileIO, Registry and Network"); session.EnableKernelProvider( KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.FileIOInit | KernelTraceEventParser.Keywords.Registry | KernelTraceEventParser.Keywords.NetworkTCPIP); // Subscribe kernel events we interest session.Source.UnhandledEvents += EventProcess; session.Source.Kernel.FileIODelete += EventProcess; session.Source.Kernel.FileIOCreate += EventProcess; session.Source.Kernel.FileIODirEnum += EventProcess; session.Source.Kernel.FileIORead += EventProcess; session.Source.Kernel.FileIORename += EventProcess; session.Source.Kernel.FileIOWrite += EventProcess; session.Source.Kernel.ProcessStop += EventProcess; session.Source.Kernel.ProcessStart += EventProcess; session.Source.Kernel.ImageLoad += EventProcess; session.Source.Kernel.TcpIpSend += EventProcess; session.Source.Kernel.TcpIpRecv += EventProcess; session.Source.Kernel.UdpIpRecv += EventProcess; session.Source.Kernel.UdpIpSend += EventProcess; session.Source.Kernel.RegistryCreate += EventProcess; session.Source.Kernel.RegistryDelete += EventProcess; session.Source.Kernel.RegistryDeleteValue += EventProcess; session.Source.Kernel.RegistrySetValue += EventProcess; // Begin to monitor events session.Source.Process(); } //timer.Dispose(); }
public void Run() { var monitoredAppPools = ConfigTools.GetConfiguredAppPools(); var monitoredApps = ConfigTools.GetConfiguredApps(); var console = Boolean.Parse(ConfigurationManager.AppSettings["ConsoleOutput"]); var api = Boolean.Parse(ConfigurationManager.AppSettings["APIOutput"]); if (TraceEventSession.IsElevated() != true) { Console.WriteLine("Must be elevated (Admin) to run this method."); Debugger.Break(); return; } try { var appPoolNames = IISAdminTools.GetApplicationPools(); foreach (var appPoolName in appPoolNames) { appPools.Add(appPoolName, IISAdminTools.GetAppPoolProcesses(appPoolName)); } } catch (Exception ex) { if (ex.Message.Contains("not registered")) { Console.WriteLine("Could not initialize; IIS isn't installed upon this machine."); } Debugger.Break(); } //var monitoringTimeSec = 60; //Console.WriteLine("The monitor will run for a maximum of {0} seconds", monitoringTimeSec); //Console.WriteLine("Press Ctrl-C to stop monitoring of GC Allocs"); // create a real time user mode session using (var userSession = new TraceEventSession("ObserveGCAllocs")) { // Set up Ctrl-C to stop the session SetupCtrlCHandler(() => { userSession.Stop(); }); // enable the CLR provider with default keywords (minus the rundown CLR events) userSession.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)(ClrTraceEventParser.Keywords.GC)); // Create a stream of GC Allocation events (happens every time 100K of allocations happen) IObservable <GCAllocationTickTraceData> gcAllocStream = userSession.Source.Clr.Observe <GCAllocationTickTraceData>(); // Print the outgoing stream to the console //gcAllocStream.Subscribe(allocData => //{ // if (GetProcessName(allocData.ProcessID) == "devenv") // { // //Out.WriteLine("GC Alloc : Proc: {0,10}({1,3}) Amount: {2,6:f1}K TypeSample: {3}", GetProcessName(allocData.ProcessID), GetProcessCPU(allocData.ProcessID), allocData.AllocationAmount / 1000.0, allocData.TypeName); // } //}); // Create a stream of GC Collection events IObservable <GCHeapStatsTraceData> gcCollectStream = userSession.Source.Clr.Observe <GCHeapStatsTraceData>(); // Print the outgoing stream to the console gcCollectStream.Subscribe(collectData => { var appName = GetProcessName(collectData.ProcessID); var metricsList = new List <MetricPackage>(); if (DEBUG_LEVEL == "DEBUG") { Out.WriteLine("Application Name : {0} is reporting in..", appName); } if (DoesProcessIdExist(collectData.ProcessID)) { var appPoolName = GetAppPoolName(collectData.ProcessID); if (DEBUG_LEVEL == "DEBUG") { Out.WriteLine("App Pool Name : {0}", appPoolName); } if (monitoredAppPools.Contains(appPoolName)) { int pid = collectData.ProcessID; Process toMonitor = Process.GetProcessById(pid); int threadCt = toMonitor.Threads.Count; long memoryUsed = toMonitor.WorkingSet64; long memoryCommitted = toMonitor.PeakWorkingSet64; var machineName = System.Environment.MachineName; if (api) { metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appPoolName, "Memory Heap - Gen 0 Usage"), collectData.GenerationSize0)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appPoolName, "Memory Heap - Gen 1 Usage"), collectData.GenerationSize1)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appPoolName, "Memory Heap - Gen 2 Usage"), collectData.GenerationSize2)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appPoolName, "Large Object Heap - Current Usage"), collectData.GenerationSize3)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|Usage Metrics|{2}", machineName, appPoolName, "Current Usage"), memoryUsed)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|Usage Metrics|{2}", machineName, appPoolName, "Current Committed"), memoryCommitted)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|Usage Metrics|{2}", machineName, appPoolName, "Thread Count"), threadCt)); } if (console) { Out.WriteLine("name = Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appPoolName, "Memory Heap - Gen 0 Usage", collectData.GenerationSize0); Out.WriteLine("name = Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appPoolName, "Memory Heap - Gen 1 Usage", collectData.GenerationSize1); Out.WriteLine("name = Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appPoolName, "Memory Heap - Gen 2 Usage", collectData.GenerationSize2); Out.WriteLine("name = Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appPoolName, "Large Object Heap - Current Usage", collectData.GenerationSize3); Out.WriteLine("name = Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appPoolName, "Current Usage", memoryUsed); Out.WriteLine("name = Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appPoolName, "Current Committed", memoryCommitted); Out.WriteLine("name = Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appPoolName, "Thread Count", threadCt); } } } else if (monitoredApps.Contains(appName)) { { var machineName = System.Environment.MachineName; int pid = collectData.ProcessID; Process toMonitor = Process.GetProcessById(pid); long memoryUsed = toMonitor.WorkingSet64; long memoryCommitted = toMonitor.PeakWorkingSet64; if (api) { metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appName, "Memory Heap - Gen 0 Usage"), collectData.GenerationSize0)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appName, "Memory Heap - Gen 1 Usage"), collectData.GenerationSize1)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appName, "Memory Heap - Gen 2 Usage"), collectData.GenerationSize2)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}", machineName, appName, "Large Object Heap - Current Usage"), collectData.GenerationSize3)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|Usage Metrics|{2}", machineName, appName, "Current Usage"), memoryUsed)); metricsList.Add(CreateMetricPackage(string.Format("Custom Metrics|Memory|Nodes|{0}|{1}|Usage Metrics|{2}", machineName, appName, "Current Committed"), memoryCommitted)); } if (console) { Out.WriteLine("name=Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appName, "Memory Heap - Gen 0 Usage", collectData.GenerationSize0); Out.WriteLine("name=Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appName, "Memory Heap - Gen 1 Usage", collectData.GenerationSize1); Out.WriteLine("name=Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appName, "Memory Heap - Gen 2 Usage", collectData.GenerationSize2); Out.WriteLine("name=Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appName, "Large Object Heap - Current Usage", collectData.GenerationSize3); Out.WriteLine("name=Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appName, "Current Usage", memoryUsed); Out.WriteLine("name=Custom Metrics|Memory|Nodes|{0}|{1}|GC Metrics|{2}, value={3}", machineName, appName, "Current Committed", memoryCommitted); } } // Out.WriteLine("GC Collect: Proc: {0,10}({1,3}) Gen0: {2,6:f1}M Gen1: {3,6:f1}M Gen2: {4,6:f1}M LargeObj: {5,6:f1}M", // GetProcessName(collectData.ProcessID), // GetProcessCPU(collectData.ProcessID), // collectData.GenerationSize0 / 1000000.0, // collectData.GenerationSize1 / 1000000.0, // collectData.GenerationSize2 / 1000000.0, // collectData.GenerationSize3 / 1000000.0); } if (metricsList.Count > 0) { WriteLines(metricsList); } }); //IObservable<long> timer = Observable.Timer(new TimeSpan(0, 0, monitoringTimeSec)); //timer.Subscribe(delegate //{ // Console.WriteLine("Stopped after {0} sec", monitoringTimeSec); // userSession.Dispose(); //}); // OK we are all set up, time to listen for events and pass them to the observers. userSession.Source.Process(); } Console.WriteLine("Done with program."); }
static void Main(string[] args) { // This is the name of the event source. // Given just the name of the eventSource you can get the GUID for the evenSource by calling this API. // From a ETW perspective, the GUID is the 'true name' of the EventSource. var providerGuid2 = Guid.Parse("{E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4}"); // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { Console.WriteLine( "To turn on ETW events you need to be Administrator, please run from an Admin process."); } // 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 mulitple versions // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix) Console.WriteLine("Creating a 'My Session' session"); var sessionName = "My Session"; using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session' { // Note that sessions create a OS object (a session) that lives beyond the lifetime of the process // that created it (like Filles), thus you have to be more careful about always cleaning them up. // An importanty way you can do this is to set the 'StopOnDispose' property which will cause the session to // stop (and thus the OS object will die) when the TraceEventSession dies. Because we used a 'using' // statement, this means that any exception in the code below will clean up the OS object. session.StopOnDispose = true; // By default, if you hit Ctrl-C your .NET objects may not be disposed, so force it to. It is OK if dispose is called twice. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); }; // prepare to read from the session, connect the ETWTraceEventSource to the session using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session)) { // To demonstrate non-trivial event manipuation, we calculate the time delta between 'MyFirstEvent and 'MySecondEvent' // These variables are used in this calculation int lastMyEventID = int.MinValue; // an illegal value to start with. double lastMyEventMSec = 0; // Hook up the parser that knows about EventSources var callParser = new ETWClrProfilerTraceEventParser(source); callParser.AddCallbackForEvents(delegate(TraceEvent eventdata) { using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\etwdata.txt", true)) { file.WriteLine(eventdata.EventName + eventdata); } }); //parser2.All += delegate (TraceEvent data) //{ // using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\etwdata.txt", true)) // { // file.WriteLine(data.EventName); // } // Console.WriteLine("Event : " + data.EventName); //}; // Enable my provider, you can call many of these on the same session to get other events. session.EnableProvider(ETWClrProfilerTraceEventParser.ProviderGuid, TraceEventLevel.Always, (ulong)ETWClrProfilerTraceEventParser.Keywords.Call); Console.WriteLine("Staring Listing for events"); // 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.Close()'. source.Process(); Console.WriteLine(); Console.WriteLine("Stopping the collection of events."); } } }
public static void StartTrace(CollectorType CollectorType, ulong TraceKeywords, OutputType OutputType, String Path, FilterOption FilterOption, Object FilterValue, String YaraScan, YaraOptions YaraOptions, String ProviderName = "", UserTraceEventLevel UserTraceEventLevel = UserTraceEventLevel.Informational) { // Is elevated? if (TraceEventSession.IsElevated() != true) { SilkUtility.ReturnStatusMessage("[!] The collector must be run as Administrator..", ConsoleColor.Red); return; } // Print status SilkUtility.ReturnStatusMessage("[>] Starting trace collector (Ctrl-c to stop)..", ConsoleColor.Yellow); SilkUtility.ReturnStatusMessage("[?] Events captured: 0", ConsoleColor.Green); // We will update this dynamically // The kernel collector has naming requirements if (CollectorType == CollectorType.Kernel) { SilkUtility.EventParseSessionName = KernelTraceEventParser.KernelSessionName; } else { // We add a GUID in case of concurrent SilkETW execution String RandId = Guid.NewGuid().ToString(); SilkUtility.EventParseSessionName = ("SilkETWUserCollector_" + RandId); } // Create trace session using (var TraceSession = new TraceEventSession(SilkUtility.EventParseSessionName)) { // The collector cannot survive process termination (safeguard) TraceSession.StopOnDispose = true; // Create event source using (var EventSource = new ETWTraceEventSource(SilkUtility.EventParseSessionName, TraceEventSourceType.Session)) { // Ctrl-c callback handler SilkUtility.SetupCtrlCHandler(() => { if (OutputType == OutputType.eventlog) { SilkUtility.WriteEventLogEntry("{\"Collector\":\"Stop\",\"Error\":false}", EventLogEntryType.SuccessAudit, EventIds.StopOk, Path); } TerminateCollector(); }); // A DynamicTraceEventParser can understand how to read the embedded manifests that occur in the dataStream var EventParser = new DynamicTraceEventParser(EventSource); // Loop events as they arrive EventParser.All += delegate(TraceEvent data) { // It's a bit ugly but ... ¯\_(ツ)_/¯ if (FilterOption != FilterOption.None) { if (FilterOption == FilterOption.Opcode && (byte)data.Opcode != (byte)FilterValue) { SilkUtility.ProcessEventData = false; } else if (FilterOption == FilterOption.ProcessID && data.ProcessID != (UInt32)FilterValue) { SilkUtility.ProcessEventData = false; } else if (FilterOption == FilterOption.ProcessName && data.ProcessName != (String)FilterValue) { SilkUtility.ProcessEventData = false; } else if (FilterOption == FilterOption.EventName && data.EventName != (String)FilterValue) { SilkUtility.ProcessEventData = false; } else { SilkUtility.ProcessEventData = true; } } else { SilkUtility.ProcessEventData = true; } // Only process/serialize events if they match our filter if (SilkUtility.ProcessEventData) { // Display running event count SilkUtility.RunningEventCount += 1; SilkUtility.UpdateEventCount("[?] Events captured: " + SilkUtility.RunningEventCount); var eRecord = new EventRecordStruct { ProviderGuid = data.ProviderGuid, YaraMatch = new List <String>(), ProviderName = data.ProviderName, EventName = data.EventName, Opcode = data.Opcode, OpcodeName = data.OpcodeName, TimeStamp = data.TimeStamp, ThreadID = data.ThreadID, ProcessID = data.ProcessID, ProcessName = data.ProcessName, PointerSize = data.PointerSize, EventDataLength = data.EventDataLength }; // Populate Proc name if undefined if (String.IsNullOrEmpty(eRecord.ProcessName)) { try { eRecord.ProcessName = Process.GetProcessById(eRecord.ProcessID).ProcessName; } catch { eRecord.ProcessName = "N/A"; } } var EventProperties = new Hashtable(); // Try to parse event XML try { StringReader XmlStringContent = new StringReader(data.ToString()); XmlTextReader EventElementReader = new XmlTextReader(XmlStringContent); while (EventElementReader.Read()) { for (int AttribIndex = 0; AttribIndex < EventElementReader.AttributeCount; AttribIndex++) { EventElementReader.MoveToAttribute(AttribIndex); // Cap maxlen for eventdata elements to 10k if (EventElementReader.Value.Length > 10000) { String DataValue = EventElementReader.Value.Substring(0, Math.Min(EventElementReader.Value.Length, 10000)); EventProperties.Add(EventElementReader.Name, DataValue); } else { EventProperties.Add(EventElementReader.Name, EventElementReader.Value); } } } } catch { // For debugging (?), never seen this fail EventProperties.Add("XmlEventParsing", "false"); } eRecord.XmlEventData = EventProperties; // Serialize to JSON String JSONEventData = Newtonsoft.Json.JsonConvert.SerializeObject(eRecord); int ProcessResult = SilkUtility.ProcessJSONEventData(JSONEventData, OutputType, Path, YaraScan, YaraOptions); // Verify that we processed the result successfully if (ProcessResult != 0) { if (ProcessResult == 1) { SilkUtility.ReturnStatusMessage("[!] The collector failed to write to file", ConsoleColor.Red); } else if (ProcessResult == 2) { SilkUtility.ReturnStatusMessage("[!] The collector failed to POST the result", ConsoleColor.Red); } else { SilkUtility.ReturnStatusMessage("[!] The collector failed write to the eventlog", ConsoleColor.Red); } // Write status to eventlog if dictated by the output type if (OutputType == OutputType.eventlog) { SilkUtility.WriteEventLogEntry($"{{\"Collector\":\"Stop\",\"Error\":true,\"ErrorCode\":{ProcessResult}}}", EventLogEntryType.Error, EventIds.StopError, Path); } // Shut down the collector TerminateCollector(); } } }; // Specify the providers details if (CollectorType == CollectorType.Kernel) { TraceSession.EnableKernelProvider((KernelTraceEventParser.Keywords)TraceKeywords); } else { // Note that the collector doesn't know if you specified a wrong provider name, // the only tell is that you won't get any events ;) TraceSession.EnableProvider(ProviderName, (TraceEventLevel)UserTraceEventLevel, TraceKeywords); } // Write status to eventlog if dictated by the output type if (OutputType == OutputType.eventlog) { String ConvertKeywords; if (CollectorType == CollectorType.Kernel) { ConvertKeywords = Enum.GetName(typeof(KernelTraceEventParser.Keywords), TraceKeywords); } else { ConvertKeywords = "0x" + String.Format("{0:X}", TraceKeywords); } String Message = $"{{\"Collector\":\"Start\",\"Data\":{{\"Type\":\"{CollectorType}\",\"Provider\":\"{ProviderName}\",\"Keywords\":\"{ConvertKeywords}\",\"FilterOption\":\"{FilterOption}\",\"FilterValue\":\"{FilterValue}\",\"YaraPath\":\"{YaraScan}\",\"YaraOption\":\"{YaraOptions}\"}}}}"; SilkUtility.WriteEventLogEntry(Message, EventLogEntryType.SuccessAudit, EventIds.Start, Path); } // Continuously process all new events in the data source EventSource.Process(); // Helper to clean up colloector void TerminateCollector() { EventSource.StopProcessing(); TraceSession?.Stop(); Console.CursorVisible = true; SilkUtility.ReturnStatusMessage("[+] Collector terminated", ConsoleColor.Green); } } } }
/// <summary> /// 1. In the specified assembly, get the ETW providers set as assembly attributes (PerformanceTestInfo) /// 2. Check if the benchmark assembly request Precise Machine Counters(PMC) to be collected /// 3. Enable Kernel providers if needed /// 4. Get non-kernel ETW flags set and enable them /// 5. Run the benchmarks /// 6. Stop collecting ETW /// 7. Merge ETL files. /// </summary> /// <param name="assemblyFileName"></param> /// <param name="runId"></param> /// <param name="outputDirectory"></param> /// <param name="action"></param> /// <param name="collectOutputFilesCallback">Callback used to collect a list of files generated.</param> /// <returns></returns> public static void Record(string assemblyFileName, string runId, string outputDirectory, Action action, Action <string> collectOutputFilesCallback) { if (TraceEventSession.IsElevated() != true) { const string errMessage = "In order to profile, application is required to run as Administrator."; WriteErrorLine(errMessage); throw new InvalidOperationException(errMessage); } const int bufferSizeMB = 256; var sessionName = $"Performance-Api-Session-{runId}"; var name = $"{runId}-{Path.GetFileNameWithoutExtension(assemblyFileName)}"; var userFullFileName = Path.Combine(outputDirectory, $"{name}.etl"); var kernelFullFileName = Path.Combine(outputDirectory, $"{name}.kernel.etl"); // without this parameter, EnableKernelProvider will fail PrintProfilingInformation(assemblyFileName, sessionName, userFullFileName); (var providers, var performanceTestMessages) = XunitBenchmark.GetMetadata(assemblyFileName); var kernelProviderInfo = providers.OfType <KernelProviderInfo>().FirstOrDefault(); var needKernelSession = NeedSeparateKernelSession(kernelProviderInfo); using (var safeKernelSession = needKernelSession ? MakeSafeTerminateTraceEventSession(KernelTraceEventParser.KernelSessionName, kernelFullFileName) : null) { var kernelSession = safeKernelSession?.BaseDisposableObject; if (kernelSession != null) { SetPreciseMachineCounters(providers); kernelSession.BufferSizeMB = bufferSizeMB; var flags = (KernelTraceEventParser.Keywords)kernelProviderInfo.Keywords; var stackCapture = (KernelTraceEventParser.Keywords)kernelProviderInfo.StackKeywords; kernelSession.EnableKernelProvider(flags, stackCapture); } using (var safeUserEventSession = MakeSafeTerminateTraceEventSession(sessionName, userFullFileName)) { var userEventSession = safeUserEventSession.BaseDisposableObject; userEventSession.BufferSizeMB = bufferSizeMB; var flags = KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Thread; var stackCapture = KernelTraceEventParser.Keywords.Profile | KernelTraceEventParser.Keywords.ContextSwitch; userEventSession.EnableKernelProvider(flags, stackCapture); foreach (var userProviderInfo in providers.OfType <UserProviderInfo>()) { userEventSession.EnableProvider(userProviderInfo.ProviderGuid, userProviderInfo.Level, userProviderInfo.Keywords); } action.Invoke(); } } TraceEventSession.MergeInPlace(userFullFileName, Console.Out); WriteInfoLine($"ETW Tracing Session saved to \"{userFullFileName}\""); collectOutputFilesCallback(userFullFileName); var assemblyModel = GetAssemblyModel(assemblyFileName, userFullFileName, runId, performanceTestMessages); var xmlFileName = Path.Combine(outputDirectory, $"{name}.xml"); new AssemblyModelCollection { assemblyModel }.Serialize(xmlFileName); WriteInfoLine($"Performance results saved to \"{xmlFileName}\""); collectOutputFilesCallback(xmlFileName); var mdFileName = Path.Combine(outputDirectory, $"{name}.md"); var dt = assemblyModel.GetStatistics(); var mdTable = MarkdownHelper.GenerateMarkdownTable(dt); MarkdownHelper.Write(mdFileName, mdTable); WriteInfoLine($"Markdown file saved to \"{mdFileName}\""); collectOutputFilesCallback(mdFileName); Console.WriteLine(MarkdownHelper.ToTrimmedTable(mdTable)); var csvFileName = Path.Combine(outputDirectory, $"{name}.csv"); dt.WriteToCSV(csvFileName); WriteInfoLine($"Statistics written to \"{csvFileName}\""); collectOutputFilesCallback(csvFileName); }
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. }
private int StartListen() { // This is the name of the event source. var providerName = textBox1.Text; //Debug.Assert(providerName == MyEventSource.Log.Name); // Given just the name of the eventSource you can get the GUID for the evenSource by calling this API. // From a ETW perspective, the GUID is the 'true name' of the EventSource. //var providerGuid = TraceEventSession.GetEventSourceGuidFromName(providerName); //Debug.Assert(providerGuid == MyEventSource.Log.Guid); // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (!(TraceEventSession.IsElevated() ?? false)) { Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return(-1); } // 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 mulitple versions // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix) Console.WriteLine("Creating a 'My Session' session"); var sessionName = "My Session"; using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session' { // Note that sessions create a OS object (a session) that lives beyond the lifetime of the process // that created it (like Filles), thus you have to be more careful about always cleaning them up. // An importanty way you can do this is to set the 'StopOnDispose' property which will cause the session to // stop (and thus the OS object will die) when the TraceEventSession dies. Because we used a 'using' // statement, this means that any exception in the code below will clean up the OS object. session.StopOnDispose = true; // By default, if you hit Ctrl-C your .NET objects may not be disposed, so force it to. It is OK if dispose is called twice. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); }; // prepare to read from the session, connect the ETWTraceEventSource to the session using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session)) { // To demonstrate non-trivial event manipuation, we calculate the time delta between 'MyFirstEvent and 'MySecondEvent' // These variables are used in this calculation int lastMyEventID = int.MinValue; // an illegal value to start with. double lastMyEventMSec = 0; // Hook up the parser that knows about EventSources var parser = new DynamicTraceEventParser(source); parser.All += delegate(TraceEvent data) { Console.WriteLine("GOT EVENT: " + data.ToString()); //if (data.ProviderGuid == providerGuid) // We don't actually need this since we only turned one one provider. //{ // Note that this is the inefficient way of parsing events (there are string comparisions on the // event Name and every payload value), however it is fine for events that occur less than 100 times // a second. For more volumous events, you should consider making a parser for you eventSource // (covered in another demo). This makes your code fare less 'reflection-like' where you have lots // of strings (e.g. "MyFirstEvent", "MyId" ...) which is better even ignoring the performance benefit. if (data.EventName == "MyFirstEvent") { // On First Events, simply remember the ID and time of the event lastMyEventID = (int)data.PayloadByName("MyId"); lastMyEventMSec = data.TimeStampRelativeMSec; } else if (data.EventName == "MySecondEvent") { // On Second Events, if the ID matches, compute the delta and display it. if (lastMyEventID == (int)data.PayloadByName("MyId")) { Console.WriteLine(" > Time Delta from first Event = {0:f3} MSec", data.TimeStampRelativeMSec - lastMyEventMSec); } } else if (data.EventName == "Stop") { // Stop processing after we we see the 'Stop' event //source.DisposeClose(); } //} }; // Enable my provider, you can call many of these on the same session to get other events. session.EnableProvider(providerName, TraceEventLevel.Always); // 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. Console.WriteLine("Staring Listing for events"); // 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.Close()'. source.Process(); Console.WriteLine(); Console.WriteLine("Stopping the collection of events."); } } return(0); }