Skip to content
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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

rocketz
Copy link
Contributor

@rocketz 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
Copy link
Member

@nicolasnoble nicolasnoble left a 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
Copy link
Member

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);
Copy link
Member

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.

Copy link
Contributor Author

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?

doBreak = curVal != self->conditionData();
if (doBreak)
{
// TODO: can't update since 'self' is const
Copy link
Member

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.

Copy link
Contributor Author

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.

// than that they want to use the same label twice
m_bpLabelString[0] = 0;

static int breakCondition2 = 0;
Copy link
Member

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.

@rocketz
Copy link
Contributor Author

rocketz commented Nov 29, 2024

Thanks for doing this!

My pleasure!

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.

No worries. You shouldn't waste valuable turkey-time on this :)

I've left a few comments, but it looks good!

Cool. I'll fix a few things though.

@nicolasnoble
Copy link
Member

Updating from main as there was some CI breakages, and we can see better where we're at here.

@nicolasnoble
Copy link
Member

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 TextUnformatted for these strings.

Copy link

codecov bot commented Dec 27, 2024

Codecov Report

Attention: Patch coverage is 0% with 264 lines in your changes missing coverage. Please review.

Project coverage is 9.19%. Comparing base (7ef9141) to head (c10ee53).
Report is 48 commits behind head on main.

Files with missing lines Patch % Lines
src/gui/widgets/breakpoints.cc 0.00% 255 Missing ⚠️
src/core/debug.h 0.00% 5 Missing ⚠️
src/gui/widgets/assembly.cc 0.00% 4 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

@nicolasnoble
Copy link
Member

Alright, let's try and get that in...

Copy link

coderabbitai bot commented Jan 5, 2025

Walkthrough

The pull request introduces enhancements to the debugging system in the PCSX emulator. The changes focus on expanding breakpoint functionality by adding a new BreakpointCondition enum, which allows for more granular control over when breakpoints are triggered. The modifications span multiple files, including debug.h, assembly.cc, breakpoints.cc, and breakpoints.h, introducing new methods for setting breakpoint conditions, reading memory values, and improving the breakpoint management user interface.

Changes

File Change Summary
src/core/debug.h - Added BreakpointCondition enum with values: Always, Change, Greater, Less, Equal
- Updated Breakpoint class with condition-related methods
- Modified BreakpointInvoker type definition
- Added removeAllBreakpoints() method
src/gui/widgets/assembly.cc - Added menu items for creating memory read and write breakpoints
- Utilized g_emulator->m_debug->addBreakpoint() method
src/gui/widgets/breakpoints.cc - Introduced new static methods for memory value retrieval
- Added getBreakpointConditionName() function
- Enhanced breakpoint addition logic with condition selection
- Updated UI to use ImGui tables for breakpoint management
src/gui/widgets/breakpoints.h - Reduced m_bpAddressString array size from 20 to 9

Sequence Diagram

sequenceDiagram
    participant User
    participant GUI
    participant Debug
    participant Emulator

    User->>GUI: Select Memory Address
    GUI->>User: Show Breakpoint Options
    User->>GUI: Choose Breakpoint Condition
    GUI->>Debug: addBreakpoint(address, type, size)
    Debug->>Emulator: Set Breakpoint
    Emulator-->>Debug: Breakpoint Registered
    Debug-->>GUI: Confirm Breakpoint
Loading

Poem

🐰 A Debugger's Delight

Breakpoints dancing, conditions bright,
Memory's secrets now in sight!
With conditions set, both wise and bold,
Our code's tale will soon unfold.
Debugging magic, rabbit's might! 🔍


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a 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 new BreakpointCondition 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

📥 Commits

Reviewing files that changed from the base of the PR and between b0721ec and 46109a2.

📒 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 updating conditionData() 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 to BreakpointCondition::Always.
No issues here. Initializing the condition to Always is a sensible default.


165-165: Mass removal of breakpoints.
The removeAllBreakpoints() method is a logical addition to cleanly clear all breakpoints in one step.

Comment on lines +94 to +425
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();
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants