blob: d90467d838d17e6abea2393de83922664d7fef1a [file] [log] [blame]
# 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:
colorama = None
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)
try:
mtb_state_at_crash = gdb.lookup_symbol("mtb_state_at_crash")[0]
if mtb_state_at_crash is not None and int(gdb.parse_and_eval('mtb_state_at_crash.mtb_base_reg')) != 0:
self._mtb_base_addr = int(gdb.parse_and_eval('mtb_state_at_crash').address)
except gdb.error:
pass
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):
try:
disasm = self._arch.disassemble(start, end)
for instr in disasm:
addr = instr["addr"]
asm = instr["asm"].expandtabs(8)
self._describe_instr(addr, asm)
except gdb.MemoryError:
print(f"Error disassembling 0x{start:08x}-0x{end:08x}")
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})")
print()
continue
else:
self._print(f"~X~Exception entry (ret address: 0x{mtb_pkt_src:08x})")
# 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)
if bit_a != 0:
self._print(f"~X~Exception started")
# 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()