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