1#!/usr/bin/env python3 2# 3# Copyright (c) 2020 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7import argparse 8import logging 9import os 10import socket 11import sys 12 13from coredump_parser.log_parser import CoredumpLogFile 14from coredump_parser.elf_parser import CoredumpElfFile 15 16import gdbstubs 17 18LOGGING_FORMAT = "[%(levelname)s][%(name)s] %(message)s" 19 20# Only bind to local host 21GDBSERVER_HOST = "" 22 23 24class FakeSocket: 25 def __init__(self) -> None: 26 self.in_stream = sys.stdin.buffer 27 self.out_stream = sys.stdout.buffer 28 29 def recv(self, bufsize): 30 return self.in_stream.read(bufsize) 31 32 def send(self, data): 33 n = self.out_stream.write(data) 34 self.out_stream.flush() 35 return n 36 37 def close(self): 38 pass 39 40 41def parse_args(): 42 parser = argparse.ArgumentParser(allow_abbrev=False) 43 44 parser.add_argument("elffile", help="Zephyr ELF binary") 45 parser.add_argument("logfile", help="Coredump binary log file") 46 parser.add_argument("--debug", action="store_true", 47 help="Print extra debugging information") 48 parser.add_argument("--port", type=int, default=1234, 49 help="GDB server port") 50 parser.add_argument("--pipe", action="store_true", 51 help="Use stdio to communicate with gdb") 52 parser.add_argument("-v", "--verbose", action="store_true", 53 help="Print more information") 54 55 return parser.parse_args() 56 57 58def main(): 59 args = parse_args() 60 61 # Setup logging 62 logging.basicConfig(format=LOGGING_FORMAT) 63 64 # Setup logging for "parser" 65 logger = logging.getLogger("parser") 66 if args.debug: 67 logger.setLevel(logging.DEBUG) 68 elif args.verbose: 69 logger.setLevel(logging.INFO) 70 else: 71 logger.setLevel(logging.WARNING) 72 73 # Setup logging for follow code 74 logger = logging.getLogger("gdbserver") 75 if args.debug: 76 logger.setLevel(logging.DEBUG) 77 else: 78 # Use INFO as default since we need to let user 79 # know what is going on 80 logger.setLevel(logging.INFO) 81 82 # Setup logging for "gdbstub" 83 logger = logging.getLogger("gdbstub") 84 if args.debug: 85 logger.setLevel(logging.DEBUG) 86 elif args.verbose: 87 logger.setLevel(logging.INFO) 88 else: 89 logger.setLevel(logging.WARNING) 90 91 if not os.path.isfile(args.elffile): 92 logger.error(f"Cannot find file {args.elffile}, exiting...") 93 sys.exit(1) 94 95 if not os.path.isfile(args.logfile): 96 logger.error(f"Cannot find file {args.logfile}, exiting...") 97 sys.exit(1) 98 99 logger.info(f"Log file: {args.logfile}") 100 logger.info(f"ELF file: {args.elffile}") 101 102 # Parse the coredump binary log file 103 logf = CoredumpLogFile(args.logfile) 104 logf.open() 105 if not logf.parse(): 106 logger.error("Cannot parse log file, exiting...") 107 logf.close() 108 sys.exit(1) 109 110 # Parse ELF file for code and read-only data 111 elff = CoredumpElfFile(args.elffile) 112 elff.open() 113 if not elff.parse(): 114 logger.error("Cannot parse ELF file, exiting...") 115 elff.close() 116 logf.close() 117 sys.exit(1) 118 119 gdbstub = gdbstubs.get_gdbstub(logf, elff) 120 121 if not args.pipe: 122 # Start a GDB server 123 gdbserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 124 125 # Reuse address so we don't have to wait for socket to be 126 # close before we can bind to the port again 127 gdbserver.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 128 129 gdbserver.bind((GDBSERVER_HOST, args.port)) 130 gdbserver.listen(1) 131 132 logger.info(f"Waiting GDB connection on port {args.port}...") 133 134 conn, remote = gdbserver.accept() 135 else: 136 conn = FakeSocket() 137 remote = "pipe" 138 139 if conn: 140 logger.info(f"Accepted GDB connection from {remote}") 141 142 gdbstub.run(conn) 143 144 conn.close() 145 146 gdbserver.close() 147 148 logger.info("GDB session finished.") 149 150 elff.close() 151 logf.close() 152 153 154if __name__ == "__main__": 155 main() 156