You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2020/11/25 12:08:02 UTC

[mynewt-core] 05/05: hw/scripts: Add GDB script to handle MTB

This is an automated email from the ASF dual-hosted git repository.

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 5f5cb2bfd6c4d9f4bdd18f566c380cf4f0164b1b
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Mon Nov 23 17:46:18 2020 +0100

    hw/scripts: Add GDB script to handle MTB
    
    This adds script for GDB to easily analyze Micro Trace Buffer (MTB)
    contents. A new "mtb" command is available in GDB:
    
    - "mtb base" -> set base address for MTB, default is 0xe0043000 (CM33)
    - "mtb enable" -> enables MTB (has to be already configured!)
    - "mtb disable" -> disables MTB
    - "mtb decode" -> decodes current MTB contents; it prints complete
                      disassembled trace along with source code if proper
                      symbol file is loaded and sources are available
    - "mtb colors <on/off>" -> enables/disables colors in decoder output,
                               disabled by default and requires colorama
                               module
    
    As a convenient shortcut, plain "mtb" command defaults to "mtb decode".
    That means if MCU has MTB already configured and enabled, it's enough
    to just issue that command to get valid output.
---
 hw/scripts/micro-trace-buffer.py | 227 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 227 insertions(+)

diff --git a/hw/scripts/micro-trace-buffer.py b/hw/scripts/micro-trace-buffer.py
new file mode 100644
index 0000000..80df78e
--- /dev/null
+++ b/hw/scripts/micro-trace-buffer.py
@@ -0,0 +1,227 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+try:
+    import colorama
+except ImportError:
+    pass
+import re
+
+
+class MicroTraceBuffer(gdb.Command):
+    _arch = None
+    _file = None
+    _func = None
+    _line = None
+    _src_file = None
+    _src_lines = None
+
+    _mtb_base_addr = 0xe0043000
+
+    _colorize = False
+    _colors = {}
+
+    def __init__(self):
+        super(MicroTraceBuffer, self).__init__("mtb", gdb.COMMAND_STATUS, gdb.COMPLETE_NONE)
+        if colorama:
+            self._colors = {"A": colorama.Fore.BLUE,
+                            "D": colorama.Fore.RESET,
+                            "E": colorama.Fore.GREEN,
+                            "F": colorama.Fore.YELLOW,
+                            "L": colorama.Fore.RESET,
+                            "S": colorama.Fore.RESET,
+                            "X": colorama.Fore.RED}
+
+    def _print(self, s: str):
+        def repl(m):
+            if m.group(1) in self._colors:
+                return self._colors[m.group(1)]
+            else:
+                return colorama.Fore.RESET
+
+        if not colorama or not self._colorize:
+            s = re.sub(r"~([A-Z])~", "", s)
+        else:
+            s = re.sub(r"~([A-Z])~", repl, s) + colorama.Style.RESET_ALL
+        print(s)
+
+    def _get_src(self, file, line):
+        if file != self._src_file:
+            self._src_file = file
+            try:
+                f = open(file, "r")
+                self._src_lines = f.readlines()
+            except OSError:
+                self._src_lines = None
+
+        if not self._src_lines:
+            return "<not found>"
+
+        src = self._src_lines[line - 1].strip()
+        return src
+
+    def _describe_instr(self, addr: int, asm: str):
+        blk = gdb.block_for_pc(addr)
+        while blk and not blk.function:
+            blk = blk.superblock
+        func = str(blk.function) if blk else "<unknown>"
+
+        pcl = gdb.find_pc_line(addr)
+        file = pcl.symtab.filename if pcl.symtab else "<unknown>"
+        line = pcl.line
+
+        if (self._file != file) or (self._func != func):
+            self._file = file
+            self._func = func
+            self._print(f"~F~{func} ~R~@ ~E~{file}")
+
+        if self._line != line:
+            self._line = line
+            if pcl.symtab:
+                fname = pcl.symtab.fullname()
+                src = self._get_src(fname, line)
+                self._print(f"~L~{line}\t~S~{src}")
+
+        self._print(f"    ~A~0x{addr:08x}:    ~D~{asm}")
+
+    def _disassemble(self, start: int, end: int):
+        disasm = self._arch.disassemble(start, end)
+        for instr in disasm:
+            addr = instr["addr"]
+            asm = instr["asm"].expandtabs(8)
+            self._describe_instr(addr, asm)
+        print()
+
+    def _cmd_decode(self):
+        print(f"NOTE: using 0x{self._mtb_base_addr:08x} as MTB base address")
+        print()
+
+        u32_ptr_t = gdb.lookup_type("uint32_t").pointer()
+        mtb_reg_pos = int(gdb.Value(self._mtb_base_addr).cast(u32_ptr_t).dereference())
+        mtb_reg_master = int(gdb.Value(self._mtb_base_addr + 0x04).cast(u32_ptr_t).dereference())
+        mtb_reg_base = int(gdb.Value(self._mtb_base_addr + 0x0c).cast(u32_ptr_t).dereference())
+
+        # size of trace buffer
+        mtb_size = 1 << ((mtb_reg_master & 0x1f) + 4)
+
+        # check buffer position
+        mtb_wrap = bool(mtb_reg_pos & 0x04)
+        mtb_reg_pos = mtb_reg_pos & 0xfffffff8
+
+        # if pointer already wrapped, we start at current entry and process complete buffer,
+        # otherwise we start at beginning and process up to current entry
+        mtb_size = mtb_size if mtb_wrap else mtb_reg_pos
+        mtb_reg_pos = mtb_reg_pos if mtb_wrap else 0
+
+        # use current frame to get architecture for later disassembling
+        self._arch = gdb.newest_frame().architecture()
+
+        mtb_pkt_dst = None
+
+        for pos in range(0, mtb_size, 8):
+            exec_begin = mtb_pkt_dst
+
+            mtb_pkt = mtb_reg_base + (mtb_reg_pos + pos) % mtb_size
+            mtb_pkt_src = int(gdb.Value(mtb_pkt).cast(u32_ptr_t).dereference())
+            mtb_pkt_dst = int(gdb.Value(mtb_pkt + 4).cast(u32_ptr_t).dereference())
+
+            bit_a = mtb_pkt_src & 1
+            bit_s = mtb_pkt_dst & 1
+
+            mtb_pkt_src = mtb_pkt_src & 0xfffffffe
+            mtb_pkt_dst = mtb_pkt_dst & 0xfffffffe
+
+            # print(f"pkt> 0x{mtb_pkt_src:08x} -> 0x{mtb_pkt_dst:08x} A:{bit_a} S:{bit_s}")
+
+            exec_end = mtb_pkt_src
+
+            # exception entry/exit events are handled separately
+            if bit_a != 0:
+                if mtb_pkt_src & 0xff000000 == 0xff000000:
+                    self._print(f"~X~Exception return (LR: 0x{mtb_pkt_src:08x}, "
+                                f"ret address: 0x{mtb_pkt_dst:08x})")
+                else:
+                    self._print(f"~X~Exception entry (ret address: 0x{mtb_pkt_src:08x})")
+                print()
+                continue
+
+            # on 1st entry in trace buffer, disassemble source of the branch
+            if exec_begin is None:
+                self._disassemble(mtb_pkt_src, mtb_pkt_src)
+                continue
+
+            # on 1st entry after MTB was enabled, disasssemble source of the branch
+            if bit_s != 0:
+                self._disassemble(mtb_pkt_src, mtb_pkt_src)
+                continue
+
+            # print(f"exe> 0x{exec_begin:08x} -> 0x{exec_end:08x}")
+
+            self._disassemble(exec_begin, exec_end)
+
+        # disassemble target of last branch
+        self._disassemble(mtb_pkt_dst, mtb_pkt_dst)
+
+    def _cmd_colors(self, args):
+        for arg in args:
+            if arg == "on":
+                self._colorize = True
+            elif arg == "off":
+                self._colorize = False
+            else:
+                # TODO: handle colors configuration
+                pass
+
+    def _cmd_enable(self):
+        u32_ptr_t = gdb.lookup_type("uint32_t").pointer()
+        addr = self._mtb_base_addr + 0x04
+        mtb_reg_master = int(gdb.Value(addr).cast(u32_ptr_t).dereference())
+        mtb_reg_master = mtb_reg_master | 0x80000000
+        gdb.execute(f"set *0x{addr:08x} = 0x{mtb_reg_master:08x}")
+
+    def _cmd_disable(self):
+        u32_ptr_t = gdb.lookup_type("uint32_t").pointer()
+        addr = self._mtb_base_addr + 0x04
+        mtb_reg_master = int(gdb.Value(addr).cast(u32_ptr_t).dereference())
+        mtb_reg_master = mtb_reg_master & 0x7fffffff
+        gdb.execute(f"set *0x{addr:08x} = 0x{mtb_reg_master:08x}")
+
+    def _cmd_base(self, arg):
+        try:
+            addr = int(arg, 0)
+            self._mtb_base_addr = addr
+            print(f"MTB base address set to 0x{self._mtb_base_addr:08x}")
+        except ValueError:
+            print(f"Invalid value for MTB base address")
+
+    def invoke(self, arg: str, from_tty: bool):
+        args = arg.split(" ")
+        if len(args[0]) == 0 or args[0] == "decode":
+            self._cmd_decode()
+        elif args[0] == "colors":
+            self._cmd_colors(args[1:])
+        elif args[0] == "enable":
+            self._cmd_enable()
+        elif args[0] == "disable":
+            self._cmd_disable()
+        elif args[0] == "base":
+            self._cmd_base(args[1])
+        else:
+            print("wut?")
+
+
+MicroTraceBuffer()