156 lines
4.0 KiB
Python
Executable File
156 lines
4.0 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 coredump_parser.log_parser import CoredumpLogFile
|
|
from coredump_parser.elf_parser import CoredumpElfFile
|
|
|
|
import gdbstubs
|
|
|
|
LOGGING_FORMAT = "[%(levelname)s][%(name)s] %(message)s"
|
|
|
|
# Only bind to local host
|
|
GDBSERVER_HOST = ""
|
|
|
|
|
|
class FakeSocket:
|
|
def __init__(self) -> None:
|
|
self.in_stream = sys.stdin.buffer
|
|
self.out_stream = sys.stdout.buffer
|
|
|
|
def recv(self, bufsize):
|
|
return self.in_stream.read(bufsize)
|
|
|
|
def send(self, data):
|
|
n = self.out_stream.write(data)
|
|
self.out_stream.flush()
|
|
return n
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(allow_abbrev=False)
|
|
|
|
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("--pipe", action="store_true",
|
|
help="Use stdio to communicate with gdb")
|
|
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 "gdbstub"
|
|
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)
|
|
|
|
if not args.pipe:
|
|
# 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()
|
|
else:
|
|
conn = FakeSocket()
|
|
remote = "pipe"
|
|
|
|
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()
|