/// <summary> /// Stops collector with the given sessionName. /// </summary> /// <param name="collectorName"> /// Name of the collector to be stopped. /// </param> /// <returns> /// True if the operation succeeded, false otherwise. /// </returns> public static bool StopCollector(string collectorName) { const string MethodName = "StopCollector"; if (string.IsNullOrEmpty(collectorName)) { throw new ArgumentException("collectorName cannot be null or empty.", "collectorName"); } Logger.Log( LoggerLevel.Info, LogId, MethodName, "Attempting to stop ETW session [{0}]", collectorName); if (!EtwSessionManager.Stop(collectorName)) { Logger.Log( LoggerLevel.Error, LogId, MethodName, "Failed to stop ETW session [{0}]", collectorName); return(false); } Logger.Log( LoggerLevel.Info, LogId, MethodName, "ETW session [{0}] was stopped", collectorName); return(true); }
/// <summary> /// Checks if an existing ETW session satisfies the given collector configuration. /// </summary> /// <param name="config"> /// Configuration that should be checked against the existing ETW session. /// </param> /// <returns> /// True if the existing session satisfies the given configuration, false otherwise. /// </returns> internal static bool ExistingSessionSatisfiesProviders(CollectorConfiguration config) { // Ok, the session is already in place, just check the providers bool providersOk = false; NativeMethods.EventTraceProperties sessionProperties; if (EtwSessionManager.TryGetSessionProperties(config.Name, out sessionProperties)) { providersOk = true; // Loop over providers and ensure that they are good var loggerId = sessionProperties.Wnode.HistoricalContext; foreach (var provider in config.Providers) { NativeMethods.TraceEnableInfo providerInSession; providersOk = EtwSessionManager.GetProviderInfo(loggerId, provider.Value.Id, out providerInSession) && providerInSession.IsEnabled != 0 && provider.Value.Level <= (EtwTraceLevel)providerInSession.Level && (provider.Value.KeywordsAll | providerInSession.MatchAllKeyword) == provider.Value.KeywordsAll && (provider.Value.KeywordsAny & providerInSession.MatchAnyKeyword) == provider.Value.KeywordsAny; if (!providersOk) { // pass the native type to a provider to get a nice string representing the // current settings var providerConfigInSession = new ProviderConfiguration( provider.Value.Id, (EtwTraceLevel)providerInSession.Level, providerInSession.MatchAnyKeyword, providerInSession.MatchAllKeyword); Logger.Log( LoggerLevel.Error, LogId, "ExistingSessionSatisfiesProviders", "Provider configuration [{0}] is not satisfied in ETW session [{1}]. Actual provider settings in session: {2}", provider.Value, config.Name, providerInSession.IsEnabled == 0 ? "provider not enabled" : providerConfigInSession.ToString()); } } } return(providersOk); }
/// <summary> /// Starts the collector with the given configuration. /// </summary> /// <param name="config"> /// The configuration of the collector to be started. /// </param> /// <returns> /// The complete list with the back log of ETL files associated to the collector from oldest to newest. /// </returns> public List <string> StartCollector(CollectorConfiguration config) { const string MethodName = "StartCollector"; var etlBacklog = new List <string>(); // If a deprecated session exists try to shut it down NativeMethods.EventTraceProperties traceProperties; if (!string.IsNullOrEmpty(config.DeprecatedCollector)) { StopCollector(config.DeprecatedCollector); } var clockType = config.ClockType == ClockType.Default ? ClockType.Perf : config.ClockType; var preExistingSession = false; if (EtwSessionManager.TryGetSessionProperties(this.Name, out traceProperties)) { if ((NativeMethods.EtwSessionClockType)clockType == traceProperties.Wnode.ClientContext) { // Old session can be re-used. preExistingSession = true; } else { // Old session needs to be stopped. Note that if stop failed there is a follow up check to ensure that // the error was not due to the session being stopped between the checks. if (!StopCollector(this.Name)) { Logger.Log( LoggerLevel.Error, LogId, MethodName, "Failed to stop existing trace session [{0}]. Cannot proceed to correctly update session.", this.Name); return(etlBacklog); } } } StringBuilder sb = new StringBuilder(256); sb.AppendFormat( CultureInfo.InvariantCulture, "{0} \"{1}\" -nb {2} {3} -bs {4} -ft {5} -ct {6} -ets ", preExistingSession ? "update" : "start", config.Name, config.MinBufferCount.ToString(CultureInfo.InvariantCulture), config.MaxBufferCount.ToString(CultureInfo.InvariantCulture), config.BufferSizeKB.ToString(CultureInfo.InvariantCulture), config.FlushTimerSec.ToString(CultureInfo.InvariantCulture), clockType); if (config.SessionType == SessionType.Realtime) { sb.Append("-rt"); } else { // Add parameters for proper ETL file rotation this.isFileCollector = true; this.maxFileCount = config.MaxFileCount; this.maxFileSizeKB = config.MaxFileSizeMB * 1024; this.maxFileTimeSpan = config.MaxFileTimeSpan; this.etlBaseName = config.OriginalName; this.EtlLogsDirectory = Path.Combine(this.baseEtlLocation, config.OriginalName); if (!ProtectedIO( () => Directory.CreateDirectory(this.EtlLogsDirectory), e => { Logger.Log( LoggerLevel.Error, LogId, MethodName, "Failed to create directory [{0}] for collector [{1}]. Exception: {2}", this.EtlLogsDirectory, config.Name, e); Logger.Log( LoggerLevel.Error, LogId, MethodName, "Failed to create or update ETW session [{0}]", config.Name); })) { return(etlBacklog); } // Get the full list of available ETL files, it will be trimmed according to success of update command. etlBacklog = this.GetExistingEtlFiles(); this.currentEtlSessionFile = this.GenerateNextSessionFileName(); sb.AppendFormat(CultureInfo.InvariantCulture, "-mode Sequential -o \"{0}\"", this.currentEtlSessionFile); if (config.SessionType == SessionType.FileAndRealtime || config.SessionType == SessionType.RealtimeAndFile) { sb.Append(" -rt"); } } var exitCode = RunCommand("logman", sb.ToString()); this.lastRotationTime = DateTime.UtcNow; if (exitCode != ExitCodeSuccess) { Logger.Log( LoggerLevel.Error, LogId, MethodName, "Logman failed to create or update ETW session [{0}].", config.Name); if (preExistingSession) { if (config.SessionType != SessionType.Realtime) { if (!EtwSessionManager.TryGetCurrentFileOfSession(config.Name, out this.currentEtlSessionFile)) { Logger.Log( LoggerLevel.Error, LogId, MethodName, "Failed to retrieve name of the ETL being used by ETW session [{0}].", config.Name); } if (etlBacklog.Count > 0 && string.Compare( etlBacklog.Last(), this.currentEtlSessionFile, StringComparison.OrdinalIgnoreCase) == 0) { etlBacklog.RemoveAt(etlBacklog.Count - 1); Logger.Log( LoggerLevel.Warning, LogId, MethodName, "Current ETL file removed from backlog list since it is still in use. ETL File [{0}]", this.currentEtlSessionFile); } } Logger.Log( LoggerLevel.Info, LogId, MethodName, "Attempting to mitigate with pre-existing session..."); if (!ExistingSessionSatisfiesProviders(config)) { Logger.Log( LoggerLevel.Error, LogId, "StartCollector", "Pre-existing session cannot be used since it does not satisfy the config."); } else { Logger.Log( LoggerLevel.Error, LogId, MethodName, "Using pre-existing ETW session since it satisfied the configuration."); exitCode = ExitCodeSuccess; } } } if (exitCode == ExitCodeSuccess || preExistingSession) { Logger.Log( LoggerLevel.Info, LogId, MethodName, "ETW session is in place call UpdateProviders to enable them."); } etlBacklog.Sort(StringComparer.OrdinalIgnoreCase); return(etlBacklog); }