49206a86ff
This adds a very primitive coredump mechanism under subsys/debug where during fatal error, register and memory content can be dumped to coredump backend. One such backend utilizing log module for output is included. Once the coredump log is converted to a binary file, it can be used with the ELF output file as inputs to an overly simplified implementation of a GDB server. This GDB server can be attached via the target remote command of GDB and will be serving register and memory content. This allows using GDB to examine stack and memory where the fatal error occurred. Signed-off-by: Daniel Leung <daniel.leung@intel.com>
133 lines
3.3 KiB
Python
Executable file
133 lines
3.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2020 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import socket
|
|
import sys
|
|
|
|
from parser.log_parser import CoredumpLogFile
|
|
from parser.elf_parser import CoredumpElfFile
|
|
|
|
import gdbstubs
|
|
|
|
LOGGING_FORMAT = "[%(levelname)s][%(name)s] %(message)s"
|
|
|
|
# Only bind to local host
|
|
GDBSERVER_HOST = ""
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("elffile", help="Zephyr ELF binary")
|
|
parser.add_argument("logfile", help="Coredump binary log file")
|
|
parser.add_argument("--debug", action="store_true",
|
|
help="Print extra debugging information")
|
|
parser.add_argument("--port", type=int, default=1234,
|
|
help="GDB server port")
|
|
parser.add_argument("-v", "--verbose", action="store_true",
|
|
help="Print more information")
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
# Setup logging
|
|
logging.basicConfig(format=LOGGING_FORMAT)
|
|
|
|
# Setup logging for "parser"
|
|
logger = logging.getLogger("parser")
|
|
if args.debug:
|
|
logger.setLevel(logging.DEBUG)
|
|
elif args.verbose:
|
|
logger.setLevel(logging.INFO)
|
|
else:
|
|
logger.setLevel(logging.WARNING)
|
|
|
|
# Setup logging for follow code
|
|
logger = logging.getLogger("gdbserver")
|
|
if args.debug:
|
|
logger.setLevel(logging.DEBUG)
|
|
else:
|
|
# Use INFO as default since we need to let user
|
|
# know what is going on
|
|
logger.setLevel(logging.INFO)
|
|
|
|
# Setup logging for "gdbstuc"
|
|
logger = logging.getLogger("gdbstub")
|
|
if args.debug:
|
|
logger.setLevel(logging.DEBUG)
|
|
elif args.verbose:
|
|
logger.setLevel(logging.INFO)
|
|
else:
|
|
logger.setLevel(logging.WARNING)
|
|
|
|
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)
|
|
|
|
logger.info(f"Log file: {args.logfile}")
|
|
logger.info(f"ELF file: {args.elffile}")
|
|
|
|
# Parse the coredump binary log file
|
|
logf = CoredumpLogFile(args.logfile)
|
|
logf.open()
|
|
if not logf.parse():
|
|
logger.error("Cannot parse log file, exiting...")
|
|
logf.close()
|
|
sys.exit(1)
|
|
|
|
# Parse ELF file for code and read-only data
|
|
elff = CoredumpElfFile(args.elffile)
|
|
elff.open()
|
|
if not elff.parse():
|
|
logger.error("Cannot parse ELF file, exiting...")
|
|
elff.close()
|
|
logf.close()
|
|
sys.exit(1)
|
|
|
|
gdbstub = gdbstubs.get_gdbstub(logf, elff)
|
|
|
|
# Start a GDB server
|
|
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((GDBSERVER_HOST, 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()
|
|
|
|
logger.info("GDB session finished.")
|
|
|
|
elff.close()
|
|
logf.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|