/// <summary> /// Constructs a CallContextServiceProfiler. /// </summary> /// <param name="distributor">A <see cref="ScopeOnSystemSwitchedDistributor"/> to hook into to receive system change events.</param> /// <param name="scopeName">The name of the call contxt being tracked.</param> /// <param name="systemGroupTransform">A <see cref="Regex"/> string to transform the procesor into a system group.</param> /// <param name="startSystem">The optional starting system.</param> public CallContextServiceProfiler(ScopeOnSystemSwitchedDistributor distributor, string scopeName, Regex?systemGroupTransform, string startSystem = "") { _distributor = distributor; _systemGroupTransform = systemGroupTransform; _scopeName = scopeName; _stopwatchTicksUsedByGroup = new Dictionary <string, (long, long)>(); _currentGroup = ProcessOrSingleTimeWindowServiceProfiler.GroupSystem(_systemGroupTransform, startSystem); _currentGroupStartStopwatchTicks = AmbientClock.Ticks; distributor.RegisterSystemSwitchedNotificationSink(this); }
/// <summary> /// Creates a service profiler which profiles the entire process for the entire (remaining) duration of execution. /// Note that this is only useful to determine the distribution for an entire process from start to finish, which is not very useful if the process is very long-lived. /// <see cref="CreateTimeWindowProfiler"/> is a better match in most situations. /// </summary> /// <param name="scopeName">A name for the contxt to attach to the analyzer.</param> /// <param name="overrideSystemGroupTransformRegex">A <see cref="Regex"/> string to transform the procesor into a processor group.</param> /// <returns>A <see cref="IAmbientServiceProfile"/> containing a service profile for the entire process. Note that the returned object is NOT thread-safe.</returns> /// <remarks> /// This is different from using <see cref="CreateCallContextProfiler"/> because that will only analyze the call context it's called from, /// whereas this will analyze all threads and call contexts in the process. /// They will produce the same results only for programs where there is only a single call context (no parallelization) /// </remarks> public IAmbientServiceProfile?CreateProcessProfiler(string scopeName, string?overrideSystemGroupTransformRegex = null) { IAmbientServiceProfiler?metrics = _AmbientServiceProfiler.Local; if (metrics != null) { Regex?groupTransform = (overrideSystemGroupTransformRegex == null) ? _defaultSystemGroupTransformSetting.Value : new Regex(overrideSystemGroupTransformRegex, RegexOptions.Compiled); ProcessOrSingleTimeWindowServiceProfiler tracker = new ProcessOrSingleTimeWindowServiceProfiler(metrics, scopeName, groupTransform); return(tracker); } return(null); }
private ProcessOrSingleTimeWindowServiceProfiler?Rotate(IAmbientServiceProfiler metrics, TimeSpan windowPeriod, Regex?systemGroupTransform) { string windowName = WindowScope.WindowId(AmbientClock.UtcNow, windowPeriod); string newAccumulatorScopeName = _scopeNamePrefix + windowName + "(" + WindowScope.WindowSize(windowPeriod) + ")";; ProcessOrSingleTimeWindowServiceProfiler newAccumulator = new ProcessOrSingleTimeWindowServiceProfiler(metrics, newAccumulatorScopeName, systemGroupTransform); ProcessOrSingleTimeWindowServiceProfiler?oldAccumulator = System.Threading.Interlocked.Exchange(ref _timeWindowCallContextCollector, newAccumulator); if (oldAccumulator != null) { // close out the old accumulator oldAccumulator.CloseSampling(); } return(oldAccumulator); }
/// <summary> /// Notifies the notification sink that the system has switched. /// </summary> /// <remarks> /// This function will be called whenever the service profiler is told that the currently-processing system has switched. /// Note that the previously-executing system may or may not be revised at this time. /// Such revisions can be used to distinguish between processing that resulted in success or failure, or other similar outcomes that the notifier wishes to distinguish. /// </remarks> /// <param name="newSystemStartStopwatchTimestamp">The stopwatch timestamp when the new system started.</param> /// <param name="newSystem">The identifier for the system that is starting to run.</param> /// <param name="oldSystemStartStopwatchTimestamp">The stopwatch timestamp when the old system started running.</param> /// <param name="revisedOldSystem">The (possibly-revised) name for the system that has just finished running, or null if the identifier for the old system does not need revising.</param> public void OnSystemSwitched(long newSystemStartStopwatchTimestamp, string newSystem, long oldSystemStartStopwatchTimestamp, string?revisedOldSystem = null) { string?justEndedGroup = (revisedOldSystem == null) ? _currentGroup : ProcessOrSingleTimeWindowServiceProfiler.GroupSystem(_systemGroupTransform, revisedOldSystem); // update the just ended group ValueTuple <long, long> values; if (!_stopwatchTicksUsedByGroup.TryGetValue(justEndedGroup, out values)) { _stopwatchTicksUsedByGroup.Add(justEndedGroup, (newSystemStartStopwatchTimestamp - oldSystemStartStopwatchTimestamp, 1)); } else { _stopwatchTicksUsedByGroup[justEndedGroup] = (values.Item1 + newSystemStartStopwatchTimestamp - oldSystemStartStopwatchTimestamp, values.Item2 + 1); } string?newGroup = ProcessOrSingleTimeWindowServiceProfiler.GroupSystem(_systemGroupTransform, newSystem); // switch to the new processor _currentGroup = newGroup; _currentGroupStartStopwatchTicks = newSystemStartStopwatchTimestamp; }