From e597f3812d6987506f9c009f35fb99b67a1add2c Mon Sep 17 00:00:00 2001 From: anjiahao Date: Mon, 28 Feb 2022 16:03:43 +0800 Subject: [PATCH] tools:use dump log creat a gdbsever to debug this is a tool,when crash help you to debug. Signed-off-by: anjiahao --- tools/minidumpserver.py | 520 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100755 tools/minidumpserver.py diff --git a/tools/minidumpserver.py b/tools/minidumpserver.py new file mode 100755 index 0000000000..9800708ee3 --- /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( + " 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 "". + # '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, + + 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()