-
Notifications
You must be signed in to change notification settings - Fork 2.1k
dist/tools/PyCortexMDebug: Integrate GDB extension into RIOT #21863
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
Merged
+246
−29
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /checkout |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| PKG_NAME = PyCortexMDebug | ||
| PKG_URL = https://github.com/bnahill/PyCortexMDebug | ||
| PKG_VERSION = ce371500500a9168729f04b9dcad5f84440deadd | ||
| PKG_LICENSE = GPL-3.0-only | ||
|
|
||
| # manually set some RIOT env vars, so this Makefile can be called stand-alone | ||
| RIOTBASE ?= $(CURDIR)/../../.. | ||
| RIOTTOOLS ?= $(CURDIR)/.. | ||
|
|
||
| PKG_SOURCE_DIR = $(CURDIR)/checkout | ||
| PKG_BUILD_OUT_OF_SOURCE := 0 | ||
|
|
||
| include $(RIOTBASE)/pkg/pkg.mk | ||
|
|
||
| .PHONY: all | ||
| all: prepare |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| PyCortexMDebug | ||
| ============== | ||
|
|
||
| This is the RIOT integration of [PyCortexMDebug][], a GDB extension that allows | ||
| inspecting and interpreting memory of MCUs based on the info provided in SVD | ||
| files. | ||
|
|
||
| This integration adds PRs 58 and 59 on top of the upstream code. To get the | ||
| CMSIS database, run: | ||
|
|
||
| ```sh | ||
| wget -O ~/.cache/cmdebug/cmsis-svd-data.zip https://github.com/cmsis-svd/cmsis-svd-data/archive/refs/heads/main.zip | ||
| ``` | ||
|
|
||
| Automating Loading of Correct SVD File | ||
| --------------------------------------- | ||
|
|
||
| If `SVD_MODEL` and `SVD_VENDOR` are declared, the build system will inject | ||
| an `svd_load $(SVD_MODEL) $(SVD_VENDOR)` command into GDB, so that users don't | ||
| need to call that themselves. | ||
|
|
||
| Ideally, those variables are provided in `cpu/$(CPU)/Makefile.include` | ||
| by inferring it from the `$(CPU_MODEL)` variable each board already provides. | ||
|
|
||
| [PyCortexMDebug]: https://github.com/bnahill/PyCortexMDebug |
41 changes: 41 additions & 0 deletions
41
dist/tools/PyCortexMDebug/patches/0001-cmdebug-LoadSVD-Fix-exception-in-complete.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| From 597434eab992cd3f9495434ed6b990c8c491d66b Mon Sep 17 00:00:00 2001 | ||
| From: Marian Buschsieweke <[email protected]> | ||
| Date: Mon, 3 Nov 2025 23:11:09 +0100 | ||
| Subject: [PATCH] cmdebug: LoadSVD(): Fix exception in complete() | ||
|
|
||
| The parameter `word` may actually be `None`, as shown by this | ||
| exception: | ||
|
|
||
| (gdb) svd_load STraceback (most recent call last): | ||
| File "/usr/lib/python3.12/site-packages/cmdebug/svd_gdb.py", line 65, in complete | ||
| prefix = word.lower() | ||
| ^^^^^^^^^^ | ||
| AttributeError: 'NoneType' object has no attribute 'lower' | ||
|
|
||
| This lets prefix fall back to `""` in case word is `None` to fix the | ||
| issue. | ||
| --- | ||
| cmdebug/svd_gdb.py | 4 ++-- | ||
| 1 file changed, 2 insertions(+), 2 deletions(-) | ||
|
|
||
| diff --git a/cmdebug/svd_gdb.py b/cmdebug/svd_gdb.py | ||
| index 4fb993b..c6e1e9c 100644 | ||
| --- a/cmdebug/svd_gdb.py | ||
| +++ b/cmdebug/svd_gdb.py | ||
| @@ -62,11 +62,11 @@ class LoadSVD(gdb.Command): | ||
|
|
||
| # "svd_load <tab>" or "svd_load ST<tab>" | ||
| if num_args == 1: | ||
| - prefix = word.lower() | ||
| + prefix = "" if word is None else word.lower() | ||
| return [vendor for vendor in self.vendors if vendor.lower().startswith(prefix)] | ||
| # "svd_load STMicro<tab>" or "svd_load STMicro STM32F1<tab>" | ||
| elif num_args == 2 and args[0] in self.vendors: | ||
| - prefix = word.lower() | ||
| + prefix = "" if word is None else word.lower() | ||
| filenames = self.vendors[args[0]] | ||
| return [fname for fname in filenames if fname.lower().startswith(prefix)] | ||
| return gdb.COMPLETE_NONE | ||
| -- | ||
| 2.51.2 | ||
|
|
115 changes: 115 additions & 0 deletions
115
...ls/PyCortexMDebug/patches/0002-LoadSVD-Load-SVD-from-ZIP-file-instead-of-pkg_resour.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| From 568ef712d68eb70574fbce5808fea63963aa908f Mon Sep 17 00:00:00 2001 | ||
| From: Marian Buschsieweke <[email protected]> | ||
| Date: Sun, 9 Nov 2025 12:25:11 +0100 | ||
| Subject: [PATCH] LoadSVD: Load SVD from ZIP file instead of pkg_resources | ||
|
|
||
| This changes the logic with `svd_load <VENDOR> <MODEL>` to not search in | ||
| the pkg_resources of Python module `cmsis_svd.data` but instead search | ||
| in a ZIP file at `$CMSIS_SVD_ZIPFILE` if that environment variable is | ||
| defined, otherwise it looks `$XDG_CACHE_HOME/cmdebug/cmsis-svd-data.zip` | ||
| with `$XDG_CACHE_HOME` falling back to `~/.cache` if not found in the | ||
| environment. | ||
|
|
||
| As of 2025-11-09, the `HEAD` of https://github.com/cmsis-svd/cmsis-svd-data | ||
| is 5.8 GiB in size uncompressed, or 254 MiB when zipped. Personally, I | ||
| do not perceive any additional latency by having to unzip the chosen | ||
| SVD file first. | ||
| --- | ||
| cmdebug/svd_gdb.py | 52 +++++++++++++++++++++++++++++++++++++--------- | ||
| 1 file changed, 42 insertions(+), 10 deletions(-) | ||
|
|
||
| diff --git a/cmdebug/svd_gdb.py b/cmdebug/svd_gdb.py | ||
| index 4fb993b..33c7bec 100644 | ||
| --- a/cmdebug/svd_gdb.py | ||
| +++ b/cmdebug/svd_gdb.py | ||
| @@ -16,11 +16,15 @@ along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>. | ||
| """ | ||
|
|
||
| import gdb | ||
| -import re | ||
| import math | ||
| -import sys | ||
| +import pathlib | ||
| +import re | ||
| import struct | ||
| -import pkg_resources | ||
| +import sys | ||
| +import tempfile | ||
| +import zipfile | ||
| + | ||
| +from os import environ | ||
|
|
||
| sys.path.append('.') | ||
| from cmdebug.svd import SVDFile | ||
| @@ -31,6 +35,16 @@ BITS_TO_UNPACK_FORMAT = { | ||
| 32: "I", | ||
| } | ||
|
|
||
| +if "XDG_CACHE_HOME" in environ and environ["XDG_CACHE_HOME"] != "": | ||
| + CACHE_DIR = pathlib.Path.joinpath(environ["XDG_CACHE_HOME"], "cmdebug") | ||
| +else: | ||
| + CACHE_DIR = pathlib.Path.joinpath(pathlib.Path.home(), ".cache", "cmdebug") | ||
| + | ||
| +if "CMSIS_SVD_ZIPFILE" in environ and environ["CMSIS_SVD_ZIPFILE"] != "": | ||
| + CMSIS_SVD_ZIPFILE = pathlib.Path(environ("CMSIS_SVD_ZIPFILE")) | ||
| +else: | ||
| + CMSIS_SVD_ZIPFILE = pathlib.Path.joinpath(CACHE_DIR, "cmsis-svd-data.zip") | ||
| + | ||
|
|
||
| class LoadSVD(gdb.Command): | ||
| """ A command to load an SVD file and to create the command for inspecting | ||
| @@ -40,11 +54,16 @@ class LoadSVD(gdb.Command): | ||
| def __init__(self): | ||
| self.vendors = {} | ||
| try: | ||
| - vendor_names = pkg_resources.resource_listdir("cmsis_svd", "data") | ||
| - for vendor in vendor_names: | ||
| - fnames = pkg_resources.resource_listdir("cmsis_svd", "data/{}".format(vendor)) | ||
| - self.vendors[vendor] = [fname for fname in fnames if fname.lower().endswith(".svd")] | ||
| - except: | ||
| + with zipfile.ZipFile(CMSIS_SVD_ZIPFILE, mode='r') as svdzip: | ||
| + for entry in svdzip.namelist(): | ||
| + if entry.lower().endswith(".svd"): | ||
| + p = pathlib.Path(entry) | ||
| + vendor = p.parent.name | ||
| + model = p.name[:-4] | ||
| + if vendor not in self.vendors: | ||
| + self.vendors[vendor] = list() | ||
| + self.vendors[vendor].append(model) | ||
| + except FileNotFoundError: | ||
| pass | ||
|
|
||
| if len(self.vendors) > 0: | ||
| @@ -75,14 +94,27 @@ class LoadSVD(gdb.Command): | ||
| def invoke(args, from_tty): | ||
| args = gdb.string_to_argv(args) | ||
| argc = len(args) | ||
| + f = None | ||
| if argc == 1: | ||
| gdb.write("Loading SVD file {}...\n".format(args[0])) | ||
| f = args[0] | ||
| elif argc == 2: | ||
| gdb.write("Loading SVD file {}/{}...\n".format(args[0], args[1])) | ||
| - f = pkg_resources.resource_filename("cmsis_svd", "data/{}/{}".format(args[0], args[1])) | ||
| + name = args[0].lower() + "/" + args[1].lower() + ".svd" | ||
| + try: | ||
| + with zipfile.ZipFile(CMSIS_SVD_ZIPFILE, mode='r') as svdzip: | ||
| + for entry in svdzip.namelist(): | ||
| + lower = entry.lower() | ||
| + if lower == name or lower.endswith("/" + name): | ||
| + f = svdzip.extract(entry, path=tempfile.TemporaryDirectory().name) | ||
| + break | ||
| + except FileNotFoundError: | ||
| + raise gdb.GdbError(f"Could not open \"{CMSIS_SVD_ZIPFILE}\"") | ||
| + | ||
| + if f is None: | ||
| + raise gdb.GdbError(f"Could not find SVD for \"{args[0]}/{args[1]}\" in \"{CMSIS_SVD_ZIPFILE}\": No case-insensitive match for \"**/{name}\"") | ||
| else: | ||
| - raise gdb.GdbError("Usage: svd_load <vendor> <device.svd> or svd_load <path/to/filename.svd>\n") | ||
| + raise gdb.GdbError("Usage: svd_load <vendor> <device> or svd_load <path/to/filename.svd>\n") | ||
| try: | ||
| SVD(SVDFile(f)) | ||
| except Exception as e: | ||
| -- | ||
| 2.51.2 | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,16 +62,24 @@ | |
| # @author Hauke Peteresen <[email protected]> | ||
|
|
||
| # Set IMAGE_OFFSET to zero by default. | ||
| : ${IMAGE_OFFSET:=0} | ||
| # Allow overwriting the reset commands. | ||
| : ${JLINK_RESET_FILE:=${RIOTTOOLS}/jlink/reset.seg} | ||
| # The setsid command is needed so that Ctrl+C in GDB doesn't kill OpenOCD | ||
| : ${SETSID:=setsid} | ||
|
|
||
| # GDB command, usually a separate command for each platform (e.g. arm-none-eabi-gdb) | ||
| : ${GDB:=gdb} | ||
| # Debugger client command, can be used to wrap GDB in a front-end | ||
| : ${DBG:=${GDB}} | ||
| # Start with tui by default | ||
| : ${TUI:=-tui} | ||
| # Extra debugger flags, added by the user | ||
| : ${DBG_EXTRA_FLAGS:=} | ||
| # default GDB port | ||
| _GDB_PORT=3333 | ||
| : ${GDB_PORT:=3333} | ||
| # default telnet port | ||
| _TELNET_PORT=4444 | ||
| : ${TELNET_PORT:=4444} | ||
| # default J-Link command names, interface and speed | ||
| _JLINK=JLinkExe | ||
| _JLINK_SERVER=JLinkGDBServer | ||
|
|
@@ -115,15 +123,6 @@ | |
| fi | ||
| } | ||
|
|
||
| test_ports() { | ||
| if [ -z "${GDB_PORT}" ]; then | ||
| GDB_PORT=${_GDB_PORT} | ||
| fi | ||
| if [ -z "${TELNET_PORT}" ]; then | ||
| TELNET_PORT=${_TELNET_PORT} | ||
| fi | ||
| } | ||
|
|
||
| test_elffile() { | ||
| if [ ! -f "${ELFFILE}" ]; then | ||
| echo "Error: Unable to locate ELFFILE" | ||
|
|
@@ -132,25 +131,13 @@ | |
| fi | ||
| } | ||
|
|
||
| test_tui() { | ||
| if [ -n "${TUI}" ]; then | ||
| TUI=-tui | ||
| fi | ||
| } | ||
|
|
||
| test_serial() { | ||
| if [ -n "${JLINK_SERIAL}" ]; then | ||
| JLINK_SERIAL_SERVER="-select usb='${JLINK_SERIAL}'" | ||
| JLINK_SERIAL="-selectemubysn '${JLINK_SERIAL}'" | ||
| fi | ||
| } | ||
|
|
||
| test_dbg() { | ||
| if [ -z "${DBG}" ]; then | ||
| DBG="${GDB}" | ||
| fi | ||
| } | ||
|
|
||
| test_term() { | ||
| if [ -z "${JLINK_TERMPROG}" ]; then | ||
| JLINK_TERMPROG="${_JLINK_TERMPROG}" | ||
|
|
@@ -167,21 +154,21 @@ | |
| # on older versions, JLINK_VERSION will still be parsed correctly. | ||
| JLINK_VERSION=$(echo q | "${JLINK}" "${JLINK_SERIAL}" -nogui 1 2> /dev/null | grep "^DLL version*" | grep -oE "[0-9]+\.[0-9]+") | ||
|
|
||
| if [ $? -ne 0 ]; then | ||
| echo "Error: J-Link appears not to be installed on your PATH" | ||
| exit 1 | ||
| fi | ||
|
|
||
| "$RIOTTOOLS"/has_minimal_version/has_minimal_version.sh "$JLINK_VERSION" "$JLINK_MINIMUM_VERSION" 2> /dev/null | ||
|
|
||
| if [ $? -ne 0 ]; then | ||
| echo "Error: J-Link V$JLINK_MINIMUM_VERSION is required, but V${JLINK_VERSION} is installed" | ||
| exit 1 | ||
| fi | ||
| fi | ||
| } | ||
|
|
||
| flash_common() { | ||
| test_config | ||
| test_serial | ||
| test_version | ||
|
|
@@ -221,7 +208,7 @@ | |
| # address to flash is hex formatted, as required by JLink | ||
| ADDR_TO_FLASH="$(printf "0x%08x\n" "$((FLASH_ADDR + IMAGE_OFFSET))")" | ||
| echo "loadbin ${BINFILE} ${ADDR_TO_FLASH}" >> "${BINDIR}/burn.seg" | ||
| flash_common | ||
| } | ||
|
|
||
| do_flash_elf() { | ||
|
|
@@ -237,7 +224,7 @@ | |
| done >> "${BINDIR}/burn.seg" | ||
| fi | ||
| echo "loadfile ${ELFFILE}" >> "${BINDIR}/burn.seg" | ||
| flash_common | ||
| } | ||
|
|
||
| do_debug() { | ||
|
|
@@ -246,9 +233,6 @@ | |
| test_serial | ||
| test_version | ||
| test_elffile | ||
| test_ports | ||
| test_tui | ||
| test_dbg | ||
| # start the J-Link GDB server | ||
| ${SETSID} sh -c "${JLINK_SERVER} ${JLINK_SERIAL_SERVER} \ | ||
| -nogui \ | ||
|
|
@@ -261,7 +245,7 @@ | |
| # save PID for terminating the server afterwards | ||
| DBG_PID=$! | ||
| # connect to the GDB server | ||
| ${DBG} -q ${TUI} -ex "tar ext :${GDB_PORT}" ${ELFFILE} | ||
| sh -c "${DBG} -q ${TUI} -ex \"tar ext :${GDB_PORT}\" ${DBG_EXTRA_FLAGS} \"${ELFFILE}\"" | ||
| # clean up | ||
| kill ${DBG_PID} | ||
| } | ||
|
|
@@ -270,7 +254,6 @@ | |
| test_config | ||
| test_serial | ||
| test_version | ||
| test_ports | ||
| # start the J-Link GDB server | ||
| sh -c "${JLINK_SERVER} ${JLINK_SERIAL_SERVER} \ | ||
| -nogui \ | ||
|
|
@@ -306,15 +289,15 @@ | |
| JLINK_PIDFILE=$(mktemp -t "jilnk_pid.XXXXXXXXXX") | ||
| # will be called by trap | ||
| cleanup() { | ||
| if [ -f $JLINK_PIDFILE ]; then | ||
| JLINK_PID="$(cat ${JLINK_PIDFILE})" | ||
| kill ${JLINK_PID} | ||
| rm -r "${JLINK_PIDFILE}" | ||
| fi | ||
| exit 0 | ||
| } | ||
| # cleanup after script terminates | ||
| trap "cleanup ${JLINK_PIDFILE}" EXIT INT | ||
|
|
||
| # start J-link as RTT server | ||
| sh -c "${JLINK} ${JLINK_SERIAL} \ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Control whether to use PyCortexMDebug | ||
| USE_PYCORTEXMDEBUG ?= 0 | ||
|
|
||
| ifeq (1,$(USE_PYCORTEXMDEBUG)) | ||
| DEBUGDEPS += $(RIOTTOOLS)/PyCortexMDebug/checkout | ||
| DBG_EXTRA_FLAGS += --init-command='$(RIOTTOOLS)/PyCortexMDebug/checkout/scripts/gdb.py' | ||
|
|
||
| ifneq (,$(SVD_MODEL)) | ||
| DBG_EXTRA_FLAGS += --eval-command='svd_load $(SVD_VENDOR) $(SVD_MODEL)' | ||
| endif | ||
| endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.