This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new e597f38 tools:use dump log creat a gdbsever to debug
e597f38 is described below
commit e597f3812d6987506f9c009f35fb99b67a1add2c
Author: anjiahao <[email protected]>
AuthorDate: Mon Feb 28 16:03:43 2022 +0800
tools:use dump log creat a gdbsever to debug
this is a tool,when crash help you to debug.
Signed-off-by: anjiahao <[email protected]>
---
tools/minidumpserver.py | 520 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 520 insertions(+)
diff --git a/tools/minidumpserver.py b/tools/minidumpserver.py
new file mode 100755
index 0000000..9800708
--- /dev/null
+++ b/tools/minidumpserver.py
@@ -0,0 +1,520 @@
+#!/usr/bin/env python3
+# tools/minidumpserver.py
+#
+# 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.
+#
+
+import argparse
+import binascii
+import logging
+import os
+import re
+import socket
+import struct
+import sys
+
+import elftools
+from elftools.elf.elffile import ELFFile
+
+# ELF section flags
+SHF_WRITE = 0x1
+SHF_ALLOC = 0x2
+SHF_EXEC = 0x4
+SHF_WRITE_ALLOC = SHF_WRITE | SHF_ALLOC
+SHF_ALLOC_EXEC = SHF_ALLOC | SHF_EXEC
+
+
+logger = logging.getLogger()
+
+
+class dump_elf_file:
+ """
+ Class to parse ELF file for memory content in various sections.
+ There are read-only sections (e.g. text and rodata) where
+ the memory content does not need to be dumped via coredump
+ and can be retrived from the ELF file.
+ """
+
+ def __init__(self, elffile):
+ self.elffile = elffile
+ self.fd = None
+ self.elf = None
+ self.memories = list()
+
+ def open(self):
+ self.fd = open(self.elffile, "rb")
+ self.elf = ELFFile(self.fd)
+
+ def close(self):
+ self.fd.close()
+
+ def parse(self):
+ if self.fd is None:
+ self.open()
+
+ for section in self.elf.iter_sections():
+ # REALLY NEED to match exact type as all other sections
+ # (debug, text, etc.) are descendants where
+ # isinstance() would match.
+ if (
+ type(section) is not elftools.elf.sections.Section
+ ): # pylint: disable=unidiomatic-typecheck
+ continue
+
+ size = section["sh_size"]
+ flags = section["sh_flags"]
+ start = section["sh_addr"]
+ end = start + size - 1
+
+ store = False
+ desc = "?"
+
+ if section["sh_type"] == "SHT_PROGBITS":
+ if (flags & SHF_ALLOC_EXEC) == SHF_ALLOC_EXEC:
+ # Text section
+ store = True
+ desc = "text"
+ elif (flags & SHF_WRITE_ALLOC) == SHF_WRITE_ALLOC:
+ # Data section
+ #
+ # Running app changes the content so no need
+ # to store
+ pass
+ elif (flags & SHF_ALLOC) == SHF_ALLOC:
+ # Read only data section
+ store = True
+ desc = "read-only data"
+
+ if store:
+ memory = {"start": start, "end": end, "data": section.data()}
+ logger.info(
+ "ELF Section: 0x%x to 0x%x of size %d (%s)"
+ % (memory["start"], memory["end"], len(memory["data"]),
desc)
+ )
+
+ self.memories.append(memory)
+
+ return True
+
+
+reg_table = {
+ "arm": {
+ "R0": 0,
+ "R1": 1,
+ "R2": 2,
+ "R3": 3,
+ "R4": 4,
+ "R5": 5,
+ "R6": 6,
+ "FP": 7,
+ "R8": 8,
+ "SB": 9,
+ "SL": 10,
+ "R11": 11,
+ "IP": 12,
+ "SP": 13,
+ "LR": 14,
+ "PC": 15,
+ "xPSR": 16,
+ },
+ "riscv": {
+ "ZERO": 0,
+ "RA": 1,
+ "SP": 2,
+ "GP": 3,
+ "TP": 4,
+ "T0": 5,
+ "T1": 6,
+ "T2": 7,
+ "FP": 8,
+ "S1": 9,
+ "A0": 10,
+ "A1": 11,
+ "A2": 12,
+ "A3": 13,
+ "A4": 14,
+ "A5": 15,
+ "A6": 16,
+ "A7": 17,
+ "S2": 18,
+ "S3": 19,
+ "S4": 20,
+ "S5": 21,
+ "S6": 22,
+ "S7": 23,
+ "S8": 24,
+ "S9": 25,
+ "S10": 26,
+ "S11": 27,
+ "T3": 28,
+ "T4": 29,
+ "T5": 30,
+ "T6": 31,
+ "PC": 32,
+ },
+}
+
+
+class dump_log_file:
+ def __init__(self, logfile):
+ self.logfile = logfile
+ self.fd = None
+ self.arch = ""
+ self.registers = []
+ self.memories = list()
+
+ def open(self):
+ self.fd = open(self.logfile, "r")
+
+ def close(self):
+ self.fd.closeself()
+
+ def parse(self):
+ data = bytes()
+ start = 0
+ if self.fd is None:
+ self.open()
+ while 1:
+ line = self.fd.readline()
+ if line == "":
+ break
+
+ tmp = re.search(r"([^ ]*)_registerdump:?", line)
+ if tmp is not None:
+ # find arch
+ self.arch = tmp.group(1)
+ if self.arch not in reg_table:
+ logger.error("%s not supported" % (self.arch))
+ # init register list
+ if len(self.registers) == 0:
+ for x in range(len(reg_table[self.arch])):
+ self.registers.append(b"x")
+
+ # find register value
+ line = line[tmp.span()[1] :]
+ line = line.replace("\n", " ")
+ while 1:
+ tmp = re.search("([^ ]+):", line)
+ if tmp is None:
+ break
+ register = tmp.group(1)
+ line = line[tmp.span()[1] :]
+ tmp = re.search("([0-9a-fA-F]+) ", line)
+ if tmp is None:
+ break
+ if register in reg_table[self.arch].keys():
+ self.registers[reg_table[self.arch][register]] = int(
+ "0x" + tmp.group().replace(" ", ""), 16
+ )
+ line = line[tmp.span()[1] :]
+ continue
+
+ tmp = re.search("_stackdump:", line)
+ if tmp is not None:
+ # find stackdump
+ line = line[tmp.span()[1] :]
+ tmp = re.search("([0-9a-fA-F]+):", line)
+ if tmp is not None:
+ line_start = int("0x" + tmp.group()[:-1], 16)
+
+ if start + len(data) != line_start:
+ # stack is not contiguous
+ if len(data) == 0:
+ start = line_start
+ else:
+ memory = {
+ "start": start,
+ "end": start + len(data),
+ "data": data,
+ }
+ self.memories.append(memory)
+ data = b""
+ start = line_start
+
+ line = line[tmp.span()[1] :]
+ line = line.replace("\n", " ")
+
+ while 1:
+ # record stack value
+ tmp = re.search(" ([0-9a-fA-F]+)", line)
+ if tmp is None:
+ break
+ data = data + struct.pack(
+ "<I", int("0x" + tmp.group().replace(" ", ""), 16)
+ )
+ line = line[tmp.span()[1] :]
+
+ if len(data):
+ memory = {"start": start, "end": start + len(data), "data": data}
+ self.memories.append(memory)
+
+
+GDB_SIGNAL_DEFAULT = 7
+
+
+class gdb_stub:
+ def __init__(self, logfile, elffile):
+ self.logfile = logfile
+ self.elffile = elffile
+ self.socket = None
+ self.gdb_signal = GDB_SIGNAL_DEFAULT
+ self.mem_regions = self.elffile.memories + self.logfile.memories
+
+ def get_gdb_packet(self):
+ socket = self.socket
+ if socket is None:
+ return None
+
+ data = b""
+ checksum = 0
+ # Wait for '$'
+ while True:
+ ch = socket.recv(1)
+ if ch == b"$":
+ break
+
+ # Get a full packet
+ while True:
+ ch = socket.recv(1)
+ if ch == b"#":
+ # End of packet
+ break
+
+ checksum += ord(ch)
+ data += ch
+
+ # Get checksum (2-bytes)
+ ch = socket.recv(2)
+ in_chksum = ord(binascii.unhexlify(ch))
+
+ logger.debug(f"Received GDB packet: {data}")
+
+ if (checksum % 256) == in_chksum:
+ # ACK
+ logger.debug("ACK")
+ socket.send(b"+")
+
+ return data
+ else:
+ # NACK
+ logger.debug(f"NACK (checksum {in_chksum} != {checksum}")
+ socket.send(b"-")
+
+ return None
+
+ def put_gdb_packet(self, data):
+ socket = self.socket
+ if socket is None:
+ return
+
+ checksum = 0
+ for d in data:
+ checksum += d
+
+ pkt = b"$" + data + b"#"
+
+ checksum = checksum % 256
+ pkt += format(checksum, "02X").encode()
+
+ logger.debug(f"Sending GDB packet: {pkt}")
+
+ socket.send(pkt)
+
+ def handle_signal_query_packet(self):
+ # the '?' packet
+ pkt = b"S"
+ pkt += format(self.gdb_signal, "02X").encode()
+
+ self.put_gdb_packet(pkt)
+
+ def handle_register_group_read_packet(self):
+ reg_fmt = "<I"
+ pkt = b""
+
+ for reg in self.logfile.registers:
+ if reg != b"x":
+ bval = struct.pack(reg_fmt, reg)
+ pkt += binascii.hexlify(bval)
+ else:
+ # Register not in coredump -> unknown value
+ # Send in "xxxxxxxx"
+ pkt += b"x" * 8
+
+ self.put_gdb_packet(pkt)
+
+ def handle_register_single_read_packet(self, pkt):
+ # Mark registers as "<unavailable>".
+ # 'p' packets are usually used for registers
+ # other than the general ones (e.g. eax, ebx)
+ # so we can safely reply "xxxxxxxx" here.
+ self.put_gdb_packet(b"x" * 8)
+
+ def handle_register_group_write_packet(self):
+ # the 'G' packet for writing to a group of registers
+ #
+ # We don't support writing so return error
+ self.put_gdb_packet(b"E01")
+
+ def handle_register_single_write_packet(self, pkt):
+ # the 'P' packet for writing to registers
+ #
+ # We don't support writing so return error
+ self.put_gdb_packet(b"E01")
+
+ def handle_memory_read_packet(self, pkt):
+ # the 'm' packet for reading memory: m<addr>,<len>
+
+ def get_mem_region(addr):
+ for r in self.mem_regions:
+ if r["start"] <= addr <= r["end"]:
+ return r
+
+ return None
+
+ # extract address and length from packet
+ # and convert them into usable integer values
+ addr, length = pkt[1:].split(b",")
+ s_addr = int(b"0x" + addr, 16)
+ length = int(b"0x" + length, 16)
+
+ # FIXME: Need more efficient way of extracting memory content
+ remaining = length
+ addr = s_addr
+ barray = b""
+ r = get_mem_region(addr)
+ while remaining > 0:
+ if r is None:
+ barray = None
+ break
+
+ if addr > r["end"]:
+ r = get_mem_region(addr)
+ continue
+
+ offset = addr - r["start"]
+ barray += r["data"][offset:offset + 1]
+
+ addr += 1
+ remaining -= 1
+
+ if barray is not None:
+ pkt = binascii.hexlify(barray)
+ self.put_gdb_packet(pkt)
+ else:
+ self.put_gdb_packet(b"E01")
+
+ def handle_memory_write_packet(self, pkt):
+ # the 'M' packet for writing to memory
+ #
+ # We don't support writing so return error
+ self.put_gdb_packet(b"E02")
+
+ def handle_general_query_packet(self, pkt):
+ self.put_gdb_packet(b"")
+
+ def run(self, socket):
+ self.socket = socket
+
+ while True:
+ pkt = self.get_gdb_packet()
+ if pkt is None:
+ continue
+
+ pkt_type = pkt[0:1]
+ logger.debug(f"Got packet type: {pkt_type}")
+
+ if pkt_type == b"?":
+ self.handle_signal_query_packet()
+ elif pkt_type in (b"C", b"S"):
+ # Continue/stepping execution, which is not supported.
+ # So signal exception again
+ self.handle_signal_query_packet()
+ elif pkt_type == b"g":
+ self.handle_register_group_read_packet()
+ elif pkt_type == b"G":
+ self.handle_register_group_write_packet()
+ elif pkt_type == b"p":
+ self.handle_register_single_read_packet(pkt)
+ elif pkt_type == b"P":
+ self.handle_register_single_write_packet(pkt)
+ elif pkt_type == b"m":
+ self.handle_memory_read_packet(pkt)
+ elif pkt_type == b"M":
+ self.handle_memory_write_packet(pkt)
+ elif pkt_type == b"q":
+ self.handle_general_query_packet(pkt)
+ elif pkt_type == b"k":
+ # GDB quits
+ break
+ else:
+ self.put_gdb_packet(b"")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument("-e", "--elffile", required=True, help="elffile")
+
+ parser.add_argument("-l", "--logfile", required=True, help="logfile")
+
+ parser.add_argument("-p", "--port", help="gdbport", type=int, default=1234)
+
+ parser.add_argument("--debug", action="store_true", default=False)
+
+ args = parser.parse_args()
+
+ if not os.path.isfile(args.elffile):
+ logger.error(f"Cannot find file {args.elffile}, exiting...")
+ sys.exit(1)
+
+ if not os.path.isfile(args.logfile):
+ logger.error(f"Cannot find file {args.logfile}, exiting...")
+ sys.exit(1)
+
+ if args.debug:
+ logger.setLevel(logging.DEBUG)
+ else:
+ logger.setLevel(logging.INFO)
+
+ log = dump_log_file(args.logfile)
+ log.parse()
+ elf = dump_elf_file(args.elffile)
+ elf.parse()
+
+ gdbstub = gdb_stub(log, elf)
+ logging.basicConfig(format="[%(levelname)s][%(name)s] %(message)s")
+
+ gdbserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ # Reuse address so we don't have to wait for socket to be
+ # close before we can bind to the port again
+ gdbserver.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+ gdbserver.bind(("", args.port))
+ gdbserver.listen(1)
+
+ logger.info(f"Waiting GDB connection on port {args.port} ...")
+
+ conn, remote = gdbserver.accept()
+
+ if conn:
+ logger.info(f"Accepted GDB connection from {remote}")
+
+ gdbstub.run(conn)
+
+ conn.close()
+
+ gdbserver.close()