mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
hide assert logs that spam every frame automatically, add "verbose" mode
This commit is contained in:
parent
10f4009a0c
commit
3f3f5f4b72
3 changed files with 87 additions and 37 deletions
|
|
@ -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;
|
||||||
|
|
@ -37,7 +41,13 @@ internal class AssertHandler : IDisposable
|
||||||
/// Gets or sets a value indicating whether ImGui asserts should be shown to the user.
|
/// Gets or sets a value indicating whether ImGui asserts should be shown to the user.
|
||||||
/// </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()
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +143,7 @@ internal class AssertHandler : IDisposable
|
||||||
Text = "Break",
|
Text = "Break",
|
||||||
AllowCloseDialog = true,
|
AllowCloseDialog = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var disableButton = new TaskDialogButton
|
var disableButton = new TaskDialogButton
|
||||||
{
|
{
|
||||||
Text = "Disable for this session",
|
Text = "Disable for this session",
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
private bool isImPlotDrawDemoWindow = false;
|
private bool isImPlotDrawDemoWindow = false;
|
||||||
private bool isImGuiTestWindowsInMonospace = false;
|
private bool isImGuiTestWindowsInMonospace = false;
|
||||||
private bool isImGuiDrawMetricsWindow = false;
|
private bool isImGuiDrawMetricsWindow = false;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private DalamudInterface(
|
private DalamudInterface(
|
||||||
Dalamud dalamud,
|
Dalamud dalamud,
|
||||||
|
|
@ -112,7 +112,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
this.interfaceManager = interfaceManager;
|
this.interfaceManager = interfaceManager;
|
||||||
|
|
||||||
this.WindowSystem = new WindowSystem("DalamudCore");
|
this.WindowSystem = new WindowSystem("DalamudCore");
|
||||||
|
|
||||||
this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
|
this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
|
||||||
this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
|
this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
|
||||||
this.dataWindow = new DataWindow() { IsOpen = false };
|
this.dataWindow = new DataWindow() { IsOpen = false };
|
||||||
|
|
@ -193,7 +193,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
|
|
||||||
this.creditsDarkeningAnimation.Point1 = Vector2.Zero;
|
this.creditsDarkeningAnimation.Point1 = Vector2.Zero;
|
||||||
this.creditsDarkeningAnimation.Point2 = new Vector2(CreditsDarkeningMaxAlpha);
|
this.creditsDarkeningAnimation.Point2 = new Vector2(CreditsDarkeningMaxAlpha);
|
||||||
|
|
||||||
// This is temporary, until we know the repercussions of vtable hooking mode
|
// This is temporary, until we know the repercussions of vtable hooking mode
|
||||||
consoleManager.AddCommand(
|
consoleManager.AddCommand(
|
||||||
"dalamud.interface.swapchain_mode",
|
"dalamud.interface.swapchain_mode",
|
||||||
|
|
@ -212,14 +212,14 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
Log.Error("Unknown swapchain mode: {Mode}", mode);
|
Log.Error("Unknown swapchain mode: {Mode}", mode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private delegate nint CrashDebugDelegate(nint self);
|
private delegate nint CrashDebugDelegate(nint self);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of frames since Dalamud has loaded.
|
/// Gets the number of frames since Dalamud has loaded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -319,7 +319,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
this.pluginStatWindow.IsOpen = true;
|
this.pluginStatWindow.IsOpen = true;
|
||||||
this.pluginStatWindow.BringToFront();
|
this.pluginStatWindow.BringToFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the <see cref="PluginInstallerWindow"/> on the plugin installed.
|
/// Opens the <see cref="PluginInstallerWindow"/> on the plugin installed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -384,7 +384,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
this.profilerWindow.IsOpen = true;
|
this.profilerWindow.IsOpen = true;
|
||||||
this.profilerWindow.BringToFront();
|
this.profilerWindow.BringToFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the <see cref="HitchSettingsWindow"/>.
|
/// Opens the <see cref="HitchSettingsWindow"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -696,7 +696,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
|
|
||||||
ImGui.EndMenu();
|
ImGui.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
var logSynchronously = this.configuration.LogSynchronously;
|
var logSynchronously = this.configuration.LogSynchronously;
|
||||||
if (ImGui.MenuItem("Log Synchronously", null, ref logSynchronously))
|
if (ImGui.MenuItem("Log Synchronously", null, ref logSynchronously))
|
||||||
{
|
{
|
||||||
|
|
@ -788,14 +788,14 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
if (ImGui.BeginMenu("Crash game"))
|
if (ImGui.BeginMenu("Crash game"))
|
||||||
{
|
{
|
||||||
if (ImGui.MenuItem("Access Violation"))
|
if (ImGui.MenuItem("Access Violation"))
|
||||||
{
|
{
|
||||||
Marshal.ReadByte(IntPtr.Zero);
|
Marshal.ReadByte(IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Set UiModule to NULL"))
|
if (ImGui.MenuItem("Set UiModule to NULL"))
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
|
|
@ -804,7 +804,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
framework->UIModule = (UIModule*)0;
|
framework->UIModule = (UIModule*)0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Set UiModule to invalid ptr"))
|
if (ImGui.MenuItem("Set UiModule to invalid ptr"))
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
|
|
@ -813,7 +813,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
framework->UIModule = (UIModule*)0x12345678;
|
framework->UIModule = (UIModule*)0x12345678;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Deref nullptr in Hook"))
|
if (ImGui.MenuItem("Deref nullptr in Hook"))
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
|
|
@ -834,7 +834,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
ImGui.PopStyleVar();
|
ImGui.PopStyleVar();
|
||||||
ImGui.PopStyleVar();
|
ImGui.PopStyleVar();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndMenu();
|
ImGui.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -850,7 +850,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
{
|
{
|
||||||
this.OpenBranchSwitcher();
|
this.OpenBranchSwitcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.MenuItem(this.dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false);
|
ImGui.MenuItem(this.dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false);
|
||||||
ImGui.MenuItem($"D: {Util.GetScmVersion()} CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.ThisAssembly.Git.Commits}]", false);
|
ImGui.MenuItem($"D: {Util.GetScmVersion()} CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.ThisAssembly.Git.Commits}]", false);
|
||||||
ImGui.MenuItem($"CLR: {Environment.Version}", false);
|
ImGui.MenuItem($"CLR: {Environment.Version}", false);
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -927,7 +935,7 @@ internal class DalamudInterface : IInternalDisposableService
|
||||||
{
|
{
|
||||||
this.configuration.ShowDevBarInfo = !this.configuration.ShowDevBarInfo;
|
this.configuration.ShowDevBarInfo = !this.configuration.ShowDevBarInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
if (ImGui.MenuItem("Show loading window"))
|
if (ImGui.MenuItem("Show loading window"))
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ internal partial class InterfaceManager : IInternalDisposableService
|
||||||
public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f;
|
public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f;
|
||||||
|
|
||||||
private static readonly ModuleLog Log = new("INTERFACE");
|
private static readonly ModuleLog Log = new("INTERFACE");
|
||||||
|
|
||||||
private readonly ConcurrentBag<IDeferredDisposable> deferredDisposeTextures = new();
|
private readonly ConcurrentBag<IDeferredDisposable> deferredDisposeTextures = new();
|
||||||
private readonly ConcurrentBag<IDisposable> deferredDisposeDisposables = new();
|
private readonly ConcurrentBag<IDisposable> deferredDisposeDisposables = new();
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ internal partial class InterfaceManager : IInternalDisposableService
|
||||||
|
|
||||||
private readonly ConcurrentQueue<Action> runBeforeImGuiRender = new();
|
private readonly ConcurrentQueue<Action> runBeforeImGuiRender = new();
|
||||||
private readonly ConcurrentQueue<Action> runAfterImGuiRender = new();
|
private readonly ConcurrentQueue<Action> runAfterImGuiRender = new();
|
||||||
|
|
||||||
private readonly AssertHandler assertHandler = new();
|
private readonly AssertHandler assertHandler = new();
|
||||||
|
|
||||||
private RawDX11Scene? scene;
|
private RawDX11Scene? scene;
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -809,14 +816,14 @@ internal partial class InterfaceManager : IInternalDisposableService
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
||||||
_ = this.dalamudAtlas.BuildFontsAsync();
|
_ = this.dalamudAtlas.BuildFontsAsync();
|
||||||
|
|
||||||
SwapChainHelper.BusyWaitForGameDeviceSwapChain();
|
SwapChainHelper.BusyWaitForGameDeviceSwapChain();
|
||||||
var swapChainDesc = default(DXGI_SWAP_CHAIN_DESC);
|
var swapChainDesc = default(DXGI_SWAP_CHAIN_DESC);
|
||||||
if (SwapChainHelper.GameDeviceSwapChain->GetDesc(&swapChainDesc).SUCCEEDED)
|
if (SwapChainHelper.GameDeviceSwapChain->GetDesc(&swapChainDesc).SUCCEEDED)
|
||||||
this.gameWindowHandle = swapChainDesc.OutputWindow;
|
this.gameWindowHandle = swapChainDesc.OutputWindow;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -959,7 +966,7 @@ internal partial class InterfaceManager : IInternalDisposableService
|
||||||
|
|
||||||
switch (this.dalamudConfiguration.SwapChainHookMode)
|
switch (this.dalamudConfiguration.SwapChainHookMode)
|
||||||
{
|
{
|
||||||
case SwapChainHelper.HookMode.ByteCode:
|
case SwapChainHelper.HookMode.ByteCode:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
Log.Information("Hooking using bytecode...");
|
Log.Information("Hooking using bytecode...");
|
||||||
|
|
@ -1149,7 +1156,7 @@ internal partial class InterfaceManager : IInternalDisposableService
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error(ex, "Error when invoking global Draw");
|
Log.Error(ex, "Error when invoking global Draw");
|
||||||
|
|
||||||
// We should always handle this in the callbacks.
|
// We should always handle this in the callbacks.
|
||||||
Util.Fatal("An internal error occurred while drawing the Dalamud UI and the game must close.\nPlease report this error.", "Dalamud");
|
Util.Fatal("An internal error occurred while drawing the Dalamud UI and the game must close.\nPlease report this error.", "Dalamud");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue