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( 36 "--hex", action="store_true", help="Log Data file is in hexadecimal strings" 37 ) 38 argparser.add_argument( 39 "--rawhex", action="store_true", help="Log file only contains hexadecimal log data" 40 ) 41 argparser.add_argument("--debug", action="store_true", help="Print extra debugging information") 42 43 return argparser.parse_args() 44 45 46def read_log_file(args): 47 """ 48 Read the log from file 49 """ 50 logdata = None 51 hexdata = '' 52 53 # Open log data file for reading 54 if args.hex: 55 if args.rawhex: 56 # Simply log file with only hexadecimal data 57 logdata = dictionary_parser.utils.convert_hex_file_to_bin(args.logfile) 58 else: 59 hexdata = '' 60 61 with open(args.logfile, encoding="iso-8859-1") as hexfile: 62 for line in hexfile.readlines(): 63 hexdata += line.strip() 64 65 if LOG_HEX_SEP not in hexdata: 66 logger.error("ERROR: Cannot find start of log data, exiting...") 67 sys.exit(1) 68 69 idx = hexdata.index(LOG_HEX_SEP) + len(LOG_HEX_SEP) 70 hexdata = hexdata[idx:] 71 72 if len(hexdata) % 2 != 0: 73 # Make sure there are even number of characters 74 idx = int(len(hexdata) / 2) * 2 75 hexdata = hexdata[:idx] 76 77 idx = 0 78 while idx < len(hexdata): 79 # When running QEMU via west or ninja, there may be additional 80 # strings printed by QEMU, west or ninja (for example, QEMU 81 # is terminated, or user interrupted, etc). So we need to 82 # figure out where the end of log data stream by 83 # trying to convert from hex to bin. 84 idx += 2 85 86 try: 87 binascii.unhexlify(hexdata[:idx]) 88 except binascii.Error: 89 idx -= 2 90 break 91 92 logdata = binascii.unhexlify(hexdata[:idx]) 93 else: 94 with open(args.logfile, "rb") as logfile: 95 if not logfile: 96 logger.error(f"ERROR: Cannot open binary log data file: {args.logfile}, exiting...") 97 sys.exit(1) 98 logdata = logfile.read() 99 100 # RTT logs add header information to the logdata, the actual log comes 101 # after newline following "Process:" line in logdata 102 if b"Process:" in logdata: 103 process_idx = logdata.find(b"Process:") 104 newline_idx = logdata.find(b"\n", process_idx) 105 if newline_idx != -1: 106 # Keep only the data after this newline 107 logdata = logdata[newline_idx + 1 :] 108 logger.debug("Found 'Process:' in the RTT header, trimmed data") 109 110 return logdata 111 112 113def main(): 114 """Main function of log parser""" 115 args = parse_args() 116 117 # Setup logging for parser 118 logging.basicConfig(format=LOGGER_FORMAT) 119 if args.debug: 120 logger.setLevel(logging.DEBUG) 121 else: 122 logger.setLevel(logging.INFO) 123 124 log_parser = parserlib.get_log_parser(args.dbfile, logger) 125 126 logdata = read_log_file(args) 127 if logdata is None: 128 logger.error("ERROR: cannot read log from file: %s, exiting...", args.logfile) 129 sys.exit(1) 130 131 parsed_data_offset = parserlib.parser(logdata, log_parser, logger) 132 if parsed_data_offset != len(logdata): 133 logger.error( 134 'ERROR: Not all data was parsed, %d bytes left unparsed', 135 len(logdata) - parsed_data_offset, 136 ) 137 sys.exit(1) 138 139 140if __name__ == "__main__": 141 main() 142