diff options
author | Drachenkaetzchen <felicia@drachenkatze.org> | 2020-01-15 16:01:35 +0100 |
---|---|---|
committer | Drachenkaetzchen <felicia@drachenkatze.org> | 2020-01-15 16:01:35 +0100 |
commit | 694cca4b21878850ba6131105a0c560fdfbc5f10 (patch) | |
tree | 016aadbd247f72e352fc341f3ce944980dc70c90 /src/SMAPI | |
parent | 280dc911839f8996cddd9804f3f545cc38d20243 (diff) | |
download | SMAPI-694cca4b21878850ba6131105a0c560fdfbc5f10.tar.gz SMAPI-694cca4b21878850ba6131105a0c560fdfbc5f10.tar.bz2 SMAPI-694cca4b21878850ba6131105a0c560fdfbc5f10.zip |
Added documentation for all performance counter methods and members. Refactored the naming of several members and methods to reflect their actual intention.
Diffstat (limited to 'src/SMAPI')
10 files changed, 261 insertions, 195 deletions
diff --git a/src/SMAPI/Framework/PerformanceCounter/AlertContext.cs b/src/SMAPI/Framework/PerformanceCounter/AlertContext.cs index c4a57a49..63f0a5ed 100644 --- a/src/SMAPI/Framework/PerformanceCounter/AlertContext.cs +++ b/src/SMAPI/Framework/PerformanceCounter/AlertContext.cs @@ -1,14 +1,26 @@ namespace StardewModdingAPI.Framework.PerformanceCounter { - public struct AlertContext + /// <summary>The context for an alert.</summary> + internal struct AlertContext { - public string Source; - public double Elapsed; + /// <summary>The source which triggered the alert.</summary> + public readonly string Source; + /// <summary>The elapsed milliseconds.</summary> + public readonly double Elapsed; + + /// <summary>Creates a new alert context.</summary> + /// <param name="source">The source which triggered the alert.</param> + /// <param name="elapsed">The elapsed milliseconds.</param> public AlertContext(string source, double elapsed) { this.Source = source; this.Elapsed = elapsed; } + + public override string ToString() + { + return $"{this.Source}: {this.Elapsed:F2}ms"; + } } } diff --git a/src/SMAPI/Framework/PerformanceCounter/AlertEntry.cs b/src/SMAPI/Framework/PerformanceCounter/AlertEntry.cs index 284af1ce..b87d8642 100644 --- a/src/SMAPI/Framework/PerformanceCounter/AlertEntry.cs +++ b/src/SMAPI/Framework/PerformanceCounter/AlertEntry.cs @@ -2,18 +2,31 @@ using System.Collections.Generic; namespace StardewModdingAPI.Framework.PerformanceCounter { + /// <summary>A single alert entry.</summary> internal struct AlertEntry { - public PerformanceCounterCollection Collection; - public double ExecutionTimeMilliseconds; - public double Threshold; - public List<AlertContext> Context; + /// <summary>The collection in which the alert occurred.</summary> + public readonly PerformanceCounterCollection Collection; - public AlertEntry(PerformanceCounterCollection collection, double executionTimeMilliseconds, double threshold, List<AlertContext> context) + /// <summary>The actual execution time in milliseconds.</summary> + public readonly double ExecutionTimeMilliseconds; + + /// <summary>The configured alert threshold. </summary> + public readonly double ThresholdMilliseconds; + + /// <summary>The context list, which records all sources involved in exceeding the threshold.</summary> + public readonly List<AlertContext> Context; + + /// <summary>Creates a new alert entry.</summary> + /// <param name="collection">The source collection in which the alert occurred.</param> + /// <param name="executionTimeMilliseconds">The actual execution time in milliseconds.</param> + /// <param name="thresholdMilliseconds">The configured threshold in milliseconds.</param> + /// <param name="context">A list of AlertContext to record which sources were involved</param> + public AlertEntry(PerformanceCounterCollection collection, double executionTimeMilliseconds, double thresholdMilliseconds, List<AlertContext> context) { this.Collection = collection; this.ExecutionTimeMilliseconds = executionTimeMilliseconds; - this.Threshold = threshold; + this.ThresholdMilliseconds = thresholdMilliseconds; this.Context = context; } } diff --git a/src/SMAPI/Framework/PerformanceCounter/EventPerformanceCounterCollection.cs b/src/SMAPI/Framework/PerformanceCounter/EventPerformanceCounterCollection.cs index 1aec28f3..4690c512 100644 --- a/src/SMAPI/Framework/PerformanceCounter/EventPerformanceCounterCollection.cs +++ b/src/SMAPI/Framework/PerformanceCounter/EventPerformanceCounterCollection.cs @@ -2,8 +2,13 @@ using StardewModdingAPI.Framework.Events; namespace StardewModdingAPI.Framework.PerformanceCounter { + /// <summary>Represents a performance counter collection specific to game events.</summary> internal class EventPerformanceCounterCollection: PerformanceCounterCollection { + /// <summary>Creates a new event performance counter collection.</summary> + /// <param name="manager">The performance counter manager.</param> + /// <param name="event">The ManagedEvent.</param> + /// <param name="isImportant">If the event is flagged as important.</param> public EventPerformanceCounterCollection(PerformanceCounterManager manager, IManagedEvent @event, bool isImportant) : base(manager, @event.GetName(), isImportant) { } diff --git a/src/SMAPI/Framework/PerformanceCounter/IPerformanceCounterEvent.cs b/src/SMAPI/Framework/PerformanceCounter/IPerformanceCounterEvent.cs deleted file mode 100644 index 1bcf4fa0..00000000 --- a/src/SMAPI/Framework/PerformanceCounter/IPerformanceCounterEvent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace StardewModdingAPI.Framework.Utilities -{ - public interface IPerformanceCounterEvent - { - string GetEventName(); - long GetAverageCallsPerSecond(); - - double GetGameAverageExecutionTime(); - double GetModsAverageExecutionTime(); - double GetAverageExecutionTime(); - } -} diff --git a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounter.cs b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounter.cs index 3dbc693a..b2ec4c90 100644 --- a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounter.cs +++ b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounter.cs @@ -1,26 +1,32 @@ using System; -using System.Diagnostics; using System.Linq; using Cyotek.Collections.Generic; -using StardewModdingAPI.Framework.Utilities; namespace StardewModdingAPI.Framework.PerformanceCounter { internal class PerformanceCounter { + /// <summary>The size of the ring buffer.</summary> private const int MAX_ENTRIES = 16384; - public string Source { get; } - public static Stopwatch Stopwatch = new Stopwatch(); - public static long TotalNumEventsLogged; - public double MonitorThresholdMilliseconds { get; set; } - public bool Monitor { get; set; } + /// <summary>The collection to which this performance counter belongs.</summary> private readonly PerformanceCounterCollection ParentCollection; + /// <summary>The circular buffer which stores all performance counter entries</summary> private readonly CircularBuffer<PerformanceCounterEntry> _counter; + /// <summary>The peak execution time</summary> private PerformanceCounterEntry? PeakPerformanceCounterEntry; + /// <summary>The name of the source.</summary> + public string Source { get; } + + /// <summary>The alert threshold in milliseconds</summary> + public double AlertThresholdMilliseconds { get; set; } + + /// <summary>If alerting is enabled or not</summary> + public bool EnableAlerts { get; set; } + public PerformanceCounter(PerformanceCounterCollection parentCollection, string source) { this.ParentCollection = parentCollection; @@ -28,90 +34,87 @@ namespace StardewModdingAPI.Framework.PerformanceCounter this._counter = new CircularBuffer<PerformanceCounterEntry>(PerformanceCounter.MAX_ENTRIES); } - public void Reset() - { - this._counter.Clear(); - this.PeakPerformanceCounterEntry = null; - } - - public int GetAverageCallsPerSecond() - { - var x = this._counter.GroupBy( - p => - (int) p.EventTime.Subtract( - new DateTime(1970, 1, 1) - ).TotalSeconds); - - return x.Last().Count(); - } - + /// <summary>Adds a new performance counter entry to the list. Updates the peak entry and adds an alert if + /// monitoring is enabled and the execution time exceeds the threshold.</summary> + /// <param name="entry">The entry to add.</param> public void Add(PerformanceCounterEntry entry) { - PerformanceCounter.Stopwatch.Start(); this._counter.Put(entry); - if (this.Monitor && entry.Elapsed.TotalMilliseconds > this.MonitorThresholdMilliseconds) - { - this.ParentCollection.AddAlert(entry.Elapsed.TotalMilliseconds, this.MonitorThresholdMilliseconds, new AlertContext(this.Source, entry.Elapsed.TotalMilliseconds)); - } + if (this.EnableAlerts && entry.ElapsedMilliseconds > this.AlertThresholdMilliseconds) + this.ParentCollection.AddAlert(entry.ElapsedMilliseconds, this.AlertThresholdMilliseconds, + new AlertContext(this.Source, entry.ElapsedMilliseconds)); if (this.PeakPerformanceCounterEntry == null) - { this.PeakPerformanceCounterEntry = entry; - } else { - if (entry.Elapsed.TotalMilliseconds > this.PeakPerformanceCounterEntry.Value.Elapsed.TotalMilliseconds) - { + if (entry.ElapsedMilliseconds > this.PeakPerformanceCounterEntry.Value.ElapsedMilliseconds) this.PeakPerformanceCounterEntry = entry; - } } + } - PerformanceCounter.Stopwatch.Stop(); - PerformanceCounter.TotalNumEventsLogged++; + /// <summary>Clears all performance counter entries and resets the peak entry.</summary> + public void Reset() + { + this._counter.Clear(); + this.PeakPerformanceCounterEntry = null; } + /// <summary>Returns the peak entry.</summary> + /// <returns>The peak entry.</returns> public PerformanceCounterEntry? GetPeak() { return this.PeakPerformanceCounterEntry; } + /// <summary>Resets the peak entry.</summary> public void ResetPeak() { this.PeakPerformanceCounterEntry = null; } + /// <summary>Returns the last entry added to the list.</summary> + /// <returns>The last entry</returns> public PerformanceCounterEntry? GetLastEntry() { if (this._counter.IsEmpty) - { return null; - } + return this._counter.PeekLast(); } + /// <summary>Returns the average execution time of all entries.</summary> + /// <returns>The average execution time in milliseconds.</returns> public double GetAverage() { if (this._counter.IsEmpty) - { return 0; - } - return this._counter.Average(p => p.Elapsed.TotalMilliseconds); + return this._counter.Average(p => p.ElapsedMilliseconds); } - public double GetAverage(TimeSpan range) + /// <summary>Returns the average over a given time span.</summary> + /// <param name="range">The time range to retrieve.</param> + /// <param name="relativeTo">The DateTime from which to start the average. Defaults to DateTime.UtcNow if null</param> + /// <returns>The average execution time in milliseconds.</returns> + /// <remarks> + /// The relativeTo parameter specifies from which point in time the range is subtracted. Example: + /// If DateTime is set to 60 seconds ago, and the range is set to 60 seconds, the method would return + /// the average between all entries between 120s ago and 60s ago. + /// </remarks> + public double GetAverage(TimeSpan range, DateTime? relativeTo = null) { if (this._counter.IsEmpty) - { return 0; - } - var lastTime = this._counter.Max(x => x.EventTime); - var start = lastTime.Subtract(range); + if (relativeTo == null) + relativeTo = DateTime.UtcNow; + + DateTime start = relativeTo.Value.Subtract(range); - var entries = this._counter.Where(x => (x.EventTime >= start) && (x.EventTime <= lastTime)); - return entries.Average(x => x.Elapsed.TotalMilliseconds); + var entries = this._counter.Where(x => (x.EventTime >= start) && (x.EventTime <= relativeTo)); + return entries.Average(x => x.ElapsedMilliseconds); } } } diff --git a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterCollection.cs b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterCollection.cs index 343fddf6..b48efd67 100644 --- a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterCollection.cs +++ b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterCollection.cs @@ -2,23 +2,41 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using StardewModdingAPI.Framework.Utilities; namespace StardewModdingAPI.Framework.PerformanceCounter { internal class PerformanceCounterCollection { - public IDictionary<string, PerformanceCounter> PerformanceCounters { get; } = new Dictionary<string, PerformanceCounter>(); - private DateTime StartDateTime = DateTime.Now; - private long CallCount; - public string Name { get; private set; } - public bool IsImportant { get; set; } - private readonly Stopwatch Stopwatch = new Stopwatch(); - private readonly PerformanceCounterManager PerformanceCounterManager; - public double MonitorThresholdMilliseconds { get; set; } - public bool Monitor { get; set; } + /// <summary>The list of triggered performance counters.</summary> private readonly List<AlertContext> TriggeredPerformanceCounters = new List<AlertContext>(); + /// <summary>The stopwatch used to track the invocation time.</summary> + private readonly Stopwatch InvocationStopwatch = new Stopwatch(); + + /// <summary>The performance counter manager.</summary> + private readonly PerformanceCounterManager PerformanceCounterManager; + + /// <summary>Holds the time to calculate the average calls per second.</summary> + private DateTime CallsPerSecondStart = DateTime.UtcNow; + + /// <summary>The number of invocations of this collection.</summary> + private long CallCount; + + public IDictionary<string, PerformanceCounter> PerformanceCounters { get; } = new Dictionary<string, PerformanceCounter>(); + + /// <summary>The name of this collection.</summary> + public string Name { get; } + + /// <summary>Flag if this collection is important (used for the console summary command).</summary> + public bool IsImportant { get; } + + /// <summary>The alert threshold in milliseconds.</summary> + public double AlertThresholdMilliseconds { get; set; } + + /// <summary>If alerting is enabled or not</summary> + public bool EnableAlerts { get; set; } + + public PerformanceCounterCollection(PerformanceCounterManager performanceCounterManager, string name, bool isImportant) { this.Name = name; @@ -32,111 +50,120 @@ namespace StardewModdingAPI.Framework.PerformanceCounter this.Name = name; } + /// <summary>Tracks a single invocation for a named source.</summary> + /// <param name="source">The name of the source.</param> + /// <param name="entry">The entry.</param> public void Track(string source, PerformanceCounterEntry entry) { if (!this.PerformanceCounters.ContainsKey(source)) - { this.PerformanceCounters.Add(source, new PerformanceCounter(this, source)); - } + this.PerformanceCounters[source].Add(entry); - if (this.Monitor) - { - this.TriggeredPerformanceCounters.Add(new AlertContext(source, entry.Elapsed.TotalMilliseconds)); - } + if (this.EnableAlerts) + this.TriggeredPerformanceCounters.Add(new AlertContext(source, entry.ElapsedMilliseconds)); } + /// <summary>Returns the average execution time for all non-game internal sources.</summary> + /// <returns>The average execution time in milliseconds</returns> public double GetModsAverageExecutionTime() { - return this.PerformanceCounters.Where(p => p.Key != Constants.GamePerformanceCounterName).Sum(p => p.Value.GetAverage()); + return this.PerformanceCounters.Where(p => + p.Key != Constants.GamePerformanceCounterName).Sum(p => p.Value.GetAverage()); } + /// <summary>Returns the overall average execution time.</summary> + /// <returns>The average execution time in milliseconds</returns> public double GetAverageExecutionTime() { return this.PerformanceCounters.Sum(p => p.Value.GetAverage()); } + /// <summary>Returns the average execution time for game-internal sources.</summary> + /// <returns>The average execution time in milliseconds</returns> public double GetGameAverageExecutionTime() { if (this.PerformanceCounters.TryGetValue(Constants.GamePerformanceCounterName, out PerformanceCounter gameExecTime)) - { return gameExecTime.GetAverage(); - } return 0; } + /// <summary>Begins tracking the invocation of this collection.</summary> public void BeginTrackInvocation() { - if (this.Monitor) + if (this.EnableAlerts) { this.TriggeredPerformanceCounters.Clear(); - this.Stopwatch.Reset(); - this.Stopwatch.Start(); + this.InvocationStopwatch.Reset(); + this.InvocationStopwatch.Start(); } this.CallCount++; - } + /// <summary>Ends tracking the invocation of this collection. Also records an alert if alerting is enabled + /// and the invocation time exceeds the threshold.</summary> public void EndTrackInvocation() { - if (!this.Monitor) return; + if (!this.EnableAlerts) return; - this.Stopwatch.Stop(); - if (this.Stopwatch.Elapsed.TotalMilliseconds >= this.MonitorThresholdMilliseconds) - { - this.AddAlert(this.Stopwatch.Elapsed.TotalMilliseconds, - this.MonitorThresholdMilliseconds, this.TriggeredPerformanceCounters); - } + this.InvocationStopwatch.Stop(); + + if (this.InvocationStopwatch.Elapsed.TotalMilliseconds >= this.AlertThresholdMilliseconds) + this.AddAlert(this.InvocationStopwatch.Elapsed.TotalMilliseconds, + this.AlertThresholdMilliseconds, this.TriggeredPerformanceCounters); } - public void AddAlert(double executionTimeMilliseconds, double threshold, List<AlertContext> alerts) + /// <summary>Adds an alert.</summary> + /// <param name="executionTimeMilliseconds">The execution time in milliseconds.</param> + /// <param name="thresholdMilliseconds">The configured threshold.</param> + /// <param name="alerts">The list of alert contexts.</param> + public void AddAlert(double executionTimeMilliseconds, double thresholdMilliseconds, List<AlertContext> alerts) { this.PerformanceCounterManager.AddAlert(new AlertEntry(this, executionTimeMilliseconds, - threshold, alerts)); + thresholdMilliseconds, alerts)); } - public void AddAlert(double executionTimeMilliseconds, double threshold, AlertContext alert) + /// <summary>Adds an alert for a single AlertContext</summary> + /// <param name="executionTimeMilliseconds">The execution time in milliseconds.</param> + /// <param name="thresholdMilliseconds">The configured threshold.</param> + /// <param name="alert">The context</param> + public void AddAlert(double executionTimeMilliseconds, double thresholdMilliseconds, AlertContext alert) { - this.AddAlert(executionTimeMilliseconds, threshold, new List<AlertContext>() {alert}); + this.AddAlert(executionTimeMilliseconds, thresholdMilliseconds, new List<AlertContext>() {alert}); } + /// <summary>Resets the calls per second counter.</summary> public void ResetCallsPerSecond() { this.CallCount = 0; - this.StartDateTime = DateTime.Now; + this.CallsPerSecondStart = DateTime.UtcNow; } + /// <summary>Resets all performance counters in this collection.</summary> public void Reset() { foreach (var i in this.PerformanceCounters) - { i.Value.Reset(); - i.Value.ResetPeak(); - } } + /// <summary>Resets the performance counter for a specific source.</summary> + /// <param name="source">The source name</param> public void ResetSource(string source) { foreach (var i in this.PerformanceCounters) - { if (i.Value.Source.Equals(source, StringComparison.InvariantCultureIgnoreCase)) - { i.Value.Reset(); - i.Value.ResetPeak(); - } - } } + /// <summary>Returns the average calls per second.</summary> + /// <returns>The average calls per second.</returns> public long GetAverageCallsPerSecond() { - long runtimeInSeconds = (long) DateTime.Now.Subtract(this.StartDateTime).TotalSeconds; + long runtimeInSeconds = (long) DateTime.UtcNow.Subtract(this.CallsPerSecondStart).TotalSeconds; - if (runtimeInSeconds == 0) - { - return 0; - } + if (runtimeInSeconds == 0) return 0; return this.CallCount / runtimeInSeconds; } diff --git a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterEntry.cs b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterEntry.cs index 8e156a32..a50fce7d 100644 --- a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterEntry.cs +++ b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterEntry.cs @@ -1,10 +1,14 @@ using System; -namespace StardewModdingAPI.Framework.Utilities +namespace StardewModdingAPI.Framework.PerformanceCounter { - public struct PerformanceCounterEntry + /// <summary>A single performance counter entry. Records the DateTime of the event and the elapsed millisecond.</summary> + internal struct PerformanceCounterEntry { + /// <summary>The DateTime when the entry occured.</summary> public DateTime EventTime; - public TimeSpan Elapsed; + + /// <summary>The elapsed milliseconds</summary> + public double ElapsedMilliseconds; } } diff --git a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterManager.cs b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterManager.cs index 9e77e2fa..d8f1f172 100644 --- a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterManager.cs +++ b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterManager.cs @@ -4,72 +4,62 @@ using System.Diagnostics; using System.Linq; using System.Text; using StardewModdingAPI.Framework.Events; -using StardewModdingAPI.Framework.Utilities; namespace StardewModdingAPI.Framework.PerformanceCounter { internal class PerformanceCounterManager { public HashSet<PerformanceCounterCollection> PerformanceCounterCollections = new HashSet<PerformanceCounterCollection>(); - public List<AlertEntry> Alerts = new List<AlertEntry>(); + + /// <summary>The recorded alerts.</summary> + private readonly List<AlertEntry> Alerts = new List<AlertEntry>(); + + /// <summary>The monitor for output logging.</summary> private readonly IMonitor Monitor; - private readonly Stopwatch Stopwatch = new Stopwatch(); + /// <summary>The invocation stopwatch.</summary> + private readonly Stopwatch InvocationStopwatch = new Stopwatch(); + + /// <summary>Constructs a performance counter manager.</summary> + /// <param name="monitor">The monitor for output logging.</param> public PerformanceCounterManager(IMonitor monitor) { this.Monitor = monitor; } + /// <summary>Resets all performance counters in all collections.</summary> public void Reset() { - foreach (var performanceCounter in this.PerformanceCounterCollections) - { - foreach (var eventPerformanceCounter in performanceCounter.PerformanceCounters) - { - eventPerformanceCounter.Value.Reset(); - } - } - } - - /// <summary>Print any queued messages.</summary> - public void PrintQueued() - { - if (this.Alerts.Count == 0) + foreach (var eventPerformanceCounter in + this.PerformanceCounterCollections.SelectMany(performanceCounter => performanceCounter.PerformanceCounters)) { - return; + eventPerformanceCounter.Value.Reset(); } - StringBuilder sb = new StringBuilder(); - - foreach (var alert in this.Alerts) - { - sb.AppendLine($"{alert.Collection.Name} took {alert.ExecutionTimeMilliseconds:F2}ms (exceeded threshold of {alert.Threshold:F2}ms)"); - - foreach (var context in alert.Context) - { - sb.AppendLine($"{context.Source}: {context.Elapsed:F2}ms"); - } - } - - this.Alerts.Clear(); - - this.Monitor.Log(sb.ToString(), LogLevel.Error); } + /// <summary>Begins tracking the invocation for a collection.</summary> + /// <param name="collectionName">The collection name</param> public void BeginTrackInvocation(string collectionName) { this.GetOrCreateCollectionByName(collectionName).BeginTrackInvocation(); } + /// <summary>Ends tracking the invocation for a collection.</summary> + /// <param name="collectionName"></param> public void EndTrackInvocation(string collectionName) { this.GetOrCreateCollectionByName(collectionName).EndTrackInvocation(); } - public void Track(string collectionName, string modName, Action action) + /// <summary>Tracks a single performance counter invocation in a specific collection.</summary> + /// <param name="collectionName">The name of the collection.</param> + /// <param name="sourceName">The name of the source.</param> + /// <param name="action">The action to execute and track invocation time for.</param> + public void Track(string collectionName, string sourceName, Action action) { DateTime eventTime = DateTime.UtcNow; - this.Stopwatch.Reset(); - this.Stopwatch.Start(); + this.InvocationStopwatch.Reset(); + this.InvocationStopwatch.Start(); try { @@ -77,75 +67,102 @@ namespace StardewModdingAPI.Framework.PerformanceCounter } finally { - this.Stopwatch.Stop(); + this.InvocationStopwatch.Stop(); - this.GetOrCreateCollectionByName(collectionName).Track(modName, new PerformanceCounterEntry + this.GetOrCreateCollectionByName(collectionName).Track(sourceName, new PerformanceCounterEntry { EventTime = eventTime, - Elapsed = this.Stopwatch.Elapsed + ElapsedMilliseconds = this.InvocationStopwatch.Elapsed.TotalMilliseconds }); } } - public PerformanceCounterCollection GetCollectionByName(string name) + /// <summary>Gets a collection by name.</summary> + /// <param name="name">The name of the collection.</param> + /// <returns>The collection or null if none was found.</returns> + private PerformanceCounterCollection GetCollectionByName(string name) { return this.PerformanceCounterCollections.FirstOrDefault(collection => collection.Name == name); } - public PerformanceCounterCollection GetOrCreateCollectionByName(string name) + /// <summary>Gets a collection by name and creates it if it doesn't exist.</summary> + /// <param name="name">The name of the collection.</param> + /// <returns>The collection.</returns> + private PerformanceCounterCollection GetOrCreateCollectionByName(string name) { PerformanceCounterCollection collection = this.GetCollectionByName(name); - if (collection == null) - { - collection = new PerformanceCounterCollection(this, name); - this.PerformanceCounterCollections.Add(collection); - } + if (collection != null) return collection; + + collection = new PerformanceCounterCollection(this, name); + this.PerformanceCounterCollections.Add(collection); return collection; } - public void ResetCategory(string name) + /// <summary>Resets the performance counters for a specific collection.</summary> + /// <param name="name">The collection name.</param> + public void ResetCollection(string name) { - foreach (var performanceCounterCollection in this.PerformanceCounterCollections) + foreach (PerformanceCounterCollection performanceCounterCollection in + this.PerformanceCounterCollections.Where(performanceCounterCollection => + performanceCounterCollection.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))) { - if (performanceCounterCollection.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) - { - performanceCounterCollection.ResetCallsPerSecond(); - performanceCounterCollection.Reset(); - } + performanceCounterCollection.ResetCallsPerSecond(); + performanceCounterCollection.Reset(); } } + /// <summary>Resets performance counters for a specific source.</summary> + /// <param name="name">The name of the source.</param> public void ResetSource(string name) { - foreach (var performanceCounterCollection in this.PerformanceCounterCollections) - { + foreach (PerformanceCounterCollection performanceCounterCollection in this.PerformanceCounterCollections) performanceCounterCollection.ResetSource(name); - } } + /// <summary>Print any queued alerts.</summary> + public void PrintQueuedAlerts() + { + if (this.Alerts.Count == 0) return; + + StringBuilder sb = new StringBuilder(); + + foreach (AlertEntry alert in this.Alerts) + { + sb.AppendLine($"{alert.Collection.Name} took {alert.ExecutionTimeMilliseconds:F2}ms (exceeded threshold of {alert.ThresholdMilliseconds:F2}ms)"); + foreach (AlertContext context in alert.Context.OrderByDescending(p => p.Elapsed)) + sb.AppendLine(context.ToString()); + } + + this.Alerts.Clear(); + this.Monitor.Log(sb.ToString(), LogLevel.Error); + } + + /// <summary>Adds an alert to the queue.</summary> + /// <param name="entry">The alert to add.</param> public void AddAlert(AlertEntry entry) { this.Alerts.Add(entry); } - public void InitializePerformanceCounterEvents(EventManager eventManager) + /// <summary>Initialized the default performance counter collections.</summary> + /// <param name="eventManager">The event manager.</param> + public void InitializePerformanceCounterCollections(EventManager eventManager) { this.PerformanceCounterCollections = new HashSet<PerformanceCounterCollection>() { new EventPerformanceCounterCollection(this, eventManager.MenuChanged, false), - // Rendering Events - new EventPerformanceCounterCollection(this, eventManager.Rendering, true), + new EventPerformanceCounterCollection(this, eventManager.Rendering, false), new EventPerformanceCounterCollection(this, eventManager.Rendered, true), - new EventPerformanceCounterCollection(this, eventManager.RenderingWorld, true), + new EventPerformanceCounterCollection(this, eventManager.RenderingWorld, false), new EventPerformanceCounterCollection(this, eventManager.RenderedWorld, true), - new EventPerformanceCounterCollection(this, eventManager.RenderingActiveMenu, true), + new EventPerformanceCounterCollection(this, eventManager.RenderingActiveMenu, false), new EventPerformanceCounterCollection(this, eventManager.RenderedActiveMenu, true), - new EventPerformanceCounterCollection(this, eventManager.RenderingHud, true), + new EventPerformanceCounterCollection(this, eventManager.RenderingHud, false), new EventPerformanceCounterCollection(this, eventManager.RenderedHud, true), new EventPerformanceCounterCollection(this, eventManager.WindowResized, false), @@ -172,19 +189,19 @@ namespace StardewModdingAPI.Framework.PerformanceCounter new EventPerformanceCounterCollection(this, eventManager.CursorMoved, true), new EventPerformanceCounterCollection(this, eventManager.MouseWheelScrolled, true), - new EventPerformanceCounterCollection(this, eventManager.PeerContextReceived, true), - new EventPerformanceCounterCollection(this, eventManager.ModMessageReceived, true), - new EventPerformanceCounterCollection(this, eventManager.PeerDisconnected, true), + new EventPerformanceCounterCollection(this, eventManager.PeerContextReceived, false), + new EventPerformanceCounterCollection(this, eventManager.ModMessageReceived, false), + new EventPerformanceCounterCollection(this, eventManager.PeerDisconnected, false), new EventPerformanceCounterCollection(this, eventManager.InventoryChanged, true), - new EventPerformanceCounterCollection(this, eventManager.LevelChanged, true), - new EventPerformanceCounterCollection(this, eventManager.Warped, true), + new EventPerformanceCounterCollection(this, eventManager.LevelChanged, false), + new EventPerformanceCounterCollection(this, eventManager.Warped, false), - new EventPerformanceCounterCollection(this, eventManager.LocationListChanged, true), - new EventPerformanceCounterCollection(this, eventManager.BuildingListChanged, true), - new EventPerformanceCounterCollection(this, eventManager.LocationListChanged, true), + new EventPerformanceCounterCollection(this, eventManager.LocationListChanged, false), + new EventPerformanceCounterCollection(this, eventManager.BuildingListChanged, false), + new EventPerformanceCounterCollection(this, eventManager.LocationListChanged, false), new EventPerformanceCounterCollection(this, eventManager.DebrisListChanged, true), new EventPerformanceCounterCollection(this, eventManager.LargeTerrainFeatureListChanged, true), - new EventPerformanceCounterCollection(this, eventManager.NpcListChanged, true), + new EventPerformanceCounterCollection(this, eventManager.NpcListChanged, false), new EventPerformanceCounterCollection(this, eventManager.ObjectListChanged, true), new EventPerformanceCounterCollection(this, eventManager.ChestInventoryChanged, true), new EventPerformanceCounterCollection(this, eventManager.TerrainFeatureListChanged, true), diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 5b0c6691..af7513e3 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -169,7 +169,7 @@ namespace StardewModdingAPI.Framework SCore.PerformanceCounterManager = new PerformanceCounterManager(this.Monitor); this.EventManager = new EventManager(this.Monitor, this.ModRegistry, SCore.PerformanceCounterManager); - SCore.PerformanceCounterManager.InitializePerformanceCounterEvents(this.EventManager); + SCore.PerformanceCounterManager.InitializePerformanceCounterCollections(this.EventManager); SCore.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry); diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 8aba9b57..266e2e6f 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -312,7 +312,7 @@ namespace StardewModdingAPI.Framework try { this.DeprecationManager.PrintQueued(); - this.PerformanceCounterManager.PrintQueued(); + this.PerformanceCounterManager.PrintQueuedAlerts(); /********* ** First-tick initialization |