hide assert logs that spam every frame automatically, add "verbose" mode

This commit is contained in:
goat 2024-12-26 22:10:10 +01:00
parent 10f4009a0c
commit 3f3f5f4b72
3 changed files with 87 additions and 37 deletions

View file

@ -15,7 +15,11 @@ namespace Dalamud.Interface.Internal.Asserts;
/// </summary> /// </summary>
internal class AssertHandler : IDisposable internal class AssertHandler : IDisposable
{ {
private const int HideThreshold = 20;
private const int HidePrintEvery = 500;
private readonly HashSet<string> ignoredAsserts = []; private readonly HashSet<string> ignoredAsserts = [];
private readonly Dictionary<string, uint> assertCounts = new();
// Store callback to avoid it from being GC'd // Store callback to avoid it from being GC'd
private readonly AssertCallbackDelegate callback; private readonly AssertCallbackDelegate callback;
@ -38,6 +42,12 @@ internal class AssertHandler : IDisposable
/// </summary> /// </summary>
public bool ShowAsserts { get; set; } public bool ShowAsserts { get; set; }
/// <summary>
/// Gets or sets a value indicating whether we want to hide asserts that occur frequently (= every update)
/// and whether we want to log callstacks.
/// </summary>
public bool EnableVerboseLogging { get; set; }
/// <summary> /// <summary>
/// Register the cimgui assert handler with the native library. /// Register the cimgui assert handler with the native library.
/// </summary> /// </summary>
@ -62,18 +72,43 @@ internal class AssertHandler : IDisposable
private void OnImGuiAssert(string expr, string file, int line) private void OnImGuiAssert(string expr, string file, int line)
{ {
Log.Warning("ImGui assertion failed: {Expr} at {File}:{Line}", expr, file, line);
if (!this.ShowAsserts)
return;
var key = $"{file}:{line}"; var key = $"{file}:{line}";
if (this.ignoredAsserts.Contains(key)) if (this.ignoredAsserts.Contains(key))
return; return;
// TODO: It would be nice to get unmanaged stack frames here, seems hard though without pulling that Lazy<string> stackTrace = new(() => new StackTrace(3).ToString());
// entire code in from the crash handler
var originalStackTrace = new StackTrace(2).ToString(); if (!this.EnableVerboseLogging)
{
if (this.assertCounts.TryGetValue(key, out var count))
{
this.assertCounts[key] = count + 1;
if (count <= HideThreshold || count % HidePrintEvery == 0)
{
Log.Warning("ImGui assertion failed: {Expr} at {File}:{Line} (repeated {Count} times)",
expr,
file,
line,
count);
}
}
else
{
this.assertCounts[key] = 1;
}
}
else
{
Log.Warning("ImGui assertion failed: {Expr} at {File}:{Line}\n{StackTrace:l}",
expr,
file,
line,
stackTrace.Value);
}
if (!this.ShowAsserts)
return;
string? GetRepoUrl() string? GetRepoUrl()
{ {
@ -132,7 +167,7 @@ internal class AssertHandler : IDisposable
{ {
CollapsedButtonText = "Show stack trace", CollapsedButtonText = "Show stack trace",
ExpandedButtonText = "Hide stack trace", ExpandedButtonText = "Hide stack trace",
Text = originalStackTrace, Text = stackTrace.Value,
}, },
Text = $"Some code in a plugin or Dalamud itself has caused an internal assertion in ImGui to fail. The game will most likely crash now.\n\n{expr}\nAt: {file}:{line}", Text = $"Some code in a plugin or Dalamud itself has caused an internal assertion in ImGui to fail. The game will most likely crash now.\n\n{expr}\nAt: {file}:{line}",
Icon = TaskDialogIcon.Warning, Icon = TaskDialogIcon.Warning,

View file

@ -867,10 +867,16 @@ internal class DalamudInterface : IInternalDisposableService
ImGui.Separator(); ImGui.Separator();
var val = this.interfaceManager.ShowAsserts; var showAsserts = this.interfaceManager.ShowAsserts;
if (ImGui.MenuItem("Enable Asserts", string.Empty, ref val)) if (ImGui.MenuItem("Enable assert popups", string.Empty, ref showAsserts))
{ {
this.interfaceManager.ShowAsserts = val; this.interfaceManager.ShowAsserts = showAsserts;
}
var enableVerboseAsserts = this.interfaceManager.EnableVerboseAssertLogging;
if (ImGui.MenuItem("Enable verbose assert logging", string.Empty, ref enableVerboseAsserts))
{
this.interfaceManager.EnableVerboseAssertLogging = enableVerboseAsserts;
} }
var assertsEnabled = this.configuration.ImGuiAssertsEnabledAtStartup ?? false; var assertsEnabled = this.configuration.ImGuiAssertsEnabledAtStartup ?? false;
@ -880,6 +886,8 @@ internal class DalamudInterface : IInternalDisposableService
this.configuration.QueueSave(); this.configuration.QueueSave();
} }
ImGui.Separator();
if (ImGui.MenuItem("Clear focus")) if (ImGui.MenuItem("Clear focus"))
{ {
ImGui.SetWindowFocus(null); ImGui.SetWindowFocus(null);

View file

@ -276,6 +276,13 @@ internal partial class InterfaceManager : IInternalDisposableService
set => this.assertHandler.ShowAsserts = value; set => this.assertHandler.ShowAsserts = value;
} }
/// <inheritdoc cref="AssertHandler.EnableVerboseLogging"/>
public bool EnableVerboseAssertLogging
{
get => this.assertHandler.EnableVerboseLogging;
set => this.assertHandler.EnableVerboseLogging = value;
}
/// <summary> /// <summary>
/// Dispose of managed and unmanaged resources. /// Dispose of managed and unmanaged resources.
/// </summary> /// </summary>