zephyr/scripts/coredump/coredump_gdbserver.py
Daniel Leung 49206a86ff debug/coredump: add a primitive coredump mechanism
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>
2020-08-24 20:28:24 -04:00

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()