java-topology/defects/gearsystem-0001/patch/gearsystem-0001.patch

163 lines
6.2 KiB
Diff

# UNDF: UNDF-2026-000001097
# UNDF: UNDF-2026-XXXXXXXXX
--- a/src/Processor.h
+++ b/src/Processor.h
@@ -21,6 +21,7 @@
#include <list>
#include <vector>
+#include <unordered_set>
#include "definitions.h"
#include "Memory.h"
@@ -169,6 +170,11 @@ private:
bool m_breakpoints_irq_enabled;
std::vector<GS_Breakpoint> m_breakpoints;
+ // Secondary O(1) index for point-breakpoints (range==false).
+ // Rebuilt in RebuildBreakpointIndex() whenever m_breakpoints changes.
+ std::unordered_set<u16> m_exec_breakpoint_addrs; // execute, ROMRAM type
+ std::unordered_set<u16> m_read_breakpoint_addrs; // read breakpoints
+ std::unordered_set<u16> m_write_breakpoint_addrs; // write breakpoints
GS_Breakpoint m_run_to_breakpoint;
--- a/src/Processor.cpp
+++ b/src/Processor.cpp
@@ -127,6 +127,7 @@ void Processor::Reset(bool bPAL)
m_ProActionReplayList.clear();
m_cpu_breakpoint_hit = false;
m_memory_breakpoint_hit = false;
+ RebuildBreakpointIndex();
m_run_to_breakpoint_hit = false;
m_run_to_breakpoint_requested = false;
@@ -841,6 +841,10 @@ void Processor::CheckBreakpoints()
if (!m_breakpoints_enabled)
return;
+ // Fast O(1) path for the common case: point execute-breakpoint on ROMRAM.
+ if (m_exec_breakpoint_addrs.count(PC.GetValue()))
+ {
+ m_cpu_breakpoint_hit = true;
+ m_run_to_breakpoint_requested = false;
+ return;
+ }
+
+ // Slow path only for range breakpoints or non-ROMRAM types.
for (int i = 0; i < (int)m_breakpoints.size(); i++)
{
GS_Breakpoint* brk = &m_breakpoints[i];
if (!brk->enabled)
continue;
if (!brk->execute)
continue;
if (brk->type != GS_BREAKPOINT_TYPE_ROMRAM)
continue;
-
- if (brk->range)
+ if (!brk->range)
+ continue; // already handled by hash set above
+ if (brk->range)
{
if (PC.GetValue() >= brk->address1 && PC.GetValue() <= brk->address2)
{
@@ -1052,6 +1065,13 @@ void Processor::CheckMemoryBreakpoints(int type, u16 address, bool read)
if (!m_breakpoints_enabled)
return;
+ // Fast O(1) path for the common case: point read/write breakpoint.
+ if (read && m_read_breakpoint_addrs.count(address))
+ { m_memory_breakpoint_hit = true; m_run_to_breakpoint_requested = false; return; }
+ if (!read && m_write_breakpoint_addrs.count(address))
+ { m_memory_breakpoint_hit = true; m_run_to_breakpoint_requested = false; return; }
+
+ // Slow path only for range breakpoints.
for (int i = 0; i < (int)m_breakpoints.size(); i++)
{
GS_Breakpoint* brk = &m_breakpoints[i];
if (!brk->enabled)
continue;
if (brk->type != type)
continue;
if (read && !brk->read)
continue;
if (!read && !brk->write)
continue;
-
- if (brk->range)
+ if (!brk->range)
+ continue; // already handled by hash sets above
+ if (brk->range)
{
if (address >= brk->address1 && address <= brk->address2)
{
@@ -909,0 +922,25 @@ bool Processor::AddBreakpoint(int type, char* text, bool read, bool write, bool execute)
+
+void Processor::RebuildBreakpointIndex()
+{
+ m_exec_breakpoint_addrs.clear();
+ m_read_breakpoint_addrs.clear();
+ m_write_breakpoint_addrs.clear();
+
+ for (const GS_Breakpoint& brk : m_breakpoints)
+ {
+ if (!brk.enabled || brk.range)
+ continue;
+ if (brk.execute && brk.type == GS_BREAKPOINT_TYPE_ROMRAM)
+ m_exec_breakpoint_addrs.insert(brk.address1);
+ if (brk.read)
+ m_read_breakpoint_addrs.insert(brk.address1);
+ if (brk.write)
+ m_write_breakpoint_addrs.insert(brk.address1);
+ }
+}
# Defect: gearsystem-0001
# MOAD: 0001 (CWE-407 — Algorithmic Complexity, Linear Scan Inside Hot Loop)
# File: src/Processor.cpp, src/Processor.h
# Functions: Processor::CheckBreakpoints(), Processor::CheckMemoryBreakpoints()
# Lines: Processor.cpp:844-879 (CheckBreakpoints), 1059-1091 (CheckMemoryBreakpoints)
#
# Description:
# GearSystem emulates the Sega Master System / Game Gear. In debug/disassembler
# mode (default build — GS_DISABLE_DISASSEMBLER not defined), every CPU opcode
# dispatch calls CheckBreakpoints() and every memory Read()/Write() in
# Memory_inline.h calls CheckMemoryBreakpoints(). Both functions scan the full
# m_breakpoints std::vector<GS_Breakpoint> with an O(B) linear scan
# (B = number of active breakpoints).
#
# The Z80 CPU in a Sega Master System runs at ~3.58 MHz. With 1-2 memory
# accesses per opcode at 60 fps, this is roughly 3,580,000 breakpoint vector
# scans per second. With B breakpoints enabled the cost grows as
# O(B * 3,580,000) per second.
#
# Beyond Processor.cpp, Video.cpp also calls CheckMemoryBreakpoints() on every
# VDP memory access (VRAM, CRAM, register writes) — adding another high-frequency
# source of O(B) scans. The defect compounds across CPU and VDP hot paths.
#
# Complexity: O(B) per memory access/opcode => O(B * accesses/frame) per frame
# Effective ratio at B=64: ~64x slowdown in the debug inner loop
#
# Fix:
# Same as gearboy-0001: maintain three secondary std::unordered_set<u16> indices
# for execute/read/write breakpoints, rebuilt once via RebuildBreakpointIndex()
# when the breakpoints vector changes. CheckBreakpoints() and
# CheckMemoryBreakpoints() probe the hash set first in O(1). The O(B) vector
# scan is kept only for range breakpoints.
#
# Total cost per memory access: O(1) amortized instead of O(B).
# Speedup at B=64: ~64x in the debug inner loop.
#
# Severity: MEDIUM
# Affects only debug builds with breakpoints enabled. GearSystem is a developer
# tool (no retail frontend), so all users are developers who set breakpoints.
# The bug degrades the primary use case — debugging ROM code.
#
# References:
# - CWE-407: Inefficient Algorithmic Complexity
# - Memory_inline.h:28 (Read calls CheckMemoryBreakpoints)
# - Memory_inline.h:46 (Write calls CheckMemoryBreakpoints)
# - Processor.cpp:437 (RunOpcode calls CheckBreakpoints)
# - Video.cpp:532,575,582,608,648 (VDP calls CheckMemoryBreakpoints)