Skip to content

Commit

Permalink
Debug Tools: Added io.ConfigDebugIsDebuggerPresent and Debug Break bu…
Browse files Browse the repository at this point in the history
…ttons. (#2673)
  • Loading branch information
ocornut committed Jan 10, 2024
1 parent 788bb58 commit fdf8d02
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 15 deletions.
8 changes: 7 additions & 1 deletion docs/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,17 @@ Other changes:
movements toward another parent BeginMenu() can keep the wrong child menu open. (#6671, #6926)
- Settings: Fixed an issue marking settings as dirty when merely clicking on a border or resize
grip without moving it.
- Debug Tools: Added io.ConfigDebugIsDebuggerPresent option. When enabled, this adds buttons
in various locations of Metrics/Debugger to manually break in debugger in selected places:
- Request a debug break in a Begin() call.
- Request a debug break in a ItemAdd() call via debug log and hovering 0xXXXXXX identifiers.
- Request a debug break in a BeginTable() call.
- Request a debug break in a SetShortcutRouting()/Shortcut() call. [Internal]
- Debug Tools: Metrics: Reorganize Tools menu.
- Debug Tools: Added DebugFlashStyleColor() to identify a style color. Added to Style Editor.
- Debug Tools: Debug Log: Hide its own clipper log to reduce noise in the output. (#5855)
- Debug Tools: Debug Log: Clicking any filter with SHIFT held enables it for 2 frames only,
making it easier when dealing with spammy logs. (#5855)
- Debug Tools: Metrics: Reorganize Tools menu.
- Misc: Added IMGUI_USER_H_FILENAME to change the path included when using
IMGUI_INCLUDE_IMGUI_USER_H. (#7039) [@bryceberger]
- Misc: Rework debug display of texture id in Metrics window to avoid compile-error when
Expand Down
106 changes: 101 additions & 5 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4787,7 +4787,10 @@ void ImGui::NewFrame()
UpdateDebugToolStackQueries();
UpdateDebugToolFlashStyleColor();
if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
{
g.DebugLocateId = 0;
g.DebugBreakInLocateId = false;
}
if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
{
DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
Expand Down Expand Up @@ -6372,6 +6375,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (window_just_created)
window = CreateNewWindow(name, flags);

// [DEBUG] Debug break requested by user
if (g.DebugBreakInWindow == window->ID)
IM_DEBUG_BREAK();

// Automatically disable manual moving/resizing when NoInputs is set
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
Expand Down Expand Up @@ -8365,6 +8372,10 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI
else
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used

// [DEBUG] Debug break requested by user
if (g.DebugBreakInShortcutRouting == key_chord)
IM_DEBUG_BREAK();

if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
if (g.NavWindow == NULL)
return false;
Expand Down Expand Up @@ -14013,6 +14024,9 @@ void ImGui::ShowMetricsWindow(bool* p_open)
return;
}

// [DEBUG] Clear debug breaks hooks after exactly one cycle.
DebugBreakClearData();

// Basic info
Text("Dear ImGui %s", GetVersion());
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
Expand Down Expand Up @@ -14072,13 +14086,14 @@ void ImGui::ShowMetricsWindow(bool* p_open)
// Tools
if (TreeNode("Tools"))
{
SeparatorText("Debug breaks");

// Debug Break features
// The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
DebugStartItemPicker();
SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
SameLine();
MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
DebugStartItemPicker();
Checkbox("Show \"Debug Break\" buttons in other sections", &g.IO.ConfigDebugIsDebuggerPresent);

SeparatorText("Visualize");

Expand Down Expand Up @@ -14166,7 +14181,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
{
static char buf[64] = "";
SetNextItemWidth(-FLT_MIN);
InputText("##Text", buf, IM_ARRAYSIZE(buf));
InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
if (buf[0] != 0)
DebugTextEncoding(buf);
}
Expand Down Expand Up @@ -14434,6 +14449,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
ImGuiKeyChord key_chord = key | routing_data->Mods;
Text("%s: 0x%08X", GetKeyChordName(key_chord, key_chord_name, IM_ARRAYSIZE(key_chord_name)), routing_data->RoutingCurr);
DebugLocateItemOnHover(routing_data->RoutingCurr);
if (g.IO.ConfigDebugIsDebuggerPresent)
{
SameLine();
if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
g.DebugBreakInShortcutRouting = key_chord;
}
idx = routing_data->NextEntryIndex;
}
}
Expand Down Expand Up @@ -14545,6 +14566,64 @@ void ImGui::ShowMetricsWindow(bool* p_open)
End();
}

void ImGui::DebugBreakClearData()
{
// Those fields are scattered in their respective subsystem to stay in hot-data locations
ImGuiContext& g = *GImGui;
g.DebugBreakInWindow = 0;
g.DebugBreakInTable = 0;
g.DebugBreakInShortcutRouting = ImGuiKey_None;
}

void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
{
if (!BeginItemTooltip())
return;
Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
Separator();
TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.");
Separator();
TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
EndTooltip();
}

// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
// In order to reduce interferences with the contents we are trying to debug into.
bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;

ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);

const ImRect bb(pos, pos + size);
ItemSize(size, 0.0f);
if (!ItemAdd(bb, id))
return false;

// WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
DebugBreakButtonTooltip(false, description_of_location);

ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImVec4 hsv;
ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);

RenderNavHighlight(bb, id);
RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);

IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
return pressed;
}

// [DEBUG] Display contents of Columns
void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
{
Expand Down Expand Up @@ -14883,6 +14962,9 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
if (window->MemoryCompacted)
TextDisabled("Note: some memory buffers have been compacted/freed.");

if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
g.DebugBreakInWindow = window->ID;

ImGuiWindowFlags flags = window->Flags;
DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
Expand Down Expand Up @@ -15117,6 +15199,7 @@ void ImGui::DebugLocateItem(ImGuiID target_id)
ImGuiContext& g = *GImGui;
g.DebugLocateId = target_id;
g.DebugLocateFrames = 2;
g.DebugBreakInLocateId = false;
}

void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
Expand All @@ -15126,11 +15209,24 @@ void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
ImGuiContext& g = *GImGui;
DebugLocateItem(target_id);
GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);

// Can't easily use a context menu here because it will mess with focus, active id etc.
if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
{
DebugBreakButtonTooltip(false, "in ItemAdd()");
if (IsKeyChordPressed(g.DebugBreakKeyChord))
g.DebugBreakInLocateId = true;
}
}

void ImGui::DebugLocateItemResolveWithLastItem()
{
ImGuiContext& g = *GImGui;

// [DEBUG] Debug break requested by user
if (g.DebugBreakInLocateId)
IM_DEBUG_BREAK();

ImGuiLastItemData item_data = g.LastItemData;
g.DebugLocateId = 0;
ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
Expand Down
20 changes: 13 additions & 7 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.90.1 WIP"
#define IMGUI_VERSION_NUM 19003
#define IMGUI_VERSION_NUM 19004
#define IMGUI_HAS_TABLE

/*
Expand Down Expand Up @@ -2089,16 +2089,22 @@ struct ImGuiIO
// Debug options
//------------------------------------------------------------------

// Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
// - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
// - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK().

// Tools to test correct Begin/End and BeginChild/EndChild behaviors.
// Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
// This is inconsistent with other BeginXXX functions and create confusion for many users.
// We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.
// - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
// - This is inconsistent with other BeginXXX functions and create confusion for many users.
// - We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.
bool ConfigDebugBeginReturnValueOnce;// = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows.
bool ConfigDebugBeginReturnValueLoop;// = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running.

// Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.
// Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
// Consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
// Option to deactivate io.AddFocusEvent(false) handling.
// - May facilitate interactions with a debugger when focus loss leads to clearing inputs data.
// - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing.

// Options to audit .ini data
Expand Down
4 changes: 3 additions & 1 deletion imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,10 +478,12 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Text("Also see Style->Rendering for rendering options.");

ImGui::SeparatorText("Debug");
ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
ImGui::BeginDisabled();
ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); // .
ImGui::EndDisabled();
ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover");
ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover.");
ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop);
ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss);
Expand Down
15 changes: 15 additions & 0 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,7 @@ struct ImGuiContext
ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow*
int WindowsActiveCount; // Number of unique windows submitted by frame
ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING)
ImGuiID DebugBreakInWindow; // Set to break in Begin() call.
ImGuiWindow* CurrentWindow; // Window being drawn into
ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs.
ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
Expand Down Expand Up @@ -1958,6 +1959,7 @@ struct ImGuiContext
ImGuiKeyRoutingTable KeysRoutingTable;
ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)
ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls.
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'
#endif
Expand Down Expand Up @@ -2074,6 +2076,7 @@ struct ImGuiContext

// Tables
ImGuiTable* CurrentTable;
ImGuiID DebugBreakInTable; // Set to break in BeginTable() call.
int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
ImVector<ImGuiTableTempData> TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting)
ImPool<ImGuiTable> Tables; // Persistent table data
Expand Down Expand Up @@ -2166,6 +2169,8 @@ struct ImGuiContext
ImGuiDebugLogFlags DebugLogAutoDisableFlags;
ImU8 DebugLogAutoDisableFrames;
ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
bool DebugBreakInLocateId; // Debug break in ItemAdd() call for g.DebugLocateId.
ImGuiKeyChord DebugBreakKeyChord; // = ImGuiKey_Pause
ImS8 DebugBeginReturnValueCullDepth; // Cycle between 0..9 then wrap around.
bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker())
ImU8 DebugItemPickerMouseButton;
Expand Down Expand Up @@ -2360,6 +2365,13 @@ struct ImGuiContext
DebugFlashStyleColorTime = 0.0f;
DebugFlashStyleColorIdx = ImGuiCol_COUNT;

// Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
DebugBreakInWindow = 0;
DebugBreakInTable = 0;
DebugBreakInLocateId = false;
DebugBreakKeyChord = ImGuiKey_Pause;
DebugBreakInShortcutRouting = ImGuiKey_None;

memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
FramerateSecPerFrameAccum = 0.0f;
Expand Down Expand Up @@ -3406,6 +3418,9 @@ namespace ImGui
IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time!
IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time!
IMGUI_API void DebugLocateItemResolveWithLastItem();
IMGUI_API void DebugBreakClearData();
IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location);
IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location);
inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);
Expand Down
Loading

0 comments on commit fdf8d02

Please sign in to comment.