mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-11-23 10:01:58 +03:00
Debug: revert cortex debug to lxml and drop DWT (#2651)
* Debug: revert cortex debug to lxml * Debug: update PyCortexMDebug readme * fbt: moved "debug" dir to "scripts" subfolder * ufbt: added missing debug_other & debug_other_blackmagic targets; github: fixed script bundling * lint: fixed formatting on debug scripts * vscode: updated configuration for debug dir changes --------- Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: hedger <hedger@nanode.su>
This commit is contained in:
parent
241b4ef6e4
commit
f57f0efc48
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -60,7 +60,7 @@ jobs:
|
|||||||
- name: 'Bundle scripts'
|
- name: 'Bundle scripts'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
run: |
|
run: |
|
||||||
tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug
|
tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts
|
||||||
|
|
||||||
- name: 'Build the firmware'
|
- name: 'Build the firmware'
|
||||||
run: |
|
run: |
|
||||||
|
31
.vscode/example/launch.json
vendored
31
.vscode/example/launch.json
vendored
@ -27,22 +27,21 @@
|
|||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
"servertype": "openocd",
|
"servertype": "openocd",
|
||||||
"device": "stlink",
|
"device": "stlink",
|
||||||
"svdFile": "./debug/STM32WB55_CM4.svd",
|
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
|
||||||
// If you're debugging early in the boot process, before OS scheduler is running,
|
// If you're debugging early in the boot process, before OS scheduler is running,
|
||||||
// you have to comment out the following line.
|
// you have to comment out the following line.
|
||||||
"rtos": "FreeRTOS",
|
"rtos": "FreeRTOS",
|
||||||
"configFiles": [
|
"configFiles": [
|
||||||
"interface/stlink.cfg",
|
"interface/stlink.cfg",
|
||||||
"./debug/stm32wbx.cfg",
|
"./scripts/debug/stm32wbx.cfg",
|
||||||
],
|
],
|
||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
"source debug/flipperversion.py",
|
"source scripts/debug/flipperversion.py",
|
||||||
"fw-version",
|
"fw-version",
|
||||||
// "compare-sections",
|
// "compare-sections",
|
||||||
"source debug/flipperapps.py",
|
"source scripts/debug/flipperapps.py",
|
||||||
"fap-set-debug-elf-root build/latest/.extapps",
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
// "source debug/FreeRTOS/FreeRTOS.py",
|
// "source scripts/debug/FreeRTOS/FreeRTOS.py",
|
||||||
// "svd_load debug/STM32WB55_CM4.svd"
|
|
||||||
]
|
]
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
},
|
},
|
||||||
@ -54,16 +53,16 @@
|
|||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
"servertype": "external",
|
"servertype": "external",
|
||||||
"gdbTarget": "${input:BLACKMAGIC}",
|
"gdbTarget": "${input:BLACKMAGIC}",
|
||||||
"svdFile": "./debug/STM32WB55_CM4.svd",
|
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
|
||||||
"rtos": "FreeRTOS",
|
"rtos": "FreeRTOS",
|
||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
"monitor swdp_scan",
|
"monitor swdp_scan",
|
||||||
"attach 1",
|
"attach 1",
|
||||||
"set confirm off",
|
"set confirm off",
|
||||||
"set mem inaccessible-by-default off",
|
"set mem inaccessible-by-default off",
|
||||||
"source debug/flipperversion.py",
|
"source scripts/debug/flipperversion.py",
|
||||||
"fw-version",
|
"fw-version",
|
||||||
"source debug/flipperapps.py",
|
"source scripts/debug/flipperapps.py",
|
||||||
"fap-set-debug-elf-root build/latest/.extapps",
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
// "compare-sections",
|
// "compare-sections",
|
||||||
]
|
]
|
||||||
@ -78,12 +77,12 @@
|
|||||||
"servertype": "jlink",
|
"servertype": "jlink",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"device": "STM32WB55RG",
|
"device": "STM32WB55RG",
|
||||||
"svdFile": "./debug/STM32WB55_CM4.svd",
|
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
|
||||||
"rtos": "FreeRTOS",
|
"rtos": "FreeRTOS",
|
||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
"source debug/flipperversion.py",
|
"source scripts/debug/flipperversion.py",
|
||||||
"fw-version",
|
"fw-version",
|
||||||
"source debug/flipperapps.py",
|
"source scripts/debug/flipperapps.py",
|
||||||
"fap-set-debug-elf-root build/latest/.extapps",
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
]
|
]
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
@ -96,16 +95,16 @@
|
|||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
"servertype": "openocd",
|
"servertype": "openocd",
|
||||||
"device": "cmsis-dap",
|
"device": "cmsis-dap",
|
||||||
"svdFile": "./debug/STM32WB55_CM4.svd",
|
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
|
||||||
"rtos": "FreeRTOS",
|
"rtos": "FreeRTOS",
|
||||||
"configFiles": [
|
"configFiles": [
|
||||||
"interface/cmsis-dap.cfg",
|
"interface/cmsis-dap.cfg",
|
||||||
"./debug/stm32wbx.cfg",
|
"./scripts/debug/stm32wbx.cfg",
|
||||||
],
|
],
|
||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
"source debug/flipperversion.py",
|
"source scripts/debug/flipperversion.py",
|
||||||
"fw-version",
|
"fw-version",
|
||||||
"source debug/flipperapps.py",
|
"source scripts/debug/flipperapps.py",
|
||||||
"fap-set-debug-elf-root build/latest/.extapps",
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
],
|
],
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
PyCortexMDebug
|
|
||||||
==============
|
|
||||||
|
|
||||||
*A set of GDB/Python-based utilities to make life debugging ARM Cortex-M processors a bit easier*
|
|
||||||
|
|
||||||
It will consist of several modules which will hopefully become integrated as they evolve. Presently, there is only one:
|
|
||||||
|
|
||||||
## SVD
|
|
||||||
ARM defines an SVD (System View Description) file format in its CMSIS
|
|
||||||
standard as a means for Cortex-M-based chip manufacturers to provide a
|
|
||||||
common description of peripherals, registers, and register fields. You
|
|
||||||
can download SVD files for different manufacturers
|
|
||||||
[here](http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php).
|
|
||||||
|
|
||||||
My implementation so far has only tested STM32 chips but should hold for others. If others are like those from ST,
|
|
||||||
expect plenty of errors in the file. Like GPIOA having a register named GPIOB_OSPEEDR and lots of 16-bit registers
|
|
||||||
that are listed as 32!
|
|
||||||
|
|
||||||
The implementation consists of two components -- An xml parser module (pysvd) and a GDB file (gdb_svd).
|
|
||||||
I haven't yet worked out a perfect workflow for this, though it's quite easy to use when
|
|
||||||
you already tend to have a GDB initialization file for starting up OpenOCD and the like.
|
|
||||||
However your workflow works, just make sure to, in GDB:
|
|
||||||
|
|
||||||
source gdb_svd.py
|
|
||||||
svd_load [your_svd_file].svd
|
|
||||||
|
|
||||||
These files can be huge so it might take a second or two. Anyways, after that, you can do
|
|
||||||
|
|
||||||
svd
|
|
||||||
|
|
||||||
to list available peripherals with descriptions. Or you can do
|
|
||||||
|
|
||||||
svd [some_peripheral_name]
|
|
||||||
|
|
||||||
to see all of the registers (with their values) for a given peripheral. For more details, run
|
|
||||||
|
|
||||||
svd [some_peripheral_name] [some_register_name]
|
|
||||||
|
|
||||||
to see all of the field values with descriptions.
|
|
||||||
|
|
||||||
You can add format modifiers like:
|
|
||||||
|
|
||||||
* `svd/x` will display values in hex
|
|
||||||
* `svd/o` will display values in octal
|
|
||||||
* `svd/t` or `svd/b` will display values in binary
|
|
||||||
* `svd/a` will display values in hex and try to resolve symbols from the values
|
|
||||||
|
|
||||||
All field values are displayed at the correct lengths as provided by the SVD files.
|
|
||||||
Also, tab completion exists for nearly everything! When in doubt, run `svd help`.
|
|
||||||
|
|
||||||
### TODO
|
|
||||||
|
|
||||||
Enable writing to registers and individual fields
|
|
||||||
|
|
||||||
### Bugs
|
|
||||||
|
|
||||||
There are probably a few. All planning, writing, and testing of this was done in an afternoon. There may be
|
|
||||||
some oddities in working with non-STM32 parts. I'll play with this when I start working with other
|
|
||||||
controllers again. If something's giving you trouble, describe the problem and it shall be fixed.
|
|
||||||
|
|
||||||
## DWT
|
|
||||||
The ARM Data Watchpoint and Trace Unit (DWT) offers data watchpoints and a series of gated cycle counters. For now,
|
|
||||||
I only support the raw cycle counter but facilities are in place to make use of others. As this is independent of the
|
|
||||||
specific device under test, commands are simple and you can configure a clock speed to get real time values from
|
|
||||||
counters.
|
|
||||||
|
|
||||||
dwt configclk 48000000
|
|
||||||
|
|
||||||
will set the current core clock speed. Then
|
|
||||||
|
|
||||||
dwt cyccnt reset
|
|
||||||
dwt cyccnt enable
|
|
||||||
|
|
||||||
will reset and start the cycle counter. At any point
|
|
||||||
|
|
||||||
dwt cycnt
|
|
||||||
|
|
||||||
will then indicate the number of cycles and amount of time that has passed.
|
|
||||||
|
|
||||||
## ITM/ETM support
|
|
||||||
|
|
||||||
This is not implemented yet. I want to have more complete support for some of the nicer debug and trace features
|
|
||||||
on Cortex-M processors. Parts of this will probably be dependent on OpenOCD and possibly on specific interfaces.
|
|
||||||
I'll try to avoid this where possible but can't make any promises.
|
|
@ -1,160 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"""
|
|
||||||
This file is part of PyCortexMDebug
|
|
||||||
|
|
||||||
PyCortexMDebug is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
PyCortexMDebug is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import gdb
|
|
||||||
import struct
|
|
||||||
|
|
||||||
DWT_CTRL = 0xE0001000
|
|
||||||
DWT_CYCCNT = 0xE0001004
|
|
||||||
DWT_CPICNT = 0xE0001008
|
|
||||||
DWT_EXTCNT = 0xE000100C
|
|
||||||
DWT_SLEEPCNT = 0xE0001010
|
|
||||||
DWT_LSUCNT = 0xE0001014
|
|
||||||
DWT_FOLDCNT = 0xE0001018
|
|
||||||
DWT_PCSR = 0xE000101C
|
|
||||||
|
|
||||||
prefix = "dwt : "
|
|
||||||
|
|
||||||
|
|
||||||
class DWT(gdb.Command):
|
|
||||||
clk = None
|
|
||||||
is_init = False
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gdb.Command.__init__(self, "dwt", gdb.COMMAND_DATA)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def read(address, bits=32):
|
|
||||||
"""Read from memory (using print) and return an integer"""
|
|
||||||
value = gdb.selected_inferior().read_memory(address, bits / 8)
|
|
||||||
return struct.unpack_from("<i", value)[0]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def write(address, value, bits=32):
|
|
||||||
"""Set a value in memory"""
|
|
||||||
gdb.selected_inferior().write_memory(address, bytes(value), bits / 8)
|
|
||||||
|
|
||||||
def invoke(self, args, from_tty):
|
|
||||||
if not self.is_init:
|
|
||||||
self.write(0xE000EDFC, self.read(0xE000EDFC) | (1 << 24))
|
|
||||||
self.write(DWT_CTRL, 0)
|
|
||||||
self.is_init = True
|
|
||||||
|
|
||||||
s = list(map(lambda x: x.lower(), str(args).split(" ")))
|
|
||||||
# Check for empty command
|
|
||||||
if s[0] in ["", "help"]:
|
|
||||||
self.print_help()
|
|
||||||
return ()
|
|
||||||
|
|
||||||
if s[0] == "cyccnt":
|
|
||||||
if len(s) > 1:
|
|
||||||
if s[1][:2] == "en":
|
|
||||||
self.cyccnt_en()
|
|
||||||
elif s[1][0] == "r":
|
|
||||||
self.cyccnt_reset()
|
|
||||||
elif s[1][0] == "d":
|
|
||||||
self.cyccnt_dis()
|
|
||||||
gdb.write(
|
|
||||||
prefix
|
|
||||||
+ "CYCCNT ({}): ".format("ON" if (self.read(DWT_CTRL) & 1) else "OFF")
|
|
||||||
+ self.cycles_str(self.read(DWT_CYCCNT))
|
|
||||||
)
|
|
||||||
elif s[0] == "reset":
|
|
||||||
if len(s) > 1:
|
|
||||||
if s[1] == "cyccnt":
|
|
||||||
self.cyccnt_reset()
|
|
||||||
gdb.write(prefix + "CYCCNT reset\n")
|
|
||||||
if s[1] == "counters":
|
|
||||||
self.cyccnt_reset()
|
|
||||||
gdb.write(prefix + "CYCCNT reset\n")
|
|
||||||
else:
|
|
||||||
self.cyccnt_reset()
|
|
||||||
gdb.write(prefix + "CYCCNT reset\n")
|
|
||||||
else:
|
|
||||||
# Reset everything
|
|
||||||
self.cyccnt_reset()
|
|
||||||
gdb.write(prefix + "CYCCNT reset\n")
|
|
||||||
elif s[0] == "configclk":
|
|
||||||
if len(s) == 2:
|
|
||||||
try:
|
|
||||||
self.clk = float(s[1])
|
|
||||||
except:
|
|
||||||
self.print_help()
|
|
||||||
else:
|
|
||||||
self.print_help()
|
|
||||||
else:
|
|
||||||
# Try to figure out what stupid went on here
|
|
||||||
gdb.write(args)
|
|
||||||
self.print_help()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def complete(text, word):
|
|
||||||
text = str(text).lower()
|
|
||||||
s = text.split(" ")
|
|
||||||
|
|
||||||
commands = ["configclk", "reset", "cyccnt"]
|
|
||||||
reset_commands = ["counters", "cyccnt"]
|
|
||||||
cyccnt_commands = ["enable", "reset", "disable"]
|
|
||||||
|
|
||||||
if len(s) == 1:
|
|
||||||
return filter(lambda x: x.startswith(s[0]), commands)
|
|
||||||
|
|
||||||
if len(s) == 2:
|
|
||||||
if s[0] == "reset":
|
|
||||||
return filter(lambda x: x.startswith(s[1]), reset_commands)
|
|
||||||
if s[0] == "cyccnt":
|
|
||||||
return filter(lambda x: x.startswith(s[1]), cyccnt_commands)
|
|
||||||
|
|
||||||
def cycles_str(self, cycles):
|
|
||||||
if self.clk:
|
|
||||||
return "%d cycles, %.3es\n" % (cycles, cycles * 1.0 / self.clk)
|
|
||||||
else:
|
|
||||||
return "%d cycles"
|
|
||||||
|
|
||||||
def cyccnt_en(self):
|
|
||||||
self.write(DWT_CTRL, self.read(DWT_CTRL) | 1)
|
|
||||||
|
|
||||||
def cyccnt_dis(self):
|
|
||||||
self.write(DWT_CTRL, self.read(DWT_CTRL) & 0xFFFFFFFE)
|
|
||||||
|
|
||||||
def cyccnt_reset(self, value=0):
|
|
||||||
self.write(DWT_CYCCNT, value)
|
|
||||||
|
|
||||||
def cpicnt_reset(self, value=0):
|
|
||||||
self.write(DWT_CPICNT, value & 0xFF)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def print_help():
|
|
||||||
gdb.write("Usage:\n")
|
|
||||||
gdb.write("=========\n")
|
|
||||||
gdb.write("dwt:\n")
|
|
||||||
gdb.write("\tList available peripherals\n")
|
|
||||||
gdb.write("dwt configclk [Hz]:\n")
|
|
||||||
gdb.write("\tSet clock for rendering time values in seconds\n")
|
|
||||||
gdb.write("dwt reset:\n")
|
|
||||||
gdb.write("\tReset everything in DWT\n")
|
|
||||||
gdb.write("dwt reset counters:\n")
|
|
||||||
gdb.write("\tReset all DWT counters\n")
|
|
||||||
gdb.write("dwt cyccnt\n")
|
|
||||||
gdb.write("\tDisplay the cycle count\n")
|
|
||||||
gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\n")
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
# Registers our class to GDB when sourced:
|
|
||||||
DWT()
|
|
@ -1,586 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"Makes working with XML feel like you are working with JSON"
|
|
||||||
|
|
||||||
try:
|
|
||||||
from defusedexpat import pyexpat as expat
|
|
||||||
except ImportError:
|
|
||||||
from xml.parsers import expat
|
|
||||||
|
|
||||||
from xml.sax.saxutils import XMLGenerator
|
|
||||||
from xml.sax.xmlreader import AttributesImpl
|
|
||||||
|
|
||||||
try: # pragma no cover
|
|
||||||
from cStringIO import StringIO
|
|
||||||
except ImportError: # pragma no cover
|
|
||||||
try:
|
|
||||||
from StringIO import StringIO
|
|
||||||
except ImportError:
|
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
from inspect import isgenerator
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectDict(dict):
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name in self:
|
|
||||||
return self[name]
|
|
||||||
else:
|
|
||||||
raise AttributeError("No such attribute: " + name)
|
|
||||||
|
|
||||||
|
|
||||||
try: # pragma no cover
|
|
||||||
_basestring = basestring
|
|
||||||
except NameError: # pragma no cover
|
|
||||||
_basestring = str
|
|
||||||
try: # pragma no cover
|
|
||||||
_unicode = unicode
|
|
||||||
except NameError: # pragma no cover
|
|
||||||
_unicode = str
|
|
||||||
|
|
||||||
__author__ = "Martin Blech"
|
|
||||||
__version__ = "0.12.0"
|
|
||||||
__license__ = "MIT"
|
|
||||||
|
|
||||||
|
|
||||||
class ParsingInterrupted(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class _DictSAXHandler(object):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
item_depth=0,
|
|
||||||
item_callback=lambda *args: True,
|
|
||||||
xml_attribs=True,
|
|
||||||
attr_prefix="@",
|
|
||||||
cdata_key="#text",
|
|
||||||
force_cdata=False,
|
|
||||||
cdata_separator="",
|
|
||||||
postprocessor=None,
|
|
||||||
dict_constructor=ObjectDict,
|
|
||||||
strip_whitespace=True,
|
|
||||||
namespace_separator=":",
|
|
||||||
namespaces=None,
|
|
||||||
force_list=None,
|
|
||||||
comment_key="#comment",
|
|
||||||
):
|
|
||||||
self.path = []
|
|
||||||
self.stack = []
|
|
||||||
self.data = []
|
|
||||||
self.item = None
|
|
||||||
self.item_depth = item_depth
|
|
||||||
self.xml_attribs = xml_attribs
|
|
||||||
self.item_callback = item_callback
|
|
||||||
self.attr_prefix = attr_prefix
|
|
||||||
self.cdata_key = cdata_key
|
|
||||||
self.force_cdata = force_cdata
|
|
||||||
self.cdata_separator = cdata_separator
|
|
||||||
self.postprocessor = postprocessor
|
|
||||||
self.dict_constructor = dict_constructor
|
|
||||||
self.strip_whitespace = strip_whitespace
|
|
||||||
self.namespace_separator = namespace_separator
|
|
||||||
self.namespaces = namespaces
|
|
||||||
self.namespace_declarations = ObjectDict()
|
|
||||||
self.force_list = force_list
|
|
||||||
self.comment_key = comment_key
|
|
||||||
|
|
||||||
def _build_name(self, full_name):
|
|
||||||
if self.namespaces is None:
|
|
||||||
return full_name
|
|
||||||
i = full_name.rfind(self.namespace_separator)
|
|
||||||
if i == -1:
|
|
||||||
return full_name
|
|
||||||
namespace, name = full_name[:i], full_name[i + 1 :]
|
|
||||||
try:
|
|
||||||
short_namespace = self.namespaces[namespace]
|
|
||||||
except KeyError:
|
|
||||||
short_namespace = namespace
|
|
||||||
if not short_namespace:
|
|
||||||
return name
|
|
||||||
else:
|
|
||||||
return self.namespace_separator.join((short_namespace, name))
|
|
||||||
|
|
||||||
def _attrs_to_dict(self, attrs):
|
|
||||||
if isinstance(attrs, dict):
|
|
||||||
return attrs
|
|
||||||
return self.dict_constructor(zip(attrs[0::2], attrs[1::2]))
|
|
||||||
|
|
||||||
def startNamespaceDecl(self, prefix, uri):
|
|
||||||
self.namespace_declarations[prefix or ""] = uri
|
|
||||||
|
|
||||||
def startElement(self, full_name, attrs):
|
|
||||||
name = self._build_name(full_name)
|
|
||||||
attrs = self._attrs_to_dict(attrs)
|
|
||||||
if attrs and self.namespace_declarations:
|
|
||||||
attrs["xmlns"] = self.namespace_declarations
|
|
||||||
self.namespace_declarations = ObjectDict()
|
|
||||||
self.path.append((name, attrs or None))
|
|
||||||
if len(self.path) > self.item_depth:
|
|
||||||
self.stack.append((self.item, self.data))
|
|
||||||
if self.xml_attribs:
|
|
||||||
attr_entries = []
|
|
||||||
for key, value in attrs.items():
|
|
||||||
key = self.attr_prefix + self._build_name(key)
|
|
||||||
if self.postprocessor:
|
|
||||||
entry = self.postprocessor(self.path, key, value)
|
|
||||||
else:
|
|
||||||
entry = (key, value)
|
|
||||||
if entry:
|
|
||||||
attr_entries.append(entry)
|
|
||||||
attrs = self.dict_constructor(attr_entries)
|
|
||||||
else:
|
|
||||||
attrs = None
|
|
||||||
self.item = attrs or None
|
|
||||||
self.data = []
|
|
||||||
|
|
||||||
def endElement(self, full_name):
|
|
||||||
name = self._build_name(full_name)
|
|
||||||
if len(self.path) == self.item_depth:
|
|
||||||
item = self.item
|
|
||||||
if item is None:
|
|
||||||
item = None if not self.data else self.cdata_separator.join(self.data)
|
|
||||||
|
|
||||||
should_continue = self.item_callback(self.path, item)
|
|
||||||
if not should_continue:
|
|
||||||
raise ParsingInterrupted()
|
|
||||||
if len(self.stack):
|
|
||||||
data = None if not self.data else self.cdata_separator.join(self.data)
|
|
||||||
item = self.item
|
|
||||||
self.item, self.data = self.stack.pop()
|
|
||||||
if self.strip_whitespace and data:
|
|
||||||
data = data.strip() or None
|
|
||||||
if data and self.force_cdata and item is None:
|
|
||||||
item = self.dict_constructor()
|
|
||||||
if item is not None:
|
|
||||||
if data:
|
|
||||||
self.push_data(item, self.cdata_key, data)
|
|
||||||
self.item = self.push_data(self.item, name, item)
|
|
||||||
else:
|
|
||||||
self.item = self.push_data(self.item, name, data)
|
|
||||||
else:
|
|
||||||
self.item = None
|
|
||||||
self.data = []
|
|
||||||
self.path.pop()
|
|
||||||
|
|
||||||
def characters(self, data):
|
|
||||||
if not self.data:
|
|
||||||
self.data = [data]
|
|
||||||
else:
|
|
||||||
self.data.append(data)
|
|
||||||
|
|
||||||
def comments(self, data):
|
|
||||||
if self.strip_whitespace:
|
|
||||||
data = data.strip()
|
|
||||||
self.item = self.push_data(self.item, self.comment_key, data)
|
|
||||||
|
|
||||||
def push_data(self, item, key, data):
|
|
||||||
if self.postprocessor is not None:
|
|
||||||
result = self.postprocessor(self.path, key, data)
|
|
||||||
if result is None:
|
|
||||||
return item
|
|
||||||
key, data = result
|
|
||||||
if item is None:
|
|
||||||
item = self.dict_constructor()
|
|
||||||
try:
|
|
||||||
value = item[key]
|
|
||||||
if isinstance(value, list):
|
|
||||||
value.append(data)
|
|
||||||
else:
|
|
||||||
item[key] = [value, data]
|
|
||||||
except KeyError:
|
|
||||||
if self._should_force_list(key, data):
|
|
||||||
item[key] = [data]
|
|
||||||
else:
|
|
||||||
item[key] = data
|
|
||||||
return item
|
|
||||||
|
|
||||||
def _should_force_list(self, key, value):
|
|
||||||
if not self.force_list:
|
|
||||||
return False
|
|
||||||
if isinstance(self.force_list, bool):
|
|
||||||
return self.force_list
|
|
||||||
try:
|
|
||||||
return key in self.force_list
|
|
||||||
except TypeError:
|
|
||||||
return self.force_list(self.path[:-1], key, value)
|
|
||||||
|
|
||||||
|
|
||||||
def parse(
|
|
||||||
xml_input,
|
|
||||||
encoding=None,
|
|
||||||
expat=expat,
|
|
||||||
process_namespaces=False,
|
|
||||||
namespace_separator=":",
|
|
||||||
disable_entities=True,
|
|
||||||
process_comments=False,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""Parse the given XML input and convert it into a dictionary.
|
|
||||||
|
|
||||||
`xml_input` can either be a `string`, a file-like object, or a generator of strings.
|
|
||||||
|
|
||||||
If `xml_attribs` is `True`, element attributes are put in the dictionary
|
|
||||||
among regular child elements, using `@` as a prefix to avoid collisions. If
|
|
||||||
set to `False`, they are just ignored.
|
|
||||||
|
|
||||||
Simple example::
|
|
||||||
|
|
||||||
>>> import xmltodict
|
|
||||||
>>> doc = xmltodict.parse(\"\"\"
|
|
||||||
... <a prop="x">
|
|
||||||
... <b>1</b>
|
|
||||||
... <b>2</b>
|
|
||||||
... </a>
|
|
||||||
... \"\"\")
|
|
||||||
>>> doc['a']['@prop']
|
|
||||||
u'x'
|
|
||||||
>>> doc['a']['b']
|
|
||||||
[u'1', u'2']
|
|
||||||
|
|
||||||
If `item_depth` is `0`, the function returns a dictionary for the root
|
|
||||||
element (default behavior). Otherwise, it calls `item_callback` every time
|
|
||||||
an item at the specified depth is found and returns `None` in the end
|
|
||||||
(streaming mode).
|
|
||||||
|
|
||||||
The callback function receives two parameters: the `path` from the document
|
|
||||||
root to the item (name-attribs pairs), and the `item` (dict). If the
|
|
||||||
callback's return value is false-ish, parsing will be stopped with the
|
|
||||||
:class:`ParsingInterrupted` exception.
|
|
||||||
|
|
||||||
Streaming example::
|
|
||||||
|
|
||||||
>>> def handle(path, item):
|
|
||||||
... print('path:%s item:%s' % (path, item))
|
|
||||||
... return True
|
|
||||||
...
|
|
||||||
>>> xmltodict.parse(\"\"\"
|
|
||||||
... <a prop="x">
|
|
||||||
... <b>1</b>
|
|
||||||
... <b>2</b>
|
|
||||||
... </a>\"\"\", item_depth=2, item_callback=handle)
|
|
||||||
path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:1
|
|
||||||
path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:2
|
|
||||||
|
|
||||||
The optional argument `postprocessor` is a function that takes `path`,
|
|
||||||
`key` and `value` as positional arguments and returns a new `(key, value)`
|
|
||||||
pair where both `key` and `value` may have changed. Usage example::
|
|
||||||
|
|
||||||
>>> def postprocessor(path, key, value):
|
|
||||||
... try:
|
|
||||||
... return key + ':int', int(value)
|
|
||||||
... except (ValueError, TypeError):
|
|
||||||
... return key, value
|
|
||||||
>>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>',
|
|
||||||
... postprocessor=postprocessor)
|
|
||||||
ObjectDict([(u'a', ObjectDict([(u'b:int', [1, 2]), (u'b', u'x')]))])
|
|
||||||
|
|
||||||
You can pass an alternate version of `expat` (such as `defusedexpat`) by
|
|
||||||
using the `expat` parameter. E.g:
|
|
||||||
|
|
||||||
>>> import defusedexpat
|
|
||||||
>>> xmltodict.parse('<a>hello</a>', expat=defusedexpat.pyexpat)
|
|
||||||
ObjectDict([(u'a', u'hello')])
|
|
||||||
|
|
||||||
You can use the force_list argument to force lists to be created even
|
|
||||||
when there is only a single child of a given level of hierarchy. The
|
|
||||||
force_list argument is a tuple of keys. If the key for a given level
|
|
||||||
of hierarchy is in the force_list argument, that level of hierarchy
|
|
||||||
will have a list as a child (even if there is only one sub-element).
|
|
||||||
The index_keys operation takes precedence over this. This is applied
|
|
||||||
after any user-supplied postprocessor has already run.
|
|
||||||
|
|
||||||
For example, given this input:
|
|
||||||
<servers>
|
|
||||||
<server>
|
|
||||||
<name>host1</name>
|
|
||||||
<os>Linux</os>
|
|
||||||
<interfaces>
|
|
||||||
<interface>
|
|
||||||
<name>em0</name>
|
|
||||||
<ip_address>10.0.0.1</ip_address>
|
|
||||||
</interface>
|
|
||||||
</interfaces>
|
|
||||||
</server>
|
|
||||||
</servers>
|
|
||||||
|
|
||||||
If called with force_list=('interface',), it will produce
|
|
||||||
this dictionary:
|
|
||||||
{'servers':
|
|
||||||
{'server':
|
|
||||||
{'name': 'host1',
|
|
||||||
'os': 'Linux'},
|
|
||||||
'interfaces':
|
|
||||||
{'interface':
|
|
||||||
[ {'name': 'em0', 'ip_address': '10.0.0.1' } ] } } }
|
|
||||||
|
|
||||||
`force_list` can also be a callable that receives `path`, `key` and
|
|
||||||
`value`. This is helpful in cases where the logic that decides whether
|
|
||||||
a list should be forced is more complex.
|
|
||||||
|
|
||||||
|
|
||||||
If `process_comment` is `True` then comment will be added with comment_key
|
|
||||||
(default=`'#comment'`) to then tag which contains comment
|
|
||||||
|
|
||||||
For example, given this input:
|
|
||||||
<a>
|
|
||||||
<b>
|
|
||||||
<!-- b comment -->
|
|
||||||
<c>
|
|
||||||
<!-- c comment -->
|
|
||||||
1
|
|
||||||
</c>
|
|
||||||
<d>2</d>
|
|
||||||
</b>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
If called with process_comment=True, it will produce
|
|
||||||
this dictionary:
|
|
||||||
'a': {
|
|
||||||
'b': {
|
|
||||||
'#comment': 'b comment',
|
|
||||||
'c': {
|
|
||||||
|
|
||||||
'#comment': 'c comment',
|
|
||||||
'#text': '1',
|
|
||||||
},
|
|
||||||
'd': '2',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
handler = _DictSAXHandler(namespace_separator=namespace_separator, **kwargs)
|
|
||||||
if isinstance(xml_input, _unicode):
|
|
||||||
if not encoding:
|
|
||||||
encoding = "utf-8"
|
|
||||||
xml_input = xml_input.encode(encoding)
|
|
||||||
if not process_namespaces:
|
|
||||||
namespace_separator = None
|
|
||||||
parser = expat.ParserCreate(encoding, namespace_separator)
|
|
||||||
try:
|
|
||||||
parser.ordered_attributes = True
|
|
||||||
except AttributeError:
|
|
||||||
# Jython's expat does not support ordered_attributes
|
|
||||||
pass
|
|
||||||
parser.StartNamespaceDeclHandler = handler.startNamespaceDecl
|
|
||||||
parser.StartElementHandler = handler.startElement
|
|
||||||
parser.EndElementHandler = handler.endElement
|
|
||||||
parser.CharacterDataHandler = handler.characters
|
|
||||||
if process_comments:
|
|
||||||
parser.CommentHandler = handler.comments
|
|
||||||
parser.buffer_text = True
|
|
||||||
if disable_entities:
|
|
||||||
try:
|
|
||||||
# Attempt to disable DTD in Jython's expat parser (Xerces-J).
|
|
||||||
feature = "http://apache.org/xml/features/disallow-doctype-decl"
|
|
||||||
parser._reader.setFeature(feature, True)
|
|
||||||
except AttributeError:
|
|
||||||
# For CPython / expat parser.
|
|
||||||
# Anything not handled ends up here and entities aren't expanded.
|
|
||||||
parser.DefaultHandler = lambda x: None
|
|
||||||
# Expects an integer return; zero means failure -> expat.ExpatError.
|
|
||||||
parser.ExternalEntityRefHandler = lambda *x: 1
|
|
||||||
if hasattr(xml_input, "read"):
|
|
||||||
parser.ParseFile(xml_input)
|
|
||||||
elif isgenerator(xml_input):
|
|
||||||
for chunk in xml_input:
|
|
||||||
parser.Parse(chunk, False)
|
|
||||||
parser.Parse(b"", True)
|
|
||||||
else:
|
|
||||||
parser.Parse(xml_input, True)
|
|
||||||
return handler.item
|
|
||||||
|
|
||||||
|
|
||||||
def _process_namespace(name, namespaces, ns_sep=":", attr_prefix="@"):
|
|
||||||
if not namespaces:
|
|
||||||
return name
|
|
||||||
try:
|
|
||||||
ns, name = name.rsplit(ns_sep, 1)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
ns_res = namespaces.get(ns.strip(attr_prefix))
|
|
||||||
name = (
|
|
||||||
"{}{}{}{}".format(
|
|
||||||
attr_prefix if ns.startswith(attr_prefix) else "", ns_res, ns_sep, name
|
|
||||||
)
|
|
||||||
if ns_res
|
|
||||||
else name
|
|
||||||
)
|
|
||||||
return name
|
|
||||||
|
|
||||||
|
|
||||||
def _emit(
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
content_handler,
|
|
||||||
attr_prefix="@",
|
|
||||||
cdata_key="#text",
|
|
||||||
depth=0,
|
|
||||||
preprocessor=None,
|
|
||||||
pretty=False,
|
|
||||||
newl="\n",
|
|
||||||
indent="\t",
|
|
||||||
namespace_separator=":",
|
|
||||||
namespaces=None,
|
|
||||||
full_document=True,
|
|
||||||
expand_iter=None,
|
|
||||||
):
|
|
||||||
key = _process_namespace(key, namespaces, namespace_separator, attr_prefix)
|
|
||||||
if preprocessor is not None:
|
|
||||||
result = preprocessor(key, value)
|
|
||||||
if result is None:
|
|
||||||
return
|
|
||||||
key, value = result
|
|
||||||
if (
|
|
||||||
not hasattr(value, "__iter__")
|
|
||||||
or isinstance(value, _basestring)
|
|
||||||
or isinstance(value, dict)
|
|
||||||
):
|
|
||||||
value = [value]
|
|
||||||
for index, v in enumerate(value):
|
|
||||||
if full_document and depth == 0 and index > 0:
|
|
||||||
raise ValueError("document with multiple roots")
|
|
||||||
if v is None:
|
|
||||||
v = ObjectDict()
|
|
||||||
elif isinstance(v, bool):
|
|
||||||
if v:
|
|
||||||
v = _unicode("true")
|
|
||||||
else:
|
|
||||||
v = _unicode("false")
|
|
||||||
elif not isinstance(v, dict):
|
|
||||||
if (
|
|
||||||
expand_iter
|
|
||||||
and hasattr(v, "__iter__")
|
|
||||||
and not isinstance(v, _basestring)
|
|
||||||
):
|
|
||||||
v = ObjectDict(((expand_iter, v),))
|
|
||||||
else:
|
|
||||||
v = _unicode(v)
|
|
||||||
if isinstance(v, _basestring):
|
|
||||||
v = ObjectDict(((cdata_key, v),))
|
|
||||||
cdata = None
|
|
||||||
attrs = ObjectDict()
|
|
||||||
children = []
|
|
||||||
for ik, iv in v.items():
|
|
||||||
if ik == cdata_key:
|
|
||||||
cdata = iv
|
|
||||||
continue
|
|
||||||
if ik.startswith(attr_prefix):
|
|
||||||
ik = _process_namespace(
|
|
||||||
ik, namespaces, namespace_separator, attr_prefix
|
|
||||||
)
|
|
||||||
if ik == "@xmlns" and isinstance(iv, dict):
|
|
||||||
for k, v in iv.items():
|
|
||||||
attr = "xmlns{}".format(":{}".format(k) if k else "")
|
|
||||||
attrs[attr] = _unicode(v)
|
|
||||||
continue
|
|
||||||
if not isinstance(iv, _unicode):
|
|
||||||
iv = _unicode(iv)
|
|
||||||
attrs[ik[len(attr_prefix) :]] = iv
|
|
||||||
continue
|
|
||||||
children.append((ik, iv))
|
|
||||||
if pretty:
|
|
||||||
content_handler.ignorableWhitespace(depth * indent)
|
|
||||||
content_handler.startElement(key, AttributesImpl(attrs))
|
|
||||||
if pretty and children:
|
|
||||||
content_handler.ignorableWhitespace(newl)
|
|
||||||
for child_key, child_value in children:
|
|
||||||
_emit(
|
|
||||||
child_key,
|
|
||||||
child_value,
|
|
||||||
content_handler,
|
|
||||||
attr_prefix,
|
|
||||||
cdata_key,
|
|
||||||
depth + 1,
|
|
||||||
preprocessor,
|
|
||||||
pretty,
|
|
||||||
newl,
|
|
||||||
indent,
|
|
||||||
namespaces=namespaces,
|
|
||||||
namespace_separator=namespace_separator,
|
|
||||||
expand_iter=expand_iter,
|
|
||||||
)
|
|
||||||
if cdata is not None:
|
|
||||||
content_handler.characters(cdata)
|
|
||||||
if pretty and children:
|
|
||||||
content_handler.ignorableWhitespace(depth * indent)
|
|
||||||
content_handler.endElement(key)
|
|
||||||
if pretty and depth:
|
|
||||||
content_handler.ignorableWhitespace(newl)
|
|
||||||
|
|
||||||
|
|
||||||
def unparse(
|
|
||||||
input_dict,
|
|
||||||
output=None,
|
|
||||||
encoding="utf-8",
|
|
||||||
full_document=True,
|
|
||||||
short_empty_elements=False,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""Emit an XML document for the given `input_dict` (reverse of `parse`).
|
|
||||||
|
|
||||||
The resulting XML document is returned as a string, but if `output` (a
|
|
||||||
file-like object) is specified, it is written there instead.
|
|
||||||
|
|
||||||
Dictionary keys prefixed with `attr_prefix` (default=`'@'`) are interpreted
|
|
||||||
as XML node attributes, whereas keys equal to `cdata_key`
|
|
||||||
(default=`'#text'`) are treated as character data.
|
|
||||||
|
|
||||||
The `pretty` parameter (default=`False`) enables pretty-printing. In this
|
|
||||||
mode, lines are terminated with `'\n'` and indented with `'\t'`, but this
|
|
||||||
can be customized with the `newl` and `indent` parameters.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if full_document and len(input_dict) != 1:
|
|
||||||
raise ValueError("Document must have exactly one root.")
|
|
||||||
must_return = False
|
|
||||||
if output is None:
|
|
||||||
output = StringIO()
|
|
||||||
must_return = True
|
|
||||||
if short_empty_elements:
|
|
||||||
content_handler = XMLGenerator(output, encoding, True)
|
|
||||||
else:
|
|
||||||
content_handler = XMLGenerator(output, encoding)
|
|
||||||
if full_document:
|
|
||||||
content_handler.startDocument()
|
|
||||||
for key, value in input_dict.items():
|
|
||||||
_emit(key, value, content_handler, full_document=full_document, **kwargs)
|
|
||||||
if full_document:
|
|
||||||
content_handler.endDocument()
|
|
||||||
if must_return:
|
|
||||||
value = output.getvalue()
|
|
||||||
try: # pragma no cover
|
|
||||||
value = value.decode(encoding)
|
|
||||||
except AttributeError: # pragma no cover
|
|
||||||
pass
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": # pragma: no cover
|
|
||||||
import sys
|
|
||||||
import marshal
|
|
||||||
|
|
||||||
try:
|
|
||||||
stdin = sys.stdin.buffer
|
|
||||||
stdout = sys.stdout.buffer
|
|
||||||
except AttributeError:
|
|
||||||
stdin = sys.stdin
|
|
||||||
stdout = sys.stdout
|
|
||||||
|
|
||||||
(item_depth,) = sys.argv[1:]
|
|
||||||
item_depth = int(item_depth)
|
|
||||||
|
|
||||||
def handle_item(path, item):
|
|
||||||
marshal.dump((path, item), stdout)
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
root = parse(
|
|
||||||
stdin,
|
|
||||||
item_depth=item_depth,
|
|
||||||
item_callback=handle_item,
|
|
||||||
dict_constructor=dict,
|
|
||||||
)
|
|
||||||
if item_depth == 0:
|
|
||||||
handle_item([], root)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
@ -29,7 +29,6 @@ from FreeRTOSgdb.GDBCommands import ShowQueueInfo
|
|||||||
|
|
||||||
class Scheduler:
|
class Scheduler:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self._blocked = ListInspector("xSuspendedTaskList")
|
self._blocked = ListInspector("xSuspendedTaskList")
|
||||||
self._delayed1 = ListInspector("xDelayedTaskList1")
|
self._delayed1 = ListInspector("xDelayedTaskList1")
|
||||||
self._delayed2 = ListInspector("xDelayedTaskList2")
|
self._delayed2 = ListInspector("xDelayedTaskList2")
|
@ -61,7 +61,6 @@ class ShowQueueInfo(gdb.Command):
|
|||||||
if maxCount == 0:
|
if maxCount == 0:
|
||||||
print(outputFmt % (q.GetName(), q.GetQueueMessagesWaiting(), "", ""))
|
print(outputFmt % (q.GetName(), q.GetQueueMessagesWaiting(), "", ""))
|
||||||
else:
|
else:
|
||||||
|
|
||||||
for i in range(0, maxCount):
|
for i in range(0, maxCount):
|
||||||
txName = ""
|
txName = ""
|
||||||
if i < len(sendList):
|
if i < len(sendList):
|
@ -48,7 +48,6 @@ class HandleRegistry:
|
|||||||
print("%d: %3s %16s" % (i, h, name))
|
print("%d: %3s %16s" % (i, h, name))
|
||||||
|
|
||||||
def FilterBy(self, qMode):
|
def FilterBy(self, qMode):
|
||||||
|
|
||||||
"""Retrieve a List of Mutex Queue Handles"""
|
"""Retrieve a List of Mutex Queue Handles"""
|
||||||
resp = []
|
resp = []
|
||||||
for i in range(self._minIndex, self._maxIndex):
|
for i in range(self._minIndex, self._maxIndex):
|
@ -56,7 +56,6 @@ class ListInspector:
|
|||||||
of some of the TCB Task lists.
|
of some of the TCB Task lists.
|
||||||
"""
|
"""
|
||||||
if self._list != None:
|
if self._list != None:
|
||||||
|
|
||||||
CastType = None
|
CastType = None
|
||||||
if CastTypeStr != None:
|
if CastTypeStr != None:
|
||||||
if type(CastTypeStr) == str:
|
if type(CastTypeStr) == str:
|
||||||
@ -73,7 +72,6 @@ class ListInspector:
|
|||||||
index = self._list["pxIndex"]
|
index = self._list["pxIndex"]
|
||||||
|
|
||||||
if numElems > 0 and numElems < 200:
|
if numElems > 0 and numElems < 200:
|
||||||
|
|
||||||
if startElem == 0:
|
if startElem == 0:
|
||||||
curr = index
|
curr = index
|
||||||
else:
|
else:
|
@ -47,7 +47,6 @@ QueueMode.Map = QueueMap
|
|||||||
|
|
||||||
|
|
||||||
class QueueInspector:
|
class QueueInspector:
|
||||||
|
|
||||||
QueueType = gdb.lookup_type("Queue_t")
|
QueueType = gdb.lookup_type("Queue_t")
|
||||||
|
|
||||||
def __init__(self, handle):
|
def __init__(self, handle):
|
@ -11,7 +11,6 @@ import gdb
|
|||||||
|
|
||||||
|
|
||||||
class TaskInspector:
|
class TaskInspector:
|
||||||
|
|
||||||
TCBType = gdb.lookup_type("TCB_t")
|
TCBType = gdb.lookup_type("TCB_t")
|
||||||
|
|
||||||
def __init__(self, handle):
|
def __init__(self, handle):
|
@ -28,7 +28,5 @@ directory = path.abspath(directory)
|
|||||||
sys.path.append(directory)
|
sys.path.append(directory)
|
||||||
|
|
||||||
from cmdebug.svd_gdb import LoadSVD
|
from cmdebug.svd_gdb import LoadSVD
|
||||||
from cmdebug.dwt_gdb import DWT
|
|
||||||
|
|
||||||
DWT()
|
|
||||||
LoadSVD()
|
LoadSVD()
|
35
scripts/debug/PyCortexMDebug/README.md
Normal file
35
scripts/debug/PyCortexMDebug/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
PyCortexMDebug
|
||||||
|
==============
|
||||||
|
|
||||||
|
## SVD
|
||||||
|
|
||||||
|
ARM defines an SVD (System View Description) file format in its CMSIS standard as a means for Cortex-M-based chip manufacturers to provide a common description of peripherals, registers, and register fields. You can download SVD files for different manufacturers [here](http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php).
|
||||||
|
|
||||||
|
The implementation consists of two components -- An lxml-based parser module (pysvd) and a GDB file (gdb_svd). I haven't yet worked out a perfect workflow for this, though it's quite easy to use when you already tend to have a GDB initialization file for starting up OpenOCD and the like. However your workflow works, just make sure to, in GDB:
|
||||||
|
|
||||||
|
source gdb_svd.py
|
||||||
|
svd_load [your_svd_file].svd
|
||||||
|
|
||||||
|
These files can be huge so it might take a second or two. Anyways, after that, you can do
|
||||||
|
|
||||||
|
svd
|
||||||
|
|
||||||
|
to list available peripherals with descriptions. Or you can do
|
||||||
|
|
||||||
|
svd [some_peripheral_name]
|
||||||
|
|
||||||
|
to see all of the registers (with their values) for a given peripheral. For more details, run
|
||||||
|
|
||||||
|
svd [some_peripheral_name] [some_register_name]
|
||||||
|
|
||||||
|
to see all of the field values with descriptions.
|
||||||
|
|
||||||
|
You can add format modifiers like:
|
||||||
|
|
||||||
|
* `svd/x` will display values in hex
|
||||||
|
* `svd/o` will display values in octal
|
||||||
|
* `svd/t` or `svd/b` will display values in binary
|
||||||
|
* `svd/a` will display values in hex and try to resolve symbols from the values
|
||||||
|
|
||||||
|
All field values are displayed at the correct lengths as provided by the SVD files.
|
||||||
|
Also, tab completion exists for nearly everything! When in doubt, run `svd help`.
|
@ -16,15 +16,14 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
|
along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from collections import OrderedDict
|
import lxml.objectify as objectify
|
||||||
from . import x2d
|
|
||||||
|
|
||||||
import traceback
|
|
||||||
import warnings
|
|
||||||
import pickle
|
|
||||||
import sys
|
import sys
|
||||||
|
from collections import OrderedDict
|
||||||
import os
|
import os
|
||||||
|
import pickle
|
||||||
|
import traceback
|
||||||
import re
|
import re
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
class SmartDict:
|
class SmartDict:
|
||||||
@ -127,31 +126,26 @@ class SVDFile:
|
|||||||
|
|
||||||
def __init__(self, fname):
|
def __init__(self, fname):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
fname: Filename for the SVD file
|
fname: Filename for the SVD file
|
||||||
"""
|
"""
|
||||||
|
f = objectify.parse(os.path.expanduser(fname))
|
||||||
|
root = f.getroot()
|
||||||
|
periph = root.peripherals.getchildren()
|
||||||
self.peripherals = SmartDict()
|
self.peripherals = SmartDict()
|
||||||
self.base_address = 0
|
self.base_address = 0
|
||||||
|
|
||||||
xml_file_name = os.path.expanduser(fname)
|
|
||||||
pickle_file_name = xml_file_name + ".pickle"
|
|
||||||
root = None
|
|
||||||
if os.path.exists(pickle_file_name):
|
|
||||||
print("Loading pickled SVD")
|
|
||||||
root = pickle.load(open(pickle_file_name, "rb"))
|
|
||||||
else:
|
|
||||||
print("Loading XML SVD and pickling it")
|
|
||||||
root = x2d.parse(open(xml_file_name, "rb"))
|
|
||||||
pickle.dump(root, open(pickle_file_name, "wb"), pickle.HIGHEST_PROTOCOL)
|
|
||||||
print("Processing SVD tree")
|
|
||||||
# XML elements
|
# XML elements
|
||||||
for p in root["device"]["peripherals"]["peripheral"]:
|
for p in periph:
|
||||||
try:
|
try:
|
||||||
self.peripherals[p["name"]] = SVDPeripheral(p, self)
|
if p.tag == "peripheral":
|
||||||
|
self.peripherals[str(p.name)] = SVDPeripheral(p, self)
|
||||||
|
else:
|
||||||
|
# This is some other tag
|
||||||
|
pass
|
||||||
except SVDNonFatalError as e:
|
except SVDNonFatalError as e:
|
||||||
# print(e)
|
print(e)
|
||||||
pass
|
|
||||||
print("SVD Ready")
|
|
||||||
|
|
||||||
|
|
||||||
def add_register(parent, node):
|
def add_register(parent, node):
|
||||||
@ -271,11 +265,11 @@ class SVDPeripheral:
|
|||||||
self.parent_base_address = parent.base_address
|
self.parent_base_address = parent.base_address
|
||||||
|
|
||||||
# Look for a base address, as it is required
|
# Look for a base address, as it is required
|
||||||
if "baseAddress" not in svd_elem:
|
if not hasattr(svd_elem, "baseAddress"):
|
||||||
raise SVDNonFatalError("Periph without base address")
|
raise SVDNonFatalError("Periph without base address")
|
||||||
self.base_address = int(str(svd_elem.baseAddress), 0)
|
self.base_address = int(str(svd_elem.baseAddress), 0)
|
||||||
if "@derivedFrom" in svd_elem:
|
if "derivedFrom" in svd_elem.attrib:
|
||||||
derived_from = svd_elem["@derivedFrom"]
|
derived_from = svd_elem.attrib["derivedFrom"]
|
||||||
try:
|
try:
|
||||||
self.name = str(svd_elem.name)
|
self.name = str(svd_elem.name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -301,14 +295,16 @@ class SVDPeripheral:
|
|||||||
self.clusters = SmartDict()
|
self.clusters = SmartDict()
|
||||||
|
|
||||||
if hasattr(svd_elem, "registers"):
|
if hasattr(svd_elem, "registers"):
|
||||||
if "register" in svd_elem.registers:
|
registers = [
|
||||||
for r in svd_elem.registers.register:
|
r
|
||||||
if isinstance(r, x2d.ObjectDict):
|
for r in svd_elem.registers.getchildren()
|
||||||
add_register(self, r)
|
if r.tag in ["cluster", "register"]
|
||||||
if "cluster" in svd_elem.registers:
|
]
|
||||||
for c in svd_elem.registers.cluster:
|
for r in registers:
|
||||||
if isinstance(c, x2d.ObjectDict):
|
if r.tag == "cluster":
|
||||||
add_cluster(self, c)
|
add_cluster(self, r)
|
||||||
|
elif r.tag == "register":
|
||||||
|
add_register(self, r)
|
||||||
|
|
||||||
def refactor_parent(self, parent):
|
def refactor_parent(self, parent):
|
||||||
self.parent_base_address = parent.base_address
|
self.parent_base_address = parent.base_address
|
||||||
@ -342,11 +338,11 @@ class SVDPeripheralRegister:
|
|||||||
else:
|
else:
|
||||||
self.size = 0x20
|
self.size = 0x20
|
||||||
self.fields = SmartDict()
|
self.fields = SmartDict()
|
||||||
if "fields" in svd_elem:
|
if hasattr(svd_elem, "fields"):
|
||||||
# Filter fields to only consider those of tag "field"
|
# Filter fields to only consider those of tag "field"
|
||||||
for f in svd_elem.fields.field:
|
fields = [f for f in svd_elem.fields.getchildren() if f.tag == "field"]
|
||||||
if isinstance(f, x2d.ObjectDict):
|
for f in fields:
|
||||||
self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self)
|
self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self)
|
||||||
|
|
||||||
def refactor_parent(self, parent):
|
def refactor_parent(self, parent):
|
||||||
self.parent_base_address = parent.base_address
|
self.parent_base_address = parent.base_address
|
@ -18,7 +18,7 @@ def GetDevices(env):
|
|||||||
def generate(env, **kw):
|
def generate(env, **kw):
|
||||||
env.AddMethod(GetDevices)
|
env.AddMethod(GetDevices)
|
||||||
env.SetDefault(
|
env.SetDefault(
|
||||||
FBT_DEBUG_DIR="${ROOT_DIR}/debug",
|
FBT_DEBUG_DIR="${FBT_SCRIPT_DIR}/debug",
|
||||||
)
|
)
|
||||||
|
|
||||||
if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto":
|
if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto":
|
||||||
|
@ -170,7 +170,6 @@ class Main(App):
|
|||||||
"update.dir",
|
"update.dir",
|
||||||
"sdk_headers.dir",
|
"sdk_headers.dir",
|
||||||
"lib.dir",
|
"lib.dir",
|
||||||
"debug.dir",
|
|
||||||
"scripts.dir",
|
"scripts.dir",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -186,6 +186,33 @@ dist_env.PhonyTarget(
|
|||||||
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Debug alien elf
|
||||||
|
debug_other_opts = [
|
||||||
|
"-ex",
|
||||||
|
"source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py",
|
||||||
|
"-ex",
|
||||||
|
"source ${FBT_DEBUG_DIR}/flipperversion.py",
|
||||||
|
"-ex",
|
||||||
|
"fw-version",
|
||||||
|
]
|
||||||
|
|
||||||
|
dist_env.PhonyTarget(
|
||||||
|
"debug_other",
|
||||||
|
"${GDBPYCOM}",
|
||||||
|
GDBOPTS="${GDBOPTS_BASE}",
|
||||||
|
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||||
|
GDBPYOPTS=debug_other_opts,
|
||||||
|
)
|
||||||
|
|
||||||
|
dist_env.PhonyTarget(
|
||||||
|
"debug_other_blackmagic",
|
||||||
|
"${GDBPYCOM}",
|
||||||
|
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||||
|
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||||
|
GDBPYOPTS=debug_other_opts,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
dist_env.PhonyTarget(
|
dist_env.PhonyTarget(
|
||||||
"flash_blackmagic",
|
"flash_blackmagic",
|
||||||
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
|
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
|
||||||
|
@ -78,10 +78,8 @@ def generate(env, **kw):
|
|||||||
env.SetDefault(
|
env.SetDefault(
|
||||||
# Paths
|
# Paths
|
||||||
SDK_DEFINITION=env.File(sdk_data["sdk_symbols"]),
|
SDK_DEFINITION=env.File(sdk_data["sdk_symbols"]),
|
||||||
FBT_DEBUG_DIR=pathlib.Path(
|
|
||||||
sdk_current_sdk_dir_node.Dir(sdk_components["debug.dir"]).abspath
|
|
||||||
).as_posix(),
|
|
||||||
FBT_SCRIPT_DIR=scripts_dir,
|
FBT_SCRIPT_DIR=scripts_dir,
|
||||||
|
FBT_DEBUG_DIR=scripts_dir.Dir("debug"),
|
||||||
LIBPATH=sdk_current_sdk_dir_node.Dir(sdk_components["lib.dir"]),
|
LIBPATH=sdk_current_sdk_dir_node.Dir(sdk_components["lib.dir"]),
|
||||||
FW_ELF=sdk_current_sdk_dir_node.File(sdk_components["firmware.elf"]),
|
FW_ELF=sdk_current_sdk_dir_node.File(sdk_components["firmware.elf"]),
|
||||||
FW_BIN=sdk_current_sdk_dir_node.File(sdk_components["full.bin"]),
|
FW_BIN=sdk_current_sdk_dir_node.File(sdk_components["full.bin"]),
|
||||||
|
Loading…
Reference in New Issue
Block a user