public static void Zip(string fileName)
            {
                var etlWriter = new ZippedETLWriter(fileName);

                etlWriter.Zip             = true;
                etlWriter.CompressETL     = true;
                etlWriter.DeleteInputFile = true;
                etlWriter.WriteArchive();
            }
        /// <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);
        }
        public override TestResult[] Execute(ITestMethod testMethod)
        {
            TestResult[] errorResults = ValidateElevated(testMethod);
            if (errorResults != null)
            {
                return(errorResults);
            }

            Exception signatureException = GetMethodSignatureException(testMethod);

            if (signatureException != null)
            {
                return(testMethod.CreateExceptionResult(signatureException));
            }

            var    runParameters = TestRunParameters.Read();
            string logFolder     = runParameters.LogFolder;
            bool   shouldLog     = !string.IsNullOrEmpty(logFolder);

            if (shouldLog)
            {
                try
                {
                    logFolder = CreateLogFolder(logFolder, testMethod.TestMethodName);
                }
                catch (Exception e)
                {
                    return(testMethod.CreateExceptionResult(e));
                }
            }

            int iterations = runParameters.Iterations;
            var results    = new TestResult[iterations];

            for (int iteration = 1; iteration <= iterations; iteration++)
            {
                string sessionName = $"{testMethod.TestMethodName}-{iteration}";

                using (var session = new TraceEventSession(sessionName))
                {
                    EnableKernelProviders(session, shouldLog);

                    TraceEventDispatcher source;
                    ZippedETLWriter      writer = null;
                    if (shouldLog)
                    {
                        string etlPath = Path.Combine(logFolder, $"Iteration{iteration}.etl");
                        source = new ETWReloggerTraceEventSource(sessionName, TraceEventSourceType.Session, etlPath);
                        writer = new ZippedETLWriter(etlPath);
                    }
                    else
                    {
                        source = session.Source;
                    }

                    EnableProviders(session);
                    PerformanceTestContext context = CreateContext(source);

                    Task <TestResult> testTask = Task.Run(() => testMethod.Invoke(new object[] { context }));

                    // This is a blocking call that in the case of ETWReloggerTraceEventSource, must be run on the same
                    // thread as ETWReloggerTraceEventSource was created on. It will become unblocked when the
                    // PerformanceTestContext calls StopProcessing on the source.
                    source.Process();

                    TestResult result      = testTask.Result;
                    string     displayName = testMethod.TestMethodName;
                    if (iterations > 1)
                    {
                        displayName += $" [{iteration}/{iterations}]";
                    }

                    result.DisplayName = displayName;

                    session.Flush();
                    OnIterationEnded(context);

                    context.LogScenarios();
                    context.LogMemoryDelta();
                    context.LogMessage($"{displayName} completed. {session.EventsLost} events lost.");
                    context.WriteLogsToResult(result, writer);

                    results[iteration - 1] = result;
                }
            }

            return(results);
        }