1#!/usr/bin/env python3
2#
3# Copyright (c) 2021 Intel Corporation
4# Copyright (c) 2024 Nordic Semiconductor ASA
5#
6# SPDX-License-Identifier: Apache-2.0
7
8"""
9Log Parser for Dictionary-based Logging
10
11This uses the JSON database file to decode the input binary
12log data and print the log messages.
13"""
14
15import argparse
16import binascii
17import logging
18import sys
19
20import dictionary_parser
21import parserlib
22
23LOGGER_FORMAT = "%(message)s"
24logger = logging.getLogger("parser")
25
26LOG_HEX_SEP = "##ZLOGV1##"
27
28
29def parse_args():
30    """Parse command line arguments"""
31    argparser = argparse.ArgumentParser(allow_abbrev=False)
32
33    argparser.add_argument("dbfile", help="Dictionary Logging Database file")
34    argparser.add_argument("logfile", help="Log Data file")
35    argparser.add_argument("--hex", action="store_true",
36                           help="Log Data file is in hexadecimal strings")
37    argparser.add_argument("--rawhex", action="store_true",
38                           help="Log file only contains hexadecimal log data")
39    argparser.add_argument("--debug", action="store_true",
40                           help="Print extra debugging information")
41
42    return argparser.parse_args()
43
44
45def read_log_file(args):
46    """
47    Read the log from file
48    """
49    logdata = None
50    hexdata = ''
51
52    # Open log data file for reading
53    if args.hex:
54        if args.rawhex:
55            # Simply log file with only hexadecimal data
56            logdata = dictionary_parser.utils.convert_hex_file_to_bin(args.logfile)
57        else:
58            hexdata = ''
59
60            with open(args.logfile, "r", encoding="iso-8859-1") as hexfile:
61                for line in hexfile.readlines():
62                    hexdata += line.strip()
63
64            if LOG_HEX_SEP not in hexdata:
65                logger.error("ERROR: Cannot find start of log data, exiting...")
66                sys.exit(1)
67
68            idx = hexdata.index(LOG_HEX_SEP) + len(LOG_HEX_SEP)
69            hexdata = hexdata[idx:]
70
71            if len(hexdata) % 2 != 0:
72                # Make sure there are even number of characters
73                idx = int(len(hexdata) / 2) * 2
74                hexdata = hexdata[:idx]
75
76            idx = 0
77            while idx < len(hexdata):
78                # When running QEMU via west or ninja, there may be additional
79                # strings printed by QEMU, west or ninja (for example, QEMU
80                # is terminated, or user interrupted, etc). So we need to
81                # figure out where the end of log data stream by
82                # trying to convert from hex to bin.
83                idx += 2
84
85                try:
86                    binascii.unhexlify(hexdata[:idx])
87                except binascii.Error:
88                    idx -= 2
89                    break
90
91            logdata = binascii.unhexlify(hexdata[:idx])
92    else:
93        logfile = open(args.logfile, "rb")
94        if not logfile:
95            logger.error("ERROR: Cannot open binary log data file: %s, exiting...", args.logfile)
96            sys.exit(1)
97
98        logdata = logfile.read()
99
100        logfile.close()
101
102    return logdata
103
104def main():
105    """Main function of log parser"""
106    args = parse_args()
107
108    # Setup logging for parser
109    logging.basicConfig(format=LOGGER_FORMAT)
110    if args.debug:
111        logger.setLevel(logging.DEBUG)
112    else:
113        logger.setLevel(logging.INFO)
114
115    logdata = read_log_file(args)
116    if logdata is None:
117        logger.error("ERROR: cannot read log from file: %s, exiting...", args.logfile)
118        sys.exit(1)
119
120    parserlib.parser(logdata, args.dbfile, logger)
121
122if __name__ == "__main__":
123    main()
124