From 2fa4aca2868911dc1a75b806d883e7fbd239bf64 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 17 Feb 2026 22:13:50 -0700 Subject: [PATCH] Fix breakpoint_list parser for OpenOCD 0.12.0 dual-format output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenOCD uses two different output formats for breakpoint listing within the same session: Breakpoint(IVA) for hardware (FPB) and IVA breakpoint for software (patched). The old regex only matched the first format, causing breakpoint_list() to always return empty when software breakpoints were present. The third field is a comparator index or bp number, not a hw/sw type flag — the format variant itself indicates the breakpoint type. Verified against live STM32F103C6T6 hardware: 8/9 lifecycle tests pass, 154/154 mock tests pass. --- src/openocd/breakpoints.py | 18 +++++++++++++----- tests/mock_server.py | 5 +++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/openocd/breakpoints.py b/src/openocd/breakpoints.py index ae6c737..310e70e 100644 --- a/src/openocd/breakpoints.py +++ b/src/openocd/breakpoints.py @@ -27,10 +27,15 @@ class BreakpointError(OpenOCDError): # Parsers # --------------------------------------------------------------------------- -# Breakpoint(IVA): 0x08001234, 0x2, 1 (hw=1) or 0 (sw) +# OpenOCD breakpoint listing uses two format variants (even within a single +# session) depending on breakpoint type: +# HW (FPB): Breakpoint(IVA): 0x08000000, 0x2, 0 (third = comparator idx) +# SW (patch): IVA breakpoint: 0x08000100, 0x2, 0x03 (third = bp number) +# The format variant itself indicates hw vs sw; the third field is NOT a type flag. _BP_RE = re.compile( - r"Breakpoint\([^)]*\):\s*(?P0x[0-9a-fA-F]+),\s*" - r"(?P0x[0-9a-fA-F]+),\s*(?P\d+)" + r"(?:(?PBreakpoint\([^)]*\))|(?:(?:IVA|CONTEXT)\s+breakpoint)):\s*" + r"(?P0x[0-9a-fA-F]+),\s*" + r"(?P0x[0-9a-fA-F]+),\s*(?P0x[0-9a-fA-F]+|\d+)" ) # Watchpoint output varies across OpenOCD versions. Common formats: @@ -57,11 +62,14 @@ def _parse_breakpoint_list(text: str) -> list[Breakpoint]: """Parse the output of ``bp`` (no arguments) into Breakpoint objects.""" breakpoints: list[Breakpoint] = [] for idx, m in enumerate(_BP_RE.finditer(text)): - hw_flag = int(m.group("hw")) + # The "old" named group matches only for the Breakpoint(...) format, + # which OpenOCD uses for hardware (FPB) breakpoints. The bare + # "IVA breakpoint:" format is used for software (patched) breakpoints. + is_hw = m.group("old") is not None breakpoints.append( Breakpoint( number=idx, - type="hw" if hw_flag else "sw", + type="hw" if is_hw else "sw", address=int(m.group("addr"), 16), length=int(m.group("len"), 16), enabled=True, diff --git a/tests/mock_server.py b/tests/mock_server.py index db9d9d4..37b9cef 100644 --- a/tests/mock_server.py +++ b/tests/mock_server.py @@ -68,8 +68,9 @@ SCAN_CHAIN_RESPONSE = """\ 0 stm32f1x.cpu Y 0x3ba00477 0x3ba00477 4 0x01 0x0f""" BP_LIST_RESPONSE = """\ -Breakpoint(IVA): 0x08001234, 0x2, 1 -Breakpoint(IVA): 0x08001300, 0x2, 0""" +Breakpoint(IVA): 0x08001234, 0x2, 0 +IVA breakpoint: 0x08001300, 0x2, 0x03 +Breakpoint(IVA): 0x08001400, 0x4, 1""" RTT_CHANNELS_RESPONSE = """\ Up-channels: