summaryrefslogtreecommitdiff
path: root/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/PerformanceCounterCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/PerformanceCounterCommand.cs')
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/PerformanceCounterCommand.cs655
1 files changed, 317 insertions, 338 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/PerformanceCounterCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/PerformanceCounterCommand.cs
index 2260296b..820f1939 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/PerformanceCounterCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/PerformanceCounterCommand.cs
@@ -7,24 +7,13 @@ using StardewModdingAPI.Framework.PerformanceCounter;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
{
- // ReSharper disable once UnusedType.Global
+ /// <summary>A set of commands which displays or configures performance monitoring.</summary>
internal class PerformanceCounterCommand : TrainerCommand
{
- /// <summary>The command names and aliases</summary>
- private readonly Dictionary<SubCommand, string[]> SubCommandNames = new Dictionary<SubCommand, string[]>()
- {
- {SubCommand.Summary, new[] {"summary", "sum", "s"}},
- {SubCommand.Detail, new[] {"detail", "d"}},
- {SubCommand.Reset, new[] {"reset", "r"}},
- {SubCommand.Trigger, new[] {"trigger"}},
- {SubCommand.Enable, new[] {"enable"}},
- {SubCommand.Disable, new[] {"disable"}},
- {SubCommand.Examples, new[] {"examples"}},
- {SubCommand.Concepts, new[] {"concepts"}},
- {SubCommand.Help, new[] {"help"}},
- };
-
- /// <summary>The available commands enum</summary>
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The available commands.</summary>
private enum SubCommand
{
Summary,
@@ -33,16 +22,16 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
Trigger,
Enable,
Disable,
- Examples,
- Help,
- Concepts,
- None
+ Help
}
+
+ /*********
+ ** Public methods
+ *********/
/// <summary>Construct an instance.</summary>
- public PerformanceCounterCommand() : base("pc", PerformanceCounterCommand.GetDescription())
- {
- }
+ public PerformanceCounterCommand()
+ : base("performance", PerformanceCounterCommand.GetDescription()) { }
/// <summary>Handle the command.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
@@ -50,92 +39,94 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
/// <param name="args">The command arguments.</param>
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- if (args.TryGet(0, "command", out string subCommandString, false))
+ // parse args
+ SubCommand subcommand = SubCommand.Summary;
{
- SubCommand subSubCommand = this.ParseCommandString(subCommandString);
-
- switch (subSubCommand)
+ if (args.TryGet(0, "command", out string subcommandStr, false) && !Enum.TryParse(subcommandStr, ignoreCase: true, out subcommand))
{
- case SubCommand.Summary:
- this.HandleSummarySubCommand(monitor, args);
- break;
- case SubCommand.Detail:
- this.HandleDetailSubCommand(monitor, args);
- break;
- case SubCommand.Reset:
- this.HandleResetSubCommand(monitor, args);
- break;
- case SubCommand.Trigger:
- this.HandleTriggerSubCommand(monitor, args);
- break;
- case SubCommand.Examples:
- break;
- case SubCommand.Concepts:
- this.OutputHelp(monitor, SubCommand.Concepts);
- break;
- case SubCommand.Enable:
- SCore.PerformanceCounterManager.EnableTracking = true;
- monitor.Log("Performance counter tracking is now enabled", LogLevel.Info);
- break;
- case SubCommand.Disable:
- SCore.PerformanceCounterManager.EnableTracking = false;
- monitor.Log("Performance counter tracking is now disabled", LogLevel.Info);
- break;
- case SubCommand.Help:
- if (args.TryGet(1, "command", out string commandString))
- this.OutputHelp(monitor, this.ParseCommandString(commandString));
- break;
- default:
- this.LogUsageError(monitor, $"Unknown command {subCommandString}");
- break;
+ this.LogUsageError(monitor, $"Unknown command {subcommandStr}");
+ return;
}
}
- else
- this.HandleSummarySubCommand(monitor, args);
+
+ // handle
+ switch (subcommand)
+ {
+ case SubCommand.Summary:
+ this.HandleSummarySubCommand(monitor, args);
+ break;
+
+ case SubCommand.Detail:
+ this.HandleDetailSubCommand(monitor, args);
+ break;
+
+ case SubCommand.Reset:
+ this.HandleResetSubCommand(monitor, args);
+ break;
+
+ case SubCommand.Trigger:
+ this.HandleTriggerSubCommand(monitor, args);
+ break;
+
+ case SubCommand.Enable:
+ SCore.PerformanceMonitor.EnableTracking = true;
+ monitor.Log("Performance counter tracking is now enabled", LogLevel.Info);
+ break;
+
+ case SubCommand.Disable:
+ SCore.PerformanceMonitor.EnableTracking = false;
+ monitor.Log("Performance counter tracking is now disabled", LogLevel.Info);
+ break;
+
+ case SubCommand.Help:
+ this.OutputHelp(monitor, args.TryGet(1, "command", out _) ? subcommand : null as SubCommand?);
+ break;
+
+ default:
+ this.LogUsageError(monitor, $"Unknown command {subcommand}");
+ break;
+ }
}
+
+ /*********
+ ** Private methods
+ *********/
/// <summary>Handles the summary sub command.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="args">The command arguments.</param>
private void HandleSummarySubCommand(IMonitor monitor, ArgumentParser args)
{
- IEnumerable<PerformanceCounterCollection> data;
-
if (!args.TryGet(1, "mode", out string mode, false))
- {
mode = "important";
- }
+ IEnumerable<PerformanceCounterCollection> data = SCore.PerformanceMonitor.GetCollections();
switch (mode)
{
case null:
case "important":
- data = SCore.PerformanceCounterManager.PerformanceCounterCollections.Where(p => p.IsImportant);
+ data = data.Where(p => p.IsPerformanceCritical);
break;
+
case "all":
- data = SCore.PerformanceCounterManager.PerformanceCounterCollections;
break;
+
default:
- data = SCore.PerformanceCounterManager.PerformanceCounterCollections.Where(p =>
- p.Name.ToLowerInvariant().Contains(mode.ToLowerInvariant()));
+ data = data.Where(p => p.Name.ToLowerInvariant().Contains(mode.ToLowerInvariant()));
break;
}
double? threshold = null;
-
if (args.TryGetDecimal(2, "threshold", out decimal t, false))
- {
- threshold = (double?) t;
- }
-
- StringBuilder sb = new StringBuilder();
+ threshold = (double?)t;
TimeSpan interval = TimeSpan.FromSeconds(60);
- sb.AppendLine($"Summary over the last {interval.TotalSeconds} seconds:");
- sb.AppendLine(this.GetTableString(
+ StringBuilder report = new StringBuilder();
+ report.AppendLine($"Summary over the last {interval.TotalSeconds} seconds:");
+ report.AppendLine(this.GetTableString(
data: data,
- header: new[] {"Collection", "Avg Calls/s", "Avg Exec Time (Game)", "Avg Exec Time (Mods)", "Avg Exec Time (Game+Mods)", "Peak Exec Time"},
+ header: new[] { "Collection", "Avg Calls/s", "Avg Exec Time (Game)", "Avg Exec Time (Mods)", "Avg Exec Time (Game+Mods)", "Peak Exec Time" },
getRow: item => new[]
{
item.Name,
@@ -148,7 +139,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
true
));
- monitor.Log(sb.ToString(), LogLevel.Info);
+ monitor.Log(report.ToString(), LogLevel.Info);
}
/// <summary>Handles the detail sub command.</summary>
@@ -163,26 +154,16 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
if (args.TryGet(1, "collection", out string collectionName))
{
- collections.AddRange(SCore.PerformanceCounterManager.PerformanceCounterCollections.Where(
- collection => collection.Name.ToLowerInvariant().Contains(collectionName.ToLowerInvariant())));
+ collections.AddRange(SCore.PerformanceMonitor.GetCollections().Where(collection => collection.Name.ToLowerInvariant().Contains(collectionName.ToLowerInvariant())));
- if (args.IsDecimal(2) && args.TryGetDecimal(2, "threshold", out decimal value, false))
- {
- thresholdMilliseconds = (double?) value;
- }
- else
- {
- if (args.TryGet(2, "source", out string sourceName, false))
- {
- sourceFilter = sourceName;
- }
- }
+ if (args.Count >= 2 && decimal.TryParse(args[2], out _) && args.TryGetDecimal(2, "threshold", out decimal value, false))
+ thresholdMilliseconds = (double?)value;
+ else if (args.TryGet(2, "source", out string sourceName, false))
+ sourceFilter = sourceName;
}
foreach (PerformanceCounterCollection c in collections)
- {
this.OutputPerformanceCollectionDetail(monitor, c, averageInterval, thresholdMilliseconds, sourceFilter);
- }
}
/// <summary>Handles the trigger sub command.</summary>
@@ -197,46 +178,44 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
case "list":
this.OutputAlertTriggers(monitor);
break;
+
case "collection":
if (args.TryGet(2, "name", out string collectionName))
{
if (args.TryGetDecimal(3, "threshold", out decimal threshold))
{
- if (args.TryGet(4, "source", out string source, false))
- {
- this.ConfigureAlertTrigger(monitor, collectionName, source, threshold);
- }
- else
- {
- this.ConfigureAlertTrigger(monitor, collectionName, null, threshold);
- }
+ if (!args.TryGet(4, "source", out string source, required: false))
+ source = null;
+ this.ConfigureAlertTrigger(monitor, collectionName, source, threshold);
}
}
break;
+
case "pause":
- SCore.PerformanceCounterManager.PauseAlerts = true;
- monitor.Log($"Alerts are now paused.", LogLevel.Info);
+ SCore.PerformanceMonitor.PauseAlerts = true;
+ monitor.Log("Alerts are now paused.", LogLevel.Info);
break;
+
case "resume":
- SCore.PerformanceCounterManager.PauseAlerts = false;
- monitor.Log($"Alerts are now resumed.", LogLevel.Info);
+ SCore.PerformanceMonitor.PauseAlerts = false;
+ monitor.Log("Alerts are now resumed.", LogLevel.Info);
break;
+
case "dump":
this.OutputAlertTriggers(monitor, true);
break;
+
case "clear":
this.ClearAlertTriggers(monitor);
break;
+
default:
this.LogUsageError(monitor, $"Unknown mode {mode}. See 'pc help trigger' for usage.");
break;
}
-
}
else
- {
this.OutputAlertTriggers(monitor);
- }
}
/// <summary>Sets up an an alert trigger.</summary>
@@ -246,7 +225,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
/// <param name="threshold">The trigger threshold, or 0 to remove.</param>
private void ConfigureAlertTrigger(IMonitor monitor, string collectionName, string sourceName, decimal threshold)
{
- foreach (PerformanceCounterCollection collection in SCore.PerformanceCounterManager.PerformanceCounterCollections)
+ foreach (PerformanceCounterCollection collection in SCore.PerformanceMonitor.GetCollections())
{
if (collection.Name.ToLowerInvariant().Equals(collectionName.ToLowerInvariant()))
{
@@ -255,8 +234,8 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
if (threshold != 0)
{
collection.EnableAlerts = true;
- collection.AlertThresholdMilliseconds = (double) threshold;
- monitor.Log($"Set up alert triggering for '{collectionName}' with '{this.FormatMilliseconds((double?) threshold)}'", LogLevel.Info);
+ collection.AlertThresholdMilliseconds = (double)threshold;
+ monitor.Log($"Set up alert triggering for '{collectionName}' with '{this.FormatMilliseconds((double?)threshold)}'", LogLevel.Info);
}
else
{
@@ -275,14 +254,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
if (threshold != 0)
{
performanceCounter.Value.EnableAlerts = true;
- performanceCounter.Value.AlertThresholdMilliseconds = (double) threshold;
- monitor.Log($"Set up alert triggering for '{sourceName}' in collection '{collectionName}' with '{this.FormatMilliseconds((double?) threshold)}", LogLevel.Info);
+ performanceCounter.Value.AlertThresholdMilliseconds = (double)threshold;
+ monitor.Log($"Set up alert triggering for '{sourceName}' in collection '{collectionName}' with '{this.FormatMilliseconds((double?)threshold)}", LogLevel.Info);
}
else
- {
performanceCounter.Value.EnableAlerts = false;
- }
-
return;
}
}
@@ -302,7 +278,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
private void ClearAlertTriggers(IMonitor monitor)
{
int clearedTriggers = 0;
- foreach (PerformanceCounterCollection collection in SCore.PerformanceCounterManager.PerformanceCounterCollections)
+ foreach (PerformanceCounterCollection collection in SCore.PerformanceMonitor.GetCollections())
{
if (collection.EnableAlerts)
{
@@ -329,92 +305,75 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
/// <param name="asDump">True to dump the triggers as commands.</param>
private void OutputAlertTriggers(IMonitor monitor, bool asDump = false)
{
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("Configured triggers:");
- sb.AppendLine();
- var collectionTriggers = new List<(string collectionName, double threshold)>();
- var sourceTriggers = new List<(string collectionName, string sourceName, double threshold)>();
+ StringBuilder report = new StringBuilder();
+ report.AppendLine("Configured triggers:");
+ report.AppendLine();
+ var collectionTriggers = new List<CollectionTrigger>();
+ var sourceTriggers = new List<SourceTrigger>();
- foreach (PerformanceCounterCollection collection in SCore.PerformanceCounterManager.PerformanceCounterCollections)
+ foreach (PerformanceCounterCollection collection in SCore.PerformanceMonitor.GetCollections())
{
if (collection.EnableAlerts)
- {
- collectionTriggers.Add((collection.Name, collection.AlertThresholdMilliseconds));
- }
+ collectionTriggers.Add(new CollectionTrigger(collection.Name, collection.AlertThresholdMilliseconds));
- sourceTriggers.AddRange(from performanceCounter in
- collection.PerformanceCounters where performanceCounter.Value.EnableAlerts
- select (collection.Name, performanceCounter.Value.Source, performanceCounter.Value.AlertThresholdMilliseconds));
+ sourceTriggers.AddRange(
+ from counter in collection.PerformanceCounters
+ where counter.Value.EnableAlerts
+ select new SourceTrigger(collection.Name, counter.Value.Source, counter.Value.AlertThresholdMilliseconds)
+ );
}
if (collectionTriggers.Count > 0)
{
- sb.AppendLine("Collection Triggers:");
- sb.AppendLine();
+ report.AppendLine("Collection Triggers:");
+ report.AppendLine();
if (asDump)
{
foreach (var item in collectionTriggers)
- {
- sb.AppendLine($"pc trigger {item.collectionName} {item.threshold}");
- }
+ report.AppendLine($"pc trigger {item.CollectionName} {item.Threshold}");
}
else
{
- sb.AppendLine(this.GetTableString(
+ report.AppendLine(this.GetTableString(
data: collectionTriggers,
- header: new[] {"Collection", "Threshold"},
- getRow: item => new[]
- {
- item.collectionName,
- this.FormatMilliseconds(item.threshold)
- },
+ header: new[] { "Collection", "Threshold" },
+ getRow: item => new[] { item.CollectionName, this.FormatMilliseconds(item.Threshold) },
true
));
}
- sb.AppendLine();
+ report.AppendLine();
}
else
- {
- sb.AppendLine("No collection triggers.");
- }
+ report.AppendLine("No collection triggers.");
if (sourceTriggers.Count > 0)
{
- sb.AppendLine("Source Triggers:");
- sb.AppendLine();
+ report.AppendLine("Source Triggers:");
+ report.AppendLine();
if (asDump)
{
- foreach (var item in sourceTriggers)
- {
- sb.AppendLine($"pc trigger {item.collectionName} {item.threshold} {item.sourceName}");
- }
+ foreach (SourceTrigger item in sourceTriggers)
+ report.AppendLine($"pc trigger {item.CollectionName} {item.Threshold} {item.SourceName}");
}
else
{
- sb.AppendLine(this.GetTableString(
+ report.AppendLine(this.GetTableString(
data: sourceTriggers,
- header: new[] {"Collection", "Source", "Threshold"},
- getRow: item => new[]
- {
- item.collectionName,
- item.sourceName,
- this.FormatMilliseconds(item.threshold)
- },
+ header: new[] { "Collection", "Source", "Threshold" },
+ getRow: item => new[] { item.CollectionName, item.SourceName, this.FormatMilliseconds(item.Threshold) },
true
));
}
- sb.AppendLine();
+ report.AppendLine();
}
else
- {
- sb.AppendLine("No source triggers.");
- }
+ report.AppendLine("No source triggers.");
- monitor.Log(sb.ToString(), LogLevel.Info);
+ monitor.Log(report.ToString(), LogLevel.Info);
}
/// <summary>Handles the reset sub command.</summary>
@@ -422,25 +381,25 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
/// <param name="args">The command arguments.</param>
private void HandleResetSubCommand(IMonitor monitor, ArgumentParser args)
{
- if (args.TryGet(1, "type", out string type, false, new []{"category", "source"}))
+ if (args.TryGet(1, "type", out string type, false, new[] { "category", "source" }))
{
args.TryGet(2, "name", out string name);
switch (type)
{
case "category":
- SCore.PerformanceCounterManager.ResetCollection(name);
+ SCore.PerformanceMonitor.ResetCollection(name);
monitor.Log($"All performance counters for category {name} are now cleared.", LogLevel.Info);
break;
case "source":
- SCore.PerformanceCounterManager.ResetSource(name);
+ SCore.PerformanceMonitor.ResetSource(name);
monitor.Log($"All performance counters for source {name} are now cleared.", LogLevel.Info);
break;
}
}
else
{
- SCore.PerformanceCounterManager.Reset();
+ SCore.PerformanceMonitor.Reset();
monitor.Log("All performance counters are now cleared.", LogLevel.Info);
}
}
@@ -455,7 +414,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
private void OutputPerformanceCollectionDetail(IMonitor monitor, PerformanceCounterCollection collection,
TimeSpan averageInterval, double? thresholdMilliseconds, string sourceFilter = null)
{
- StringBuilder sb = new StringBuilder($"Performance Counter for {collection.Name}:\n\n");
+ StringBuilder report = new StringBuilder($"Performance Counter for {collection.Name}:\n\n");
List<KeyValuePair<string, PerformanceCounter>> data = collection.PerformanceCounters.ToList();
@@ -466,15 +425,13 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
}
if (thresholdMilliseconds != null)
- {
data = data.Where(p => p.Value.GetAverage(averageInterval) >= thresholdMilliseconds).ToList();
- }
if (data.Any())
{
- sb.AppendLine(this.GetTableString(
+ report.AppendLine(this.GetTableString(
data: data,
- header: new[] {"Mod", $"Avg Exec Time (last {(int) averageInterval.TotalSeconds}s)", "Last Exec Time", "Peak Exec Time", $"Peak Exec Time (last {(int) averageInterval.TotalSeconds}s)"},
+ header: new[] { "Mod", $"Avg Exec Time (last {(int)averageInterval.TotalSeconds}s)", "Last Exec Time", "Peak Exec Time", $"Peak Exec Time (last {(int)averageInterval.TotalSeconds}s)" },
getRow: item => new[]
{
item.Key,
@@ -488,28 +445,13 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
}
else
{
- sb.Clear();
- sb.AppendLine($"Performance Counter for {collection.Name}: none.");
- }
-
- monitor.Log(sb.ToString(), LogLevel.Info);
- }
-
- /// <summary>Parses a command string and returns the associated command.</summary>
- /// <param name="commandString">The command string</param>
- /// <returns>The parsed command.</returns>
- private SubCommand ParseCommandString(string commandString)
- {
- foreach (var i in this.SubCommandNames.Where(i =>
- i.Value.Any(str => str.Equals(commandString, StringComparison.InvariantCultureIgnoreCase))))
- {
- return i.Key;
+ report.Clear();
+ report.AppendLine($"Performance Counter for {collection.Name}: none.");
}
- return SubCommand.None;
+ monitor.Log(report.ToString(), LogLevel.Info);
}
-
/// <summary>Formats the given milliseconds value into a string format. Optionally
/// allows a threshold to return "-" if the value is less than the threshold.</summary>
/// <param name="milliseconds">The milliseconds to format. Returns "-" if null</param>
@@ -518,175 +460,212 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
private string FormatMilliseconds(double? milliseconds, double? thresholdMilliseconds = null)
{
if (milliseconds == null || (thresholdMilliseconds != null && milliseconds < thresholdMilliseconds))
- {
return "-";
- }
- return ((double) milliseconds).ToString("F2");
+ return ((double)milliseconds).ToString("F2");
}
/// <summary>Shows detailed help for a specific sub command.</summary>
- /// <param name="monitor">The output monitor</param>
- /// <param name="subCommand">The sub command</param>
- private void OutputHelp(IMonitor monitor, SubCommand subCommand)
+ /// <param name="monitor">The output monitor.</param>
+ /// <param name="subcommand">The subcommand.</param>
+ private void OutputHelp(IMonitor monitor, SubCommand? subcommand)
{
- StringBuilder sb = new StringBuilder();
- sb.AppendLine();
+ StringBuilder report = new StringBuilder();
+ report.AppendLine();
- switch (subCommand)
+ switch (subcommand)
{
- case SubCommand.Concepts:
- sb.AppendLine("A performance counter is a metric which measures execution time. Each performance");
- sb.AppendLine("counter consists of:");
- sb.AppendLine();
- sb.AppendLine(" - A source, which typically is a mod or the game itself.");
- sb.AppendLine(" - A ring buffer which stores the data points (execution time and time when it was executed)");
- sb.AppendLine();
- sb.AppendLine("A set of performance counters is organized in a collection to group various areas.");
- sb.AppendLine("Per default, collections for all game events [1] are created.");
- sb.AppendLine();
- sb.AppendLine("Example:");
- sb.AppendLine();
- sb.AppendLine("The performance counter collection named 'Display.Rendered' contains one performance");
- sb.AppendLine("counters when the game executes the 'Display.Rendered' event, and one additional");
- sb.AppendLine("performance counter for each mod which handles the 'Display.Rendered' event.");
- sb.AppendLine();
- sb.AppendLine("[1] https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events");
- break;
case SubCommand.Detail:
- sb.AppendLine("Usage: pc detail <collection> <source>");
- sb.AppendLine(" pc detail <collection> <threshold>");
- sb.AppendLine();
- sb.AppendLine("Displays details for a specific collection.");
- sb.AppendLine();
- sb.AppendLine("Arguments:");
- sb.AppendLine(" <collection> Required. The full or partial name of the collection to display.");
- sb.AppendLine(" <source> Optional. The full or partial name of the source.");
- sb.AppendLine(" <threshold> Optional. The threshold in milliseconds. Any average execution time below that");
- sb.AppendLine(" threshold is not reported.");
- sb.AppendLine();
- sb.AppendLine("Examples:");
- sb.AppendLine("pc detail Display.Rendering Displays all performance counters for the 'Display.Rendering' collection");
- sb.AppendLine("pc detail Display.Rendering Pathoschild.ChestsAnywhere Displays the 'Display.Rendering' performance counter for 'Pathoschild.ChestsAnywhere'");
- sb.AppendLine("pc detail Display.Rendering 5 Displays the 'Display.Rendering' performance counters exceeding an average of 5ms");
+ report.AppendLine("Usage: pc detail <collection> <source>");
+ report.AppendLine(" pc detail <collection> <threshold>");
+ report.AppendLine();
+ report.AppendLine("Displays details for a specific collection.");
+ report.AppendLine();
+ report.AppendLine("Arguments:");
+ report.AppendLine(" <collection> Required. The full or partial name of the collection to display.");
+ report.AppendLine(" <source> Optional. The full or partial name of the source.");
+ report.AppendLine(" <threshold> Optional. The threshold in milliseconds. Any average execution time below that");
+ report.AppendLine(" threshold is not reported.");
+ report.AppendLine();
+ report.AppendLine("Examples:");
+ report.AppendLine("pc detail Display.Rendering Displays all performance counters for the 'Display.Rendering' collection");
+ report.AppendLine("pc detail Display.Rendering Pathoschild.ChestsAnywhere Displays the 'Display.Rendering' performance counter for 'Pathoschild.ChestsAnywhere'");
+ report.AppendLine("pc detail Display.Rendering 5 Displays the 'Display.Rendering' performance counters exceeding an average of 5ms");
break;
+
case SubCommand.Summary:
- sb.AppendLine("Usage: pc summary <mode|name> <threshold>");
- sb.AppendLine();
- sb.AppendLine("Displays the performance counter summary.");
- sb.AppendLine();
- sb.AppendLine("Arguments:");
- sb.AppendLine(" <mode> Optional. Defaults to 'important' if omitted. Specifies one of these modes:");
- sb.AppendLine(" - all Displays performance counters from all collections");
- sb.AppendLine(" - important Displays only important performance counter collections");
- sb.AppendLine();
- sb.AppendLine(" <name> Optional. Only shows performance counter collections matching the given name");
- sb.AppendLine(" <threshold> Optional. Hides the actual execution time if it is below this threshold");
- sb.AppendLine();
- sb.AppendLine("Examples:");
- sb.AppendLine("pc summary all Shows all events");
- sb.AppendLine("pc summary all 5 Shows all events");
- sb.AppendLine("pc summary Display.Rendering Shows only the 'Display.Rendering' collection");
+ report.AppendLine("Usage: pc summary <mode|name> <threshold>");
+ report.AppendLine();
+ report.AppendLine("Displays the performance counter summary.");
+ report.AppendLine();
+ report.AppendLine("Arguments:");
+ report.AppendLine(" <mode> Optional. Defaults to 'important' if omitted. Specifies one of these modes:");
+ report.AppendLine(" - all Displays performance counters from all collections");
+ report.AppendLine(" - important Displays only important performance counter collections");
+ report.AppendLine();
+ report.AppendLine(" <name> Optional. Only shows performance counter collections matching the given name");
+ report.AppendLine(" <threshold> Optional. Hides the actual execution time if it is below this threshold");
+ report.AppendLine();
+ report.AppendLine("Examples:");
+ report.AppendLine("pc summary all Shows all events");
+ report.AppendLine("pc summary all 5 Shows all events");
+ report.AppendLine("pc summary Display.Rendering Shows only the 'Display.Rendering' collection");
break;
+
case SubCommand.Trigger:
- sb.AppendLine("Usage: pc trigger <mode>");
- sb.AppendLine("Usage: pc trigger collection <collectionName> <threshold>");
- sb.AppendLine("Usage: pc trigger collection <collectionName> <threshold> <sourceName>");
- sb.AppendLine();
- sb.AppendLine("Manages alert triggers.");
- sb.AppendLine();
- sb.AppendLine("Arguments:");
- sb.AppendLine(" <mode> Optional. Specifies if a specific source or a specific collection should be triggered.");
- sb.AppendLine(" - list Lists current triggers");
- sb.AppendLine(" - collection Sets up a trigger for a collection");
- sb.AppendLine(" - clear Clears all trigger entries");
- sb.AppendLine(" - pause Pauses triggering of alerts");
- sb.AppendLine(" - resume Resumes triggering of alerts");
- sb.AppendLine(" - dump Dumps all triggers as commands for copy and paste");
- sb.AppendLine(" Defaults to 'list' if not specified.");
- sb.AppendLine();
- sb.AppendLine(" <collectionName> Required if the mode 'collection' is specified.");
- sb.AppendLine(" Specifies the name of the collection to be triggered. Must be an exact match.");
- sb.AppendLine();
- sb.AppendLine(" <sourceName> Optional. Specifies the name of a specific source. Must be an exact match.");
- sb.AppendLine();
- sb.AppendLine(" <threshold> Required if the mode 'collection' is specified.");
- sb.AppendLine(" Specifies the threshold in milliseconds (fractions allowed).");
- sb.AppendLine(" Specify '0' to remove the threshold.");
- sb.AppendLine();
- sb.AppendLine("Examples:");
- sb.AppendLine();
- sb.AppendLine("pc trigger collection Display.Rendering 10");
- sb.AppendLine(" Sets up an alert trigger which writes on the console if the execution time of all performance counters in");
- sb.AppendLine(" the 'Display.Rendering' collection exceed 10 milliseconds.");
- sb.AppendLine();
- sb.AppendLine("pc trigger collection Display.Rendering 5 Pathoschild.ChestsAnywhere");
- sb.AppendLine(" Sets up an alert trigger to write on the console if the execution time of Pathoschild.ChestsAnywhere in");
- sb.AppendLine(" the 'Display.Rendering' collection exceed 5 milliseconds.");
- sb.AppendLine();
- sb.AppendLine("pc trigger collection Display.Rendering 0");
- sb.AppendLine(" Removes the threshold previously defined from the collection. Note that source-specific thresholds are left intact.");
- sb.AppendLine();
- sb.AppendLine("pc trigger clear");
- sb.AppendLine(" Clears all previously setup alert triggers.");
+ report.AppendLine("Usage: pc trigger <mode>");
+ report.AppendLine("Usage: pc trigger collection <collectionName> <threshold>");
+ report.AppendLine("Usage: pc trigger collection <collectionName> <threshold> <sourceName>");
+ report.AppendLine();
+ report.AppendLine("Manages alert triggers.");
+ report.AppendLine();
+ report.AppendLine("Arguments:");
+ report.AppendLine(" <mode> Optional. Specifies if a specific source or a specific collection should be triggered.");
+ report.AppendLine(" - list Lists current triggers");
+ report.AppendLine(" - collection Sets up a trigger for a collection");
+ report.AppendLine(" - clear Clears all trigger entries");
+ report.AppendLine(" - pause Pauses triggering of alerts");
+ report.AppendLine(" - resume Resumes triggering of alerts");
+ report.AppendLine(" - dump Dumps all triggers as commands for copy and paste");
+ report.AppendLine(" Defaults to 'list' if not specified.");
+ report.AppendLine();
+ report.AppendLine(" <collectionName> Required if the mode 'collection' is specified.");
+ report.AppendLine(" Specifies the name of the collection to be triggered. Must be an exact match.");
+ report.AppendLine();
+ report.AppendLine(" <sourceName> Optional. Specifies the name of a specific source. Must be an exact match.");
+ report.AppendLine();
+ report.AppendLine(" <threshold> Required if the mode 'collection' is specified.");
+ report.AppendLine(" Specifies the threshold in milliseconds (fractions allowed).");
+ report.AppendLine(" Specify '0' to remove the threshold.");
+ report.AppendLine();
+ report.AppendLine("Examples:");
+ report.AppendLine();
+ report.AppendLine("pc trigger collection Display.Rendering 10");
+ report.AppendLine(" Sets up an alert trigger which writes on the console if the execution time of all performance counters in");
+ report.AppendLine(" the 'Display.Rendering' collection exceed 10 milliseconds.");
+ report.AppendLine();
+ report.AppendLine("pc trigger collection Display.Rendering 5 Pathoschild.ChestsAnywhere");
+ report.AppendLine(" Sets up an alert trigger to write on the console if the execution time of Pathoschild.ChestsAnywhere in");
+ report.AppendLine(" the 'Display.Rendering' collection exceed 5 milliseconds.");
+ report.AppendLine();
+ report.AppendLine("pc trigger collection Display.Rendering 0");
+ report.AppendLine(" Removes the threshold previously defined from the collection. Note that source-specific thresholds are left intact.");
+ report.AppendLine();
+ report.AppendLine("pc trigger clear");
+ report.AppendLine(" Clears all previously setup alert triggers.");
break;
+
case SubCommand.Reset:
- sb.AppendLine("Usage: pc reset <type> <name>");
- sb.AppendLine();
- sb.AppendLine("Resets performance counters.");
- sb.AppendLine();
- sb.AppendLine("Arguments:");
- sb.AppendLine(" <type> Optional. Specifies if a collection or source should be reset.");
- sb.AppendLine(" If omitted, all performance counters are reset.");
- sb.AppendLine();
- sb.AppendLine(" - source Clears performance counters for a specific source");
- sb.AppendLine(" - collection Clears performance counters for a specific collection");
- sb.AppendLine();
- sb.AppendLine(" <name> Required if a <type> is given. Specifies the name of either the collection");
- sb.AppendLine(" or the source. The name must be an exact match.");
- sb.AppendLine();
- sb.AppendLine("Examples:");
- sb.AppendLine("pc reset Resets all performance counters");
- sb.AppendLine("pc reset source Pathoschild.ChestsAnywhere Resets all performance for the source named Pathoschild.ChestsAnywhere");
- sb.AppendLine("pc reset collection Display.Rendering Resets all performance for the collection named Display.Rendering");
+ report.AppendLine("Usage: pc reset <type> <name>");
+ report.AppendLine();
+ report.AppendLine("Resets performance counters.");
+ report.AppendLine();
+ report.AppendLine("Arguments:");
+ report.AppendLine(" <type> Optional. Specifies if a collection or source should be reset.");
+ report.AppendLine(" If omitted, all performance counters are reset.");
+ report.AppendLine();
+ report.AppendLine(" - source Clears performance counters for a specific source");
+ report.AppendLine(" - collection Clears performance counters for a specific collection");
+ report.AppendLine();
+ report.AppendLine(" <name> Required if a <type> is given. Specifies the name of either the collection");
+ report.AppendLine(" or the source. The name must be an exact match.");
+ report.AppendLine();
+ report.AppendLine("Examples:");
+ report.AppendLine("pc reset Resets all performance counters");
+ report.AppendLine("pc reset source Pathoschild.ChestsAnywhere Resets all performance for the source named Pathoschild.ChestsAnywhere");
+ report.AppendLine("pc reset collection Display.Rendering Resets all performance for the collection named Display.Rendering");
break;
}
- sb.AppendLine();
- monitor.Log(sb.ToString(), LogLevel.Info);
+ report.AppendLine();
+ monitor.Log(report.ToString(), LogLevel.Info);
}
/// <summary>Get the command description.</summary>
private static string GetDescription()
{
- StringBuilder sb = new StringBuilder();
-
- sb.AppendLine("Displays and configures performance counters.");
- sb.AppendLine();
- sb.AppendLine("A performance counter records the invocation time of in-game events being");
- sb.AppendLine("processed by mods or the game itself. See 'concepts' for a detailed explanation.");
- sb.AppendLine();
- sb.AppendLine("Usage: pc <command> <action>");
- sb.AppendLine();
- sb.AppendLine("Commands:");
- sb.AppendLine();
- sb.AppendLine(" summary|sum|s Displays a summary of important or all collections");
- sb.AppendLine(" detail|d Shows performance counter information for a given collection");
- sb.AppendLine(" reset|r Resets the performance counters");
- sb.AppendLine(" trigger Configures alert triggers");
- sb.AppendLine(" enable Enables performance counter recording");
- sb.AppendLine(" disable Disables performance counter recording");
- sb.AppendLine(" examples Displays various examples");
- sb.AppendLine(" concepts Displays an explanation of the performance counter concepts");
- sb.AppendLine(" help Displays verbose help for the available commands");
- sb.AppendLine();
- sb.AppendLine("To get help for a specific command, use 'pc help <command>', for example:");
- sb.AppendLine("pc help summary");
- sb.AppendLine();
- sb.AppendLine("Defaults to summary if no command is given.");
- sb.AppendLine();
-
- return sb.ToString();
+ StringBuilder report = new StringBuilder();
+
+ report.AppendLine("Displays or configures performance monitoring for diagnose issues.");
+ report.AppendLine();
+ report.AppendLine("A 'performance counter' is a metric which measures execution time across a range of time for a source (e.g. a mod).");
+ report.AppendLine("A set of performance counters is organized in a collection to group various areas.");
+ report.AppendLine("For example, the performance counter collection named 'Display.Rendered' contains one performance");
+ report.AppendLine("counter when the game executes the 'Display.Rendered' event, and another counter for each mod which handles it.");
+ report.AppendLine();
+ report.AppendLine("Usage: pc <command> <action>");
+ report.AppendLine();
+ report.AppendLine("Commands:");
+ report.AppendLine();
+ report.AppendLine(" summary Show a summary of collections.");
+ report.AppendLine(" detail Show a summary for a given collection.");
+ report.AppendLine(" reset Reset all performance counters.");
+ report.AppendLine(" trigger Configure alert triggers.");
+ report.AppendLine(" enable Enable performance counter recording.");
+ report.AppendLine(" disable Disable performance counter recording.");
+ report.AppendLine(" help Show verbose help for the available commands.");
+ report.AppendLine();
+ report.AppendLine("To get help for a specific command, use 'pc help <command>', for example:");
+ report.AppendLine("pc help summary");
+ report.AppendLine();
+ report.AppendLine("Defaults to summary if no command is given.");
+ report.AppendLine();
+
+ return report.ToString();
+ }
+
+
+ /*********
+ ** Private models
+ *********/
+ /// <summary>An alert trigger for a collection.</summary>
+ private class CollectionTrigger
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The collection name.</summary>
+ public string CollectionName { get; }
+
+ /// <summary>The trigger threshold.</summary>
+ public double Threshold { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="collectionName">The collection name.</param>
+ /// <param name="threshold">The trigger threshold.</param>
+ public CollectionTrigger(string collectionName, double threshold)
+ {
+ this.CollectionName = collectionName;
+ this.Threshold = threshold;
+ }
+ }
+
+ /// <summary>An alert triggered for a source.</summary>
+ private class SourceTrigger : CollectionTrigger
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The source name.</summary>
+ public string SourceName { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="collectionName">The collection name.</param>
+ /// <param name="sourceName">The source name.</param>
+ /// <param name="threshold">The trigger threshold.</param>
+ public SourceTrigger(string collectionName, string sourceName, double threshold)
+ : base(collectionName, threshold)
+ {
+ this.SourceName = sourceName;
+ }
}
}
}