blob: 3cf668ee45852d370769f116fa94144f1c0b4826 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
using System;
using System.Collections.Generic;
using System.Linq;
using Harmony;
namespace StardewModdingAPI.Framework.PerformanceMonitoring
{
/// <summary>Tracks metadata about a particular code event.</summary>
internal class PerformanceCounter
{
/*********
** Fields
*********/
/// <summary>The size of the ring buffer.</summary>
private readonly int MaxEntries = 16384;
/// <summary>The collection to which this performance counter belongs.</summary>
private readonly PerformanceCounterCollection ParentCollection;
/// <summary>The performance counter entries.</summary>
private readonly Stack<PerformanceCounterEntry> Entries;
/// <summary>The entry with the highest execution time.</summary>
private PerformanceCounterEntry? PeakPerformanceCounterEntry;
/*********
** Accessors
*********/
/// <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 methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="parentCollection">The collection to which this performance counter belongs.</param>
/// <param name="source">The name of the source.</param>
public PerformanceCounter(PerformanceCounterCollection parentCollection, string source)
{
this.ParentCollection = parentCollection;
this.Source = source;
this.Entries = new Stack<PerformanceCounterEntry>(this.MaxEntries);
}
/// <summary>Add a performance counter entry to the list, update monitoring, and raise alerts if needed.</summary>
/// <param name="entry">The entry to add.</param>
public void Add(PerformanceCounterEntry entry)
{
// add entry
if (this.Entries.Count > this.MaxEntries)
this.Entries.Pop();
this.Entries.Add(entry);
// update metrics
if (this.PeakPerformanceCounterEntry == null || entry.ElapsedMilliseconds > this.PeakPerformanceCounterEntry.Value.ElapsedMilliseconds)
this.PeakPerformanceCounterEntry = entry;
// raise alert
if (this.EnableAlerts && entry.ElapsedMilliseconds > this.AlertThresholdMilliseconds)
this.ParentCollection.AddAlert(entry.ElapsedMilliseconds, this.AlertThresholdMilliseconds, new AlertContext(this.Source, entry.ElapsedMilliseconds));
}
/// <summary>Clear all performance counter entries and monitoring.</summary>
public void Reset()
{
this.Entries.Clear();
this.PeakPerformanceCounterEntry = null;
}
/// <summary>Get the peak entry.</summary>
public PerformanceCounterEntry? GetPeak()
{
return this.PeakPerformanceCounterEntry;
}
/// <summary>Get the entry with the highest execution time.</summary>
/// <param name="range">The time range to search.</param>
/// <param name="endTime">The end time for the <paramref name="range"/>, or null for the current time.</param>
public PerformanceCounterEntry? GetPeak(TimeSpan range, DateTime? endTime = null)
{
endTime ??= DateTime.UtcNow;
DateTime startTime = endTime.Value.Subtract(range);
return this.Entries
.Where(entry => entry.EventTime >= startTime && entry.EventTime <= endTime)
.OrderByDescending(x => x.ElapsedMilliseconds)
.FirstOrDefault();
}
/// <summary>Get the last entry added to the list.</summary>
public PerformanceCounterEntry? GetLastEntry()
{
if (this.Entries.Count == 0)
return null;
return this.Entries.Peek();
}
/// <summary>Get the average over a given time span.</summary>
/// <param name="range">The time range to search.</param>
/// <param name="endTime">The end time for the <paramref name="range"/>, or null for the current time.</param>
public double GetAverage(TimeSpan range, DateTime? endTime = null)
{
endTime ??= DateTime.UtcNow;
DateTime startTime = endTime.Value.Subtract(range);
double[] entries = this.Entries
.Where(entry => entry.EventTime >= startTime && entry.EventTime <= endTime)
.Select(p => p.ElapsedMilliseconds)
.ToArray();
return entries.Length > 0
? entries.Average()
: 0;
}
}
}
|