-
-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Breakpoint improvements #1809
base: main
Are you sure you want to change the base?
Breakpoint improvements #1809
Conversation
rocketz
commented
Nov 24, 2024
- Cleaned up UI. New breakpoints are now handled in a pop-up dialog
- Breakpoint are now shown in a table.
- You can easily see and edit breakpoint names in the table
- Clicking on the address will take you there (in asm or memory view)
- Enable all/disable all/delete all buttons
- Context menu in assembly view to quickly create read/write breakpoints on the address the instruction reads/writes
- New breakpoint types:
- On changes. WIll only break when the value is written to with a change. The new value written is the new value tested against
- Lower than
- Higher than
- Equal
- Range
- When a code breakpoint is hit it is highlighted in red in breakpoint table
- Cleaned up UI. New breakpoints are now handled in a pop-up dialog - Breakpoint are now shown in a table. - You can easily see and edit breakpoint names in the table - Clicking on the address will take you there (in asm or memory view) - Enable all/disable all/delete all buttons - Context menu in assembly view to quickly create read/write breakpoints on the address the instruction reads/writes - New breakpoint types: * On changes. WIll only break when the value is written to with a change. The new value written is the new value tested against * Lower than * Higher than * Equal * Range - When a code breakpoint is hit it is highlighted in red in breakpoint table
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this!
It's Thanksgiving break over here, so I'm a bit slower than usual (and I'm already not too fast to begin with), so, sorry about the delay here.
I've left a few comments, but it looks good!
#include "fmt/format.h" | ||
#include "imgui.h" | ||
#include "support/imgui-helpers.h" | ||
|
||
static ImVec4 s_currentColor = ImColor(0xff, 0xeb, 0x3b); | ||
// Note: We ignore SWL and SWR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of a blind spot overall. It feels like we can properly support swl/swr. I'll try and work this out a bit.
MemVal final = {}; | ||
switch (width) { | ||
case 1: { | ||
uint8_t val = PCSX::g_emulator->m_mem->read8(addr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't use read8/read16/read32 for the purpose of the debugger, as it may advance internal state machines on things like the cd-rom controller or the pads. This one's a bit difficult.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I see.. maybe we need a more direct access?
src/gui/widgets/breakpoints.cc
Outdated
doBreak = curVal != self->conditionData(); | ||
if (doBreak) | ||
{ | ||
// TODO: can't update since 'self' is const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you fill in what'd need to be updated still? It's probably doable to make everything work properly still. It may be fine to also change the upper API generally speaking, but I'd rather we discuss this a bit. We could also use mutable
within reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually have this working, but I messed up with some stashes. Will bring those changes in.
src/gui/widgets/breakpoints.cc
Outdated
// than that they want to use the same label twice | ||
m_bpLabelString[0] = 0; | ||
|
||
static int breakCondition2 = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we name this breakConditionImGuiValue
instead or something? The 2
suffix doesn't really explain why it's there in the first place.
My pleasure!
No worries. You shouldn't waste valuable turkey-time on this :)
Cool. I'll fix a few things though. |
Updating from main as there was some CI breakages, and we can see better where we're at here. |
Right so there's still some breakages on Linux: src/gui/widgets/breakpoints.cc:242:32: error: format not a string literal and no format arguments [-Werror=format-security] Needs to use |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1809 +/- ##
==========================================
- Coverage 9.73% 9.19% -0.54%
==========================================
Files 451 467 +16
Lines 136014 144109 +8095
==========================================
+ Hits 13238 13250 +12
- Misses 122776 130859 +8083 ☔ View full report in Codecov by Sentry. |
Alright, let's try and get that in... |
WalkthroughThe pull request introduces enhancements to the debugging system in the PCSX emulator. The changes focus on expanding breakpoint functionality by adding a new Changes
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/gui/widgets/assembly.cc (1)
343-348
: Creating memory breakpoints directly from the Assembly view.
This is a helpful addition for quick debugging. Consider eventually offering a condition selection (similar to code breakpoints) to match the newBreakpointCondition
features. Additionally, watch the complexity of this function as it grows.🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)
[warning] 343-348: ❌ New issue: Complex Method
PCSX::Widgets::Assembly::addMemoryEditorContext has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/core/debug.h
(5 hunks)src/gui/widgets/assembly.cc
(1 hunks)src/gui/widgets/breakpoints.cc
(1 hunks)src/gui/widgets/breakpoints.h
(1 hunks)
🧰 Additional context used
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)
src/gui/widgets/assembly.cc
[warning] 343-348: ❌ New issue: Complex Method
PCSX::Widgets::Assembly::addMemoryEditorContext has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
src/gui/widgets/breakpoints.cc
[warning] 94-425: ❌ Getting worse: Complex Method
PCSX::Widgets::Breakpoints::draw increases in cyclomatic complexity from 25 to 70, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
[warning] 94-425: ❌ Getting worse: Bumpy Road Ahead
PCSX::Widgets::Breakpoints::draw increases from 5 to 12 logical blocks with deeply nested code, threshold is one single block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.
[warning] 94-425: ❌ New issue: Deep, Nested Complexity
PCSX::Widgets::Breakpoints::draw has a nested complexity depth of 6, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.
🔇 Additional comments (10)
src/gui/widgets/breakpoints.cc (5)
21-22
: No issues found in the new includes.
Everything looks good and appears necessary for the features implemented.
27-34
: Revisit partial store instructions (SWL/SWR).
This code continues to skip partial store instructions like SWL and SWR. As noted in a past review, proper handling of these instructions would be beneficial to accurately capture the value about to be written.
36-50
: Function for condition names looks good.
The switch adequately covers all known breakpoint conditions, returning translated strings.
52-85
: Use of read8/read16/read32 in debugging context may cause side effects.
Prior comments warned that these read methods can advance certain emulator internal states (e.g., CD-ROM, controllers). Consider adding a safer, direct-read mechanism if these side effects are undesirable in debugging scenarios.
88-89
: Color constants defined.
No issues found. The naming is clear, and the colors are easily distinguishable.src/gui/widgets/breakpoints.h (1)
44-44
: Verify the reduced buffer size for breakpoint addresses.
Changing from 20 to 9 bytes exactly accommodates up to 8 hex characters plus the null terminator. If the user ever attempts a prefix (e.g., "0x") or inputs more digits, it may cause partial or invalid reads. Confirm the UI’s text fields and error handling align with this constraint.src/core/debug.h (4)
68-68
: Allowing a non-const Breakpoint pointer.
Switching to a non-const pointer is consistent with updatingconditionData()
or other state within the invoker. Looks correct.
80-83
: New getters and setters for condition and conditionData.
These are straightforward and align with the new breakpoint condition functionality.
101-102
: Defaulting toBreakpointCondition::Always
.
No issues here. Initializing the condition to Always is a sensible default.
165-165
: Mass removal of breakpoints.
TheremoveAllBreakpoints()
method is a logical addition to cleanly clear all breakpoints in one step.
src/gui/widgets/breakpoints.cc
Outdated
default: | ||
case Debug::BreakpointCondition::Always: | ||
break; | ||
case Debug::BreakpointCondition::Equal: | ||
conditionData = conditionVal; | ||
break; | ||
case Debug::BreakpointCondition::Less: | ||
case Debug::BreakpointCondition::Greater: | ||
case Debug::BreakpointCondition::Change: | ||
conditionData = conditionVal; | ||
break; | ||
} | ||
|
||
Debug::Breakpoint* bp = debugger->addBreakpoint(breakpointAddress, bpType, | ||
(bpType == Debug::BreakpointType::Exec) ? 4 : actualWidth, _("GUI"), | ||
m_bpLabelString, invoker); | ||
|
||
bp->setCondition(breakCondition); | ||
bp->setConditionData(conditionData); | ||
|
||
// we clear the label string because it seems more likely that the user would forget to clear the | ||
// field than that they want to use the same label twice | ||
m_bpLabelString[0] = 0; | ||
ImGui::CloseCurrentPopup(); | ||
} | ||
} | ||
|
||
ImGui::EndPopup(); | ||
} | ||
ImGui::End(); | ||
|
||
if (!editorToOpen.empty()) { | ||
ImGui::OpenPopup(editorToOpen.c_str()); | ||
if (!tree.empty()) { | ||
ImGui::SameLine(); | ||
if (ImGui::Button(_("Activate All"))) { | ||
for (auto bp = tree.begin(); bp != tree.end(); bp++) { | ||
bp->enable(); | ||
} | ||
} | ||
|
||
ImGui::SameLine(); | ||
if (ImGui::Button(_("Deactivate All"))) { | ||
for (auto bp = tree.begin(); bp != tree.end(); bp++) { | ||
bp->disable(); | ||
} | ||
} | ||
|
||
ImGui::SameLine(); | ||
if (ImGui::Button(_("Delete All"))) { | ||
ImGui::OpenPopup("delbp_popup"); | ||
} | ||
if (ImGui::BeginPopup("delbp_popup")) { | ||
ImGui::TextUnformatted(_("Delete all Breakpoints?")); | ||
if (ImGui::Button(_("Delete"))) { | ||
g_emulator->m_debug->removeAllBreakpoints(); | ||
} | ||
ImGui::EndPopup(); | ||
} | ||
} | ||
counter = 0; | ||
for (auto bp = tree.begin(); bp != tree.end(); bp++, counter++) { | ||
showEditLabelPopup(&*bp, counter); | ||
|
||
ImGui::Separator(); | ||
if (ImGui::TreeNode(_("Execution Map"))) { | ||
if (ImGui::Button(_("Clear maps"))) { | ||
debugger->clearMaps(); | ||
} | ||
ImGuiHelpers::ShowHelpMarker( | ||
_("The mapping feature is a simple concept, but requires some amount of explanation. See the documentation " | ||
"website for more details, in the Misc Features subsection of the Debugging section.")); | ||
ImGui::Checkbox(_("Map execution"), &debugger->m_mapping_e); | ||
ImGui::Checkbox(_("Map byte reads "), &debugger->m_mapping_r8); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Map half reads "), &debugger->m_mapping_r16); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Map word reads "), &debugger->m_mapping_r32); | ||
ImGui::Checkbox(_("Map byte writes "), &debugger->m_mapping_w8); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Map half writes "), &debugger->m_mapping_w16); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Map word writes "), &debugger->m_mapping_w32); | ||
ImGui::Separator(); | ||
ImGui::Checkbox(_("Break on execution map"), &debugger->m_breakmp_e); | ||
ImGui::Checkbox(_("Break on byte read map "), &debugger->m_breakmp_r8); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Break on half read map "), &debugger->m_breakmp_r16); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Break on word read map "), &debugger->m_breakmp_r32); | ||
ImGui::Checkbox(_("Break on byte write map"), &debugger->m_breakmp_w8); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Break on half write map"), &debugger->m_breakmp_w16); | ||
ImGui::SameLine(); | ||
ImGui::Checkbox(_("Break on word write map"), &debugger->m_breakmp_w32); | ||
ImGui::TreePop(); | ||
} | ||
|
||
if (toErase) g_emulator->m_debug->removeBreakpoint(toErase); | ||
ImGui::End(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
High cyclomatic complexity in Breakpoints::draw
.
Static analysis indicates the method’s complexity has grown significantly, making it challenging to maintain and extend. Consider refactoring into smaller sub-functions (e.g., table creation, row rendering, condition popups, etc.) for improved readability and maintainability.
🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)
[warning] 94-425: ❌ Getting worse: Complex Method
PCSX::Widgets::Breakpoints::draw increases in cyclomatic complexity from 25 to 70, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
[warning] 94-425: ❌ Getting worse: Bumpy Road Ahead
PCSX::Widgets::Breakpoints::draw increases from 5 to 12 logical blocks with deeply nested code, threshold is one single block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.
[warning] 94-425: ❌ New issue: Deep, Nested Complexity
PCSX::Widgets::Breakpoints::draw has a nested complexity depth of 6, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/core/debug.h
(5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
- GitHub Check: toolchain
- GitHub Check: asan
- GitHub Check: cross-arm64
- GitHub Check: aur-build
- GitHub Check: build-openbios
- GitHub Check: macos-build-and-test-toolchain
- GitHub Check: build
- GitHub Check: coverage
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (2)
src/core/debug.h (2)
Line range hint
40-168
: Verify condition checking implementationThe header defines the structure for conditional breakpoints, but the actual condition checking logic needs to be implemented in the source file. Please ensure:
- The
triggerBP
method checks conditions before triggering- For
Change
condition, previous values are tracked- For
Range
condition, both bounds are properly handledLet's verify the implementation:
70-71
:⚠️ Potential issueUpdate default lambda to match non-const Breakpoint parameter
The
BreakpointInvoker
type now uses non-constBreakpoint*
, but the default lambda inaddBreakpoint
still usesconst Breakpoint*
. This inconsistency should be fixed.Let's verify all usages of the invoker:
src/core/debug.h
Outdated
BreakpointCondition m_condition = BreakpointCondition::Always; | ||
uint32_t m_conditionData; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize m_conditionData member
The m_conditionData
member is not initialized, which could lead to undefined behavior. Initialize it to 0 since it's unused for the default Always
condition.
BreakpointCondition m_condition = BreakpointCondition::Always;
- uint32_t m_conditionData;
+ uint32_t m_conditionData = 0;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
BreakpointCondition m_condition = BreakpointCondition::Always; | |
uint32_t m_conditionData; | |
BreakpointCondition m_condition = BreakpointCondition::Always; | |
uint32_t m_conditionData = 0; |
src/core/debug.h
Outdated
@@ -158,6 +165,7 @@ | |||
if (m_lastBP == bp) m_lastBP = nullptr; | |||
delete const_cast<Breakpoint*>(bp); | |||
} | |||
void removeAllBreakpoints() { m_breakpoints.clear(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reset m_lastBP in removeAllBreakpoints
The removeAllBreakpoints
method should reset m_lastBP
to maintain consistency with removeBreakpoint
.
- void removeAllBreakpoints() { m_breakpoints.clear(); }
+ void removeAllBreakpoints() {
+ m_lastBP = nullptr;
+ m_breakpoints.clear();
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
void removeAllBreakpoints() { m_breakpoints.clear(); } | |
void removeAllBreakpoints() { | |
m_lastBP = nullptr; | |
m_breakpoints.clear(); | |
} |
@@ -37,6 +37,7 @@ | |||
static bool isInKernel(uint32_t address, bool biosIsKernel = true); | |||
static inline std::function<const char*()> s_breakpoint_type_names[] = {l_("Exec"), l_("Read"), l_("Write")}; | |||
enum class BreakpointType { Exec, Read, Write }; | |||
enum class BreakpointCondition { Always, Change, Greater, Less, Equal }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add documentation and Range condition
The new BreakpointCondition
enum should include:
- Documentation explaining the behavior of each condition
- The
Range
condition mentioned in the PR objectives
+ /// Breakpoint conditions that determine when a breakpoint should trigger
enum class BreakpointCondition {
+ /// Always trigger the breakpoint
Always,
+ /// Trigger when the value changes
Change,
+ /// Trigger when the value is greater than conditionData
Greater,
+ /// Trigger when the value is less than conditionData
Less,
+ /// Trigger when the value equals conditionData
Equal,
+ /// Trigger when the value is within a range (requires two values in conditionData)
+ Range
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
enum class BreakpointCondition { Always, Change, Greater, Less, Equal }; | |
/// Breakpoint conditions that determine when a breakpoint should trigger | |
enum class BreakpointCondition { | |
/// Always trigger the breakpoint | |
Always, | |
/// Trigger when the value changes | |
Change, | |
/// Trigger when the value is greater than conditionData | |
Greater, | |
/// Trigger when the value is less than conditionData | |
Less, | |
/// Trigger when the value equals conditionData | |
Equal, | |
/// Trigger when the value is within a range (requires two values in conditionData) | |
Range | |
}; |
BreakpointCondition condition() const { return m_condition; } | ||
void setCondition(BreakpointCondition condition) { m_condition = condition; } | ||
uint32_t conditionData() const { return m_conditionData; } | ||
void setConditionData(uint32_t data) { m_conditionData = data; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation and documentation for condition methods
The condition methods need:
- Documentation explaining how
conditionData
is used for each condition type - Validation in
setConditionData
based on the current condition
+ /// @returns The current condition for this breakpoint
BreakpointCondition condition() const { return m_condition; }
+ /// @param condition The new condition to set
void setCondition(BreakpointCondition condition) { m_condition = condition; }
+ /// @returns The condition-specific data:
+ /// - For Greater/Less/Equal: The value to compare against
+ /// - For Range: The upper bound (lower bound stored internally)
+ /// - For Always/Change: Unused
uint32_t conditionData() const { return m_conditionData; }
+ /// @param data The condition-specific data to set
+ /// @throws std::invalid_argument if data is invalid for current condition
void setConditionData(uint32_t data) {
+ switch (m_condition) {
+ case BreakpointCondition::Range:
+ if (data <= getLow()) throw std::invalid_argument("Range upper bound must be greater than lower bound");
+ break;
+ case BreakpointCondition::Always:
+ case BreakpointCondition::Change:
+ if (data != 0) throw std::invalid_argument("Condition data must be 0 for Always/Change conditions");
+ break;
+ }
m_conditionData = data;
}
Committable suggestion skipped: line range outside the PR's diff.
Debug::BreakpointType type = (Debug::BreakpointType)m_breakpointType; | ||
if (type != Debug::BreakpointType::Exec) { | ||
ImGui::Combo(_("Break Condition"), &breakConditionImguiValue, _("Always\0Change\0Greater\0Less\0Equal\0")); | ||
breakCondition = (Debug::BreakpointCondition)breakConditionImguiValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the type is Exec, this path will not be executed, leaving breakCondition with an undefined value. It should be defined when declared above.
src/gui/widgets/breakpoints.cc
Outdated
ImGui::TextColored(((bp->address() | bp->base()) == pc) ? s_hitColor : s_normalColor, "%d", row); | ||
|
||
ImGui::TableNextColumn(); | ||
std::string buttonStr = fmt::format("{:08x}", bp->address() | bp->base()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to prevent conflicting imgui IDs we should append something like so:
std::string buttonStr = fmt::format("{:08x}##{:d}", bp->address() | bp->base(), row);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
src/gui/widgets/breakpoints.cc (2)
94-425
: 🛠️ Refactor suggestionRefactor the complex draw method into smaller functions.
The
draw
method has grown significantly in complexity (from 25 to 70 cyclomatic complexity), making it harder to maintain and understand. Consider breaking it down into smaller, focused functions for different aspects like table creation, row rendering, condition popups, etc.For example, you could extract these logical sections into separate methods:
renderBreakpointTable()
- Lines ~104-180renderBreakpointAddDialog()
- Lines ~187-360renderBreakpointControls()
- Lines ~362-387renderExecutionMapOptions()
- Lines ~391-422This would make the code more maintainable and easier to extend in the future.
🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)
[warning] 94-425: ❌ Getting worse: Complex Method
PCSX::Widgets::Breakpoints::draw increases in cyclomatic complexity from 25 to 70, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
[warning] 94-425: ❌ Getting worse: Bumpy Road Ahead
PCSX::Widgets::Breakpoints::draw increases from 5 to 12 logical blocks with deeply nested code, threshold is one single block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.
[warning] 94-425: ❌ New issue: Deep, Nested Complexity
PCSX::Widgets::Breakpoints::draw has a nested complexity depth of 6, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.
220-224
:⚠️ Potential issueInitialize breakCondition with default value at declaration.
If the breakpoint type is Exec, this path will not be executed, leaving breakCondition potentially with an undefined value.
This is consistent with notdodgeball's previous comment: "if the type is Exec, this path will not be executed, leaving breakCondition with an undefined value."
Although you initialize it at line 220, it's better to be explicit about the behavior for Exec breakpoints:
Debug::BreakpointCondition breakCondition = Debug::BreakpointCondition::Always; Debug::BreakpointType type = (Debug::BreakpointType)m_breakpointType; if (type != Debug::BreakpointType::Exec) { ImGui::Combo(_("Break Condition"), &breakConditionImguiValue, _("Always\0Change\0Greater\0Less\0Equal\0")); breakCondition = (Debug::BreakpointCondition)breakConditionImguiValue; +} else { + // Execution breakpoints always use the "Always" condition }src/core/debug.h (1)
40-40
: 🛠️ Refactor suggestionAdd Range condition to the BreakpointCondition enum.
The PR objectives mention a "Range" condition type, but it's not implemented in the enum.
enum class BreakpointCondition { Always, Change, Greater, Less, Equal, + Range };
Also consider adding documentation comments to explain each condition's behavior, as suggested in previous review comments.
🧹 Nitpick comments (1)
src/gui/widgets/breakpoints.cc (1)
36-50
: Add default case to handle future enum values.The switch statement doesn't have a default case, which could lead to unexpected behavior if new condition types are added to the enum in the future.
static const char* getBreakpointConditionName(PCSX::Debug::BreakpointCondition condition) { switch (condition) { case PCSX::Debug::BreakpointCondition::Always: return _("Always"); case PCSX::Debug::BreakpointCondition::Greater: return _("Greater"); case PCSX::Debug::BreakpointCondition::Less: return _("Less"); case PCSX::Debug::BreakpointCondition::Change: return _("Change"); case PCSX::Debug::BreakpointCondition::Equal: return _("Equal"); + default: + return _("Unknown"); } - return _("Unknown"); }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/core/debug.h
(5 hunks)src/gui/widgets/assembly.cc
(1 hunks)src/gui/widgets/breakpoints.cc
(1 hunks)
🧰 Additional context used
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)
src/gui/widgets/breakpoints.cc
[warning] 94-425: ❌ Getting worse: Complex Method
PCSX::Widgets::Breakpoints::draw increases in cyclomatic complexity from 25 to 70, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
[warning] 94-425: ❌ Getting worse: Bumpy Road Ahead
PCSX::Widgets::Breakpoints::draw increases from 5 to 12 logical blocks with deeply nested code, threshold is one single block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.
[warning] 94-425: ❌ New issue: Deep, Nested Complexity
PCSX::Widgets::Breakpoints::draw has a nested complexity depth of 6, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.
src/gui/widgets/assembly.cc
[warning] 343-348: ❌ New issue: Complex Method
PCSX::Widgets::Assembly::addMemoryEditorContext has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
⏰ Context from checks skipped due to timeout of 90000ms (11)
- GitHub Check: pcsx-redux (x86_64-linux)
- GitHub Check: pcsx-redux (aarch64-linux)
- GitHub Check: aur-build
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: coverage
- GitHub Check: build-openbios
- GitHub Check: macos-build-and-test-toolchain
- GitHub Check: build
- GitHub Check: asan
- GitHub Check: cross-arm64
- GitHub Check: toolchain
🔇 Additional comments (7)
src/gui/widgets/assembly.cc (1)
343-348
: Great addition of memory breakpoint creation in the context menu.This addition allows users to easily create memory read and write breakpoints directly from the assembly view, enhancing the debugging workflow. The implementation is clean and integrates well with the existing context menu structure.
🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)
[warning] 343-348: ❌ New issue: Complex Method
PCSX::Widgets::Assembly::addMemoryEditorContext has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.src/gui/widgets/breakpoints.cc (3)
27-34
: Add support for SWL and SWR instructions.The function ignores SWL and SWR instructions as noted in the comment, which could lead to incorrect values for these specific store instructions.
This aligns with a known limitation previously noted by nicolasnoble: "This is a bit of a blind spot overall. It feels like we can properly support swl/swr."
52-86
: Use safer memory access methods for debugging.The current implementation uses
read8
/read16
/read32
which could potentially advance internal state machines on components like the CD-ROM controller or pads, as noted in previous feedback.This matches nicolasnoble's previous comment: "We shouldn't use read8/read16/read32 for the purpose of the debugger, as it may advance internal state machines on things like the cd-rom controller or the pads."
127-128
: Add unique identifiers to imgui buttons.To prevent conflicting ImGui IDs, append a unique identifier to button labels.
This matches notdodgeball's previous comment about preventing conflicting imgui IDs. The current implementation does this correctly by appending the row number to the button string:
std::string buttonStr = fmt::format("{:08x}##{:d}", bp->address() | bp->base(), row);Good job on this implementation!
src/core/debug.h (3)
82-85
: Add validation in setConditionData based on condition type.Currently, there's no validation to ensure that the condition data makes sense for the given condition type.
Consider adding validation to ensure the data is appropriate for the condition, especially for a future Range condition which might require checking that the upper bound is greater than the lower bound.
168-171
: Good addition of removeAllBreakpoints method.This method properly clears all breakpoints and resets m_lastBP to avoid dangling pointers, which is important for maintaining consistency when removing breakpoints.
70-71
:❓ Verification inconclusive
Breaking change in BreakpointInvoker signature.
Changing the first parameter from
const Breakpoint*
toBreakpoint*
is necessary for the "Change" condition, but it's a breaking change that could affect existing code.
🏁 Script executed:
#!/bin/bash # Find any existing uses of BreakpointInvoker that might be affected by the signature change rg -A 2 -B 2 "BreakpointInvoker" --glob "!src/core/debug.h" --glob "!src/gui/widgets/breakpoints.cc"Length of output: 1224
Action Required: Confirm Consistency for BreakpointInvoker Change
The typedef for BreakpointInvoker insrc/core/debug.h
was updated from taking aconst Breakpoint*
to a non-constBreakpoint*
as its first parameter—this was needed for the new “Change” condition. However, our search reveals that several lambda implementations (e.g., insrc/gui/widgets/typed_debugger.cc
) still declare the parameter as aconst PCSX::Debug::Breakpoint*
. Although passing a non-const pointer to a lambda expecting a const pointer is allowed by conversion rules, the inconsistency in const-qualification may lead to unintended side effects or miscommunication of intended mutability. Please review these call sites and decide whether to update the lambda signatures to match the non-const requirement or document the intended behavior clearly.
- Locations to review:
src/core/debug.h
: typedef definition of BreakpointInvoker (lines 70-71).src/gui/widgets/typed_debugger.cc
: lambda expressions capturing BreakpointInvoker that still useconst PCSX::Debug::Breakpoint*
.