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
24def parse_args():
25    parser = argparse.ArgumentParser(allow_abbrev=False)
26
27    parser.add_argument("elffile", help="Zephyr ELF binary")
28    parser.add_argument("logfile", help="Coredump binary log file")
29    parser.add_argument("--debug", action="store_true",
30                        help="Print extra debugging information")
31    parser.add_argument("--port", type=int, default=1234,
32                        help="GDB server port")
33    parser.add_argument("-v", "--verbose", action="store_true",
34                        help="Print more information")
35
36    return parser.parse_args()
37
38
39def main():
40    args = parse_args()
41
42    # Setup logging
43    logging.basicConfig(format=LOGGING_FORMAT)
44
45    # Setup logging for "parser"
46    logger = logging.getLogger("parser")
47    if args.debug:
48        logger.setLevel(logging.DEBUG)
49    elif args.verbose:
50        logger.setLevel(logging.INFO)
51    else:
52        logger.setLevel(logging.WARNING)
53
54    # Setup logging for follow code
55    logger = logging.getLogger("gdbserver")
56    if args.debug:
57        logger.setLevel(logging.DEBUG)
58    else:
59        # Use INFO as default since we need to let user
60        # know what is going on
61        logger.setLevel(logging.INFO)
62
63    # Setup logging for "gdbstub"
64    logger = logging.getLogger("gdbstub")
65    if args.debug:
66        logger.setLevel(logging.DEBUG)
67    elif args.verbose:
68        logger.setLevel(logging.INFO)
69    else:
70        logger.setLevel(logging.WARNING)
71
72    if not os.path.isfile(args.elffile):
73        logger.error(f"Cannot find file {args.elffile}, exiting...")
74        sys.exit(1)
75
76    if not os.path.isfile(args.logfile):
77        logger.error(f"Cannot find file {args.logfile}, exiting...")
78        sys.exit(1)
79
80    logger.info(f"Log file: {args.logfile}")
81    logger.info(f"ELF file: {args.elffile}")
82
83    # Parse the coredump binary log file
84    logf = CoredumpLogFile(args.logfile)
85    logf.open()
86    if not logf.parse():
87        logger.error("Cannot parse log file, exiting...")
88        logf.close()
89        sys.exit(1)
90
91    # Parse ELF file for code and read-only data
92    elff = CoredumpElfFile(args.elffile)
93    elff.open()
94    if not elff.parse():
95        logger.error("Cannot parse ELF file, exiting...")
96        elff.close()
97        logf.close()
98        sys.exit(1)
99
100    gdbstub = gdbstubs.get_gdbstub(logf, elff)
101
102    # Start a GDB server
103    gdbserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
104
105    # Reuse address so we don't have to wait for socket to be
106    # close before we can bind to the port again
107    gdbserver.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
108
109    gdbserver.bind((GDBSERVER_HOST, args.port))
110    gdbserver.listen(1)
111
112    logger.info(f"Waiting GDB connection on port {args.port}...")
113
114    conn, remote = gdbserver.accept()
115
116    if conn:
117        logger.info(f"Accepted GDB connection from {remote}")
118
119        gdbstub.run(conn)
120
121        conn.close()
122
123    gdbserver.close()
124
125    logger.info("GDB session finished.")
126
127    elff.close()
128    logf.close()
129
130
131if __name__ == "__main__":
132    main()
133