From 5b48c14558fff588978a15ee2f6863461f0007ab Mon Sep 17 00:00:00 2001 From: varghese-bibin Date: Mon, 13 Apr 2026 14:30:58 +0530 Subject: [PATCH 1/7] Refactor TRAP logParser : Modularize log preprocessing a. Separated the preprocessing functions from logParser.py and kept them in a separate preProcessor.py file b. moved logParser.py and logPreprocesser.py to logAnalyser folder, to keep the similar types of file together c. preProcessing includes format_log_file() and find_number_of_binaries() d. instead of calling these function trap.py call preprocessLogFile --- tools/trap/cli/{ => logAnalyser}/logParser.py | 112 ------------- tools/trap/cli/logAnalyser/logPreprocessor.py | 153 ++++++++++++++++++ tools/trap/cli/trap.py | 10 +- 3 files changed, 157 insertions(+), 118 deletions(-) rename tools/trap/cli/{ => logAnalyser}/logParser.py (89%) create mode 100755 tools/trap/cli/logAnalyser/logPreprocessor.py diff --git a/tools/trap/cli/logParser.py b/tools/trap/cli/logAnalyser/logParser.py similarity index 89% rename from tools/trap/cli/logParser.py rename to tools/trap/cli/logAnalyser/logParser.py index c6fee7c18b..6f77f0d8aa 100755 --- a/tools/trap/cli/logParser.py +++ b/tools/trap/cli/logAnalyser/logParser.py @@ -135,72 +135,6 @@ def is_kernel_text_address(self, address): else: return False - # Function to get the number of application binaries, names, text address and sizes - def find_number_of_binaries(self): - - current_line = "" - # Parse the contents based on tokens in log file. - with open(self.log_file) as searchfile: - for line in searchfile: - if partition_string in line: - line = next(searchfile) - current_line = line - line = next(searchfile) - continue - # Get the number of applications loaded - if BIN_ADDR_FXN == current_line: - self.g_app_idx = self.g_app_idx + 1 - - app_idx = 0 - self.g_stext_app = array('i', range(0, self.g_app_idx)) - self.g_etext_app = array('i', range(0, self.g_app_idx)) - with open(self.log_file) as searchfile: - for line in searchfile: - if partition_string in line: - line = next(searchfile) - current_line = line - line = next(searchfile) - continue - # Read the app text address and size - if BIN_ADDR_FXN == current_line: - word = line.split(':') - t = word[2].split(',') # word[2] is the App Start Text address - w = word[1].split(' ') - # w[1] denotes string '[]' - start_idx = int(w[1].find('[')) + 1 - end_idx = int(w[1].find(']')) - self.app_name.append(w[1][start_idx:end_idx]) - self.g_stext_app[app_idx] = int(t[0], 16) - self.g_etext_app[app_idx] = self.g_stext_app[app_idx] + int(word[3], 10) # word[3] is text_size - app_idx = app_idx + 1 - - if app_idx == 0: - app_count = 0 - app_names = [] - - debug_files = [ - f for f in os.listdir(self.bin_path) - if f.endswith("_dbg") and os.path.isfile(os.path.join(self.bin_path, f)) - ] - - for f in debug_files: - app_names.append(f.split("_")[0]) - self.app_name.append(app_names[app_count]) - app_count = app_count + 1 - - if app_count > 0: - self.read_all_elf = True - self.g_app_idx = app_count - self.g_stext_app = array('i', range(0, self.g_app_idx)) - self.g_etext_app = array('i', range(0, self.g_app_idx)) - - for app_idx in range(app_count): - #setting start and end address with minimum and maximum possible values for now when address is not available - self.g_stext_app[app_idx] = int("0x00000000", 16) - self.g_etext_app[app_idx] = int("0x77777777", 16) - else: - print("\nNo debug files found for common and app binaries\n") - # API to find crash binary, crash point and crash type from assert log def find_crash_point(self): pc_value = 0 @@ -833,49 +767,3 @@ def parse_log(self): # print current running task List self.print_task_list() - - # Function to format logs and delete the timestamp (supported formats-|xxxxxxxxx| and [xxxxxxxxx]) if it consists of timestamp at the start of each log line - def format_log_file(self): - - current_line = "" # Initialize current_line - - # Delete unwanted logs (if any) and timestamp at the start of each line - with open(self.log_file, "r") as f: - data = f.readlines() - with open(self.log_file, "w") as f: - data = iter(data) - for line in data: - delete_idx = 0 - # Timestamp present if line starts with '|' - if line[0] == '|' or line[0] == '[': - for idx in range(1, len(line)): - if '|' == line[idx] or ']' == line[idx]: - delete_idx = idx + 1 - break - if line[delete_idx] == ' ': # Check for trailing white spaces - delete_idx = delete_idx + 1 - line = line[delete_idx:] - f.write(line) - - # Check for invalid format after above formatting - with open(self.log_file, "r") as f: - data = f.readlines() - data = iter(data) - for line in data: - if partition_string in line: - line = next(data) - current_line = line - line = next(data) - continue - if 'Assertion failed at file:' in line and current_line == assertion_details: - word = line.split() - if word[1] != 'Assertion': - for idx in range(0, len(line)): - if 'A' == line[idx]: - delete_idx = idx - break - correctFormatString = line[delete_idx:] - print ("\n\t- Below log format is not supported in TRAP") - print ('\t\t-{0}\t- Instead, supported log format in TRAP is as follows:'.format(line)) - print ("\t\t-{0} {1}\n\tKindly modify the log file as per accepted format.\n".format(word[word.index('Assertion')-1], correctFormatString)) - sys.exit(1) diff --git a/tools/trap/cli/logAnalyser/logPreprocessor.py b/tools/trap/cli/logAnalyser/logPreprocessor.py new file mode 100755 index 0000000000..066601e571 --- /dev/null +++ b/tools/trap/cli/logAnalyser/logPreprocessor.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +# File : log_preprocessor.py +# Description: Contains preprocessing functions for log files including +# format validation and binary discovery + +from __future__ import print_function +import os +import sys +from array import * + +# Global variables +assertion_details = "Assertion details\n" +BIN_ADDR_FXN = "Loading location information\n" + + +partition_string = "===========================================================" + + +# Function to format logs and delete the timestamp (supported formats-|xxxxxxxxx| and [xxxxxxxxx]) if it consists of timestamp at the start of each log line +def format_log_file(log_file): + + current_line = "" # Initialize current_line + + # Delete unwanted logs (if any) and timestamp at the start of each line + with open(log_file, "r") as f: + data = f.readlines() + with open(log_file, "w") as f: + data = iter(data) + for line in data: + delete_idx = 0 + # Timestamp present if line starts with '|' + if line[0] == '|' or line[0] == '[': + for idx in range(1, len(line)): + if '|' == line[idx] or ']' == line[idx]: + delete_idx = idx + 1 + break + if line[delete_idx] == ' ': # Check for trailing white spaces + delete_idx = delete_idx + 1 + line = line[delete_idx:] + f.write(line) + + # Check for invalid format after above formatting + with open(log_file, "r") as f: + data = f.readlines() + data = iter(data) + for line in data: + if partition_string in line: + line = next(data) + current_line = line + line = next(data) + continue + if 'Assertion failed at file:' in line and current_line == assertion_details: + word = line.split() + if word[1] != 'Assertion': + for idx in range(0, len(line)): + if 'A' == line[idx]: + delete_idx = idx + break + correctFormatString = line[delete_idx:] + print ("\n\t- Below log format is not supported in TRAP") + print ('\t\t-{0}\t- Instead, supported log format in TRAP is as follows:'.format(line)) + print ("\t\t-{0} {1}\n\tKindly modify the log file as per accepted format.\n".format(word[word.index('Assertion')-1], correctFormatString)) + sys.exit(1) + +# Function to get the number of application binaries, names, text address and sizes +def find_number_of_binaries(lparser): + + current_line = "" + # Parse the contents based on tokens in log file. + with open(lparser.log_file) as searchfile: + for line in searchfile: + if partition_string in line: + line = next(searchfile) + current_line = line + line = next(searchfile) + continue + # Get the number of applications loaded + if BIN_ADDR_FXN == current_line: + lparser.g_app_idx = lparser.g_app_idx + 1 + + app_idx = 0 + lparser.g_stext_app = array('i', range(0, lparser.g_app_idx)) + lparser.g_etext_app = array('i', range(0, lparser.g_app_idx)) + with open(lparser.log_file) as searchfile: + for line in searchfile: + if partition_string in line: + line = next(searchfile) + current_line = line + line = next(searchfile) + continue + # Read the app text address and size + if BIN_ADDR_FXN == current_line: + word = line.split(':') + t = word[2].split(',') # word[2] is the App Start Text address + w = word[1].split(' ') + # w[1] denotes string '[]' + start_idx = int(w[1].find('[')) + 1 + end_idx = int(w[1].find(']')) + lparser.app_name.append(w[1][start_idx:end_idx]) + lparser.g_stext_app[app_idx] = int(t[0], 16) + lparser.g_etext_app[app_idx] = lparser.g_stext_app[app_idx] + int(word[3], 10) # word[3] is text_size + app_idx = app_idx + 1 + + if app_idx == 0: + app_count = 0 + app_names = [] + + debug_files = [ + f for f in os.listdir(lparser.bin_path) + if f.endswith("_dbg") and os.path.isfile(os.path.join(lparser.bin_path, f)) + ] + + for f in debug_files: + app_names.append(f.split("_")[0]) + lparser.app_name.append(app_names[app_count]) + app_count = app_count + 1 + + if app_count > 0: + lparser.read_all_elf = True + lparser.g_app_idx = app_count + lparser.g_stext_app = array('i', range(0, lparser.g_app_idx)) + lparser.g_etext_app = array('i', range(0, lparser.g_app_idx)) + + for app_idx in range(app_count): + #setting start and end address with minimum and maximum possible values for now when address is not available + lparser.g_stext_app[app_idx] = int("0x00000000", 16) + lparser.g_etext_app[app_idx] = int("0x77777777", 16) + else: + print("\nNo debug files found for common and app binaries\n") + +def preprocessLogFile(lparser): + # Format log file if timestamp is present at the start of each line + format_log_file(lparser.log_file) + + # Get the number of application binaries, names, text address and sizes + find_number_of_binaries(lparser) \ No newline at end of file diff --git a/tools/trap/cli/trap.py b/tools/trap/cli/trap.py index 782e8d4e26..b1b3cb2221 100755 --- a/tools/trap/cli/trap.py +++ b/tools/trap/cli/trap.py @@ -27,7 +27,8 @@ import sys, time from getopt import GetoptError, getopt as GetOpt from dumpParser import dumpParser -from logParser import logParser +from logAnalyser.logParser import logParser +from logAnalyser.logPreprocessor import preprocessLogFile # Global variables # Variable to check if board has kernel text in RAM region @@ -204,11 +205,8 @@ def main(): # Instance for assert log Parsing lparser = logParser(elf = elf, log_file = log_file, bin_path = BIN_PATH, config_path = CONFIG_PATH, xip_enabled = xip_enabled, have_ram_kernel_text =have_ram_kernel_text) - # Format log file if timestamp is present at the start of each line - lparser.format_log_file() - - # Get the number of application binaries, names, text address and sizes - lparser.find_number_of_binaries() + # preprocess the log files + preprocessLogFile(lparser) # Print TRAP output Header information i.e. log_file, dump file, elf files, etc. print_trap_output_header(lparser) From 2ae621a7c5796367f4ad5bbb2ffe843ffb03cc2e Mon Sep 17 00:00:00 2001 From: varghese-bibin Date: Mon, 13 Apr 2026 14:35:06 +0530 Subject: [PATCH 2/7] Refactor TRAP logParser : Move utility functions to dedicated logUtils module a. created cli/logAnalyser/logUtils.py b. moved all the independent helper functions from logParser.py to it c. format output, print crash type, is text addr, is app adrr, convert state no --- tools/trap/cli/logAnalyser/logParser.py | 154 +++++------------------- tools/trap/cli/logAnalyser/logUtils.py | 122 +++++++++++++++++++ 2 files changed, 154 insertions(+), 122 deletions(-) create mode 100755 tools/trap/cli/logAnalyser/logUtils.py diff --git a/tools/trap/cli/logAnalyser/logParser.py b/tools/trap/cli/logAnalyser/logParser.py index 6f77f0d8aa..2377c3e0dc 100755 --- a/tools/trap/cli/logAnalyser/logParser.py +++ b/tools/trap/cli/logAnalyser/logParser.py @@ -27,6 +27,7 @@ import sys, time from array import * import utils as utils +import logAnalyser.logUtils as logUtils import heapNode # Global variables @@ -83,57 +84,7 @@ def __init__(self, elf = None, log_file = None, bin_path = None, config_path= No self.xip_enabled = xip_enabled self.have_ram_kernel_text = have_ram_kernel_text - self.convert_stateno_statemsg() - - def format_output(self,res , string): - r = res.split('\n') - print('\t- symbol addr {0} : {1}'.format(string, r[0])) - print('\t- function name {0} : {1}'.format(string, r[1])) - print('\t- file {0} : {1}'.format(string, r[2])) - - def print_crash_type(self, string): - if 'up_memfault' in string: - print('\n2. Crash type : memory fault') - elif 'up_busfault' in string: - print('\n2. Crash type : bus fault') - elif 'up_usagefault' in string: - print('\n2. Crash type : usage fault') - elif 'up_hardfault' in string: - print('\n2. Crash type : hard fault') - elif 'dataabort' in string: - print('\n2. Crash type : data abort') - elif 'prefetchabort' in string: - print('\n2. Crash type : prefetch abort') - elif 'undefinedinsn' in string: - print('\n2. Crash type : undefined instruction abort') - elif 'Assertion failed' in string: - self.crash_type_assert = True - print('\n2. Crash type : code assertion by code ASSERT or PANIC') - else: - print('\n2. Crash type : etc') - if (self.crash_type_assert == True): - print('\n3. Crash point\n\t-', string.split(': ',1)[1]) - else: - print(' Crash log\n\t-', string) - - # Function to check if address lies in the application's text address range - def is_app_text_address(self, address): - idx = 0 - # Check the application text address range - for idx in range(self.g_app_idx): - if (address >= hex(self.g_stext_app[idx]) and address < hex(self.g_etext_app[idx])): - return (idx + 1) - if (idx == self.g_app_idx): - return False - - # Function to check if address lies in the kernel text address range - def is_kernel_text_address(self, address): - - # Check the kernel text address range - if (address >= hex(self.g_stext_ram) and address < hex(self.g_etext_ram)) or (address >= hex(self.g_stext_flash) and address < hex(self.g_etext_flash)): - return True - else: - return False + logUtils.convert_stateno_statemsg(self) # API to find crash binary, crash point and crash type from assert log def find_crash_point(self): @@ -184,7 +135,7 @@ def find_crash_point(self): word = line.split(':') #word[2] contains the g_assertpc value self.g_assertpc = int(word[2].strip(),16) - + print('-----------------------------------------------------------------------------------------') address1 = hex(lr_value) address2 = hex(pc_value) @@ -202,11 +153,11 @@ def find_crash_point(self): if '??' not in result and '$d' not in result: is_app_crash = 1 print('\n1. Crash Binary : {0}'.format(self.app_name[app_idx])) - self.print_crash_type(assertline) + logUtils.print_crash_type(self, assertline) if (self.crash_type_assert == False): print('3. Crash point (PC or LR)') print('\n\t[ Caller - return address (LR) - of the function which has caused the crash ]') - self.format_output(result, "") + logUtils.format_output(result, "") if (address2 >= hex(self.g_stext_app[app_idx]) and address2 < hex(self.g_etext_app[app_idx])): if self.xip_enabled: addr = pc_value @@ -217,27 +168,27 @@ def find_crash_point(self): if '??' not in result and '$d' not in result: if (not is_app_crash): print('\n1. Crash Binary : {0}'.format(self.app_name[app_idx])) - self.print_crash_type(assertline) + logUtils.print_crash_type(self, assertline) if (self.crash_type_assert == False): print('3. Crash point (PC or LR)') is_app_crash = 1 if (self.crash_type_assert == False): print('\n\t[ Current location (PC) of assert ]') - self.format_output(result, "") + logUtils.format_output(result, "") if ((addr - 4) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 4)) result1 = f.read() if '??' not in result1 and '$d' not in result1: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result1, "of (pc - 4)") + logUtils.format_output(result1, "of (pc - 4)") if ((addr - 8) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 8)) result2 = f.read() if '??' not in result2 and '$d' not in result2: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result2, "of (pc - 8)") + logUtils.format_output(result2, "of (pc - 8)") # Scenario when up_registerdump() is not present in dump logs, use g_assertpc value to give crash point else: @@ -253,25 +204,25 @@ def find_crash_point(self): if '??' not in result and '$d' not in result: is_app_crash = 1 print('\n1. Crash Binary : {0}'.format(self.app_name[app_idx])) - self.print_crash_type(assertline) + logUtils.print_crash_type(self, assertline) if (self.crash_type_assert == False): print('3. Crash point (PC or LR)') print('\n\t[ Current location (PC) of assert ]') - self.format_output(result, "") + logUtils.format_output(result, "") if ((addr - 4) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 4)) result1 = f.read() if '??' not in result1 and '$d' not in result1: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result1, "of (pc - 4)") + logUtils.format_output(result1, "of (pc - 4)") if ((addr - 8) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 8)) result2 = f.read() if '??' not in result2 and '$d' not in result2: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result2, "of (pc - 8)") + logUtils.format_output(result2, "of (pc - 8)") # Check for lr & pc values in kernel text address range if (not is_app_crash) and (pc_value != 00000000): @@ -283,38 +234,38 @@ def find_crash_point(self): if '??' not in result and '$d' not in result: is_kernel_crash = 1 print('1. Crash Binary : kernel') - self.print_crash_type(assertline) + logUtils.print_crash_type(self, assertline) if (self.crash_type_assert == False): print('3. Crash point (PC or LR)') print('\n\t[ Caller - return address (LR) - of the function which has caused the crash ]') - self.format_output(result, "") + logUtils.format_output(result, "") if (address2 >= hex(self.g_stext_flash) and address2 < hex(self.g_etext_flash)) or (address2 >= hex(self.g_stext_ram) and address2 < hex(self.g_etext_ram)): f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(pc_value)) result = f.read() if '??' not in result and '$d' not in result: if (not is_kernel_crash): print('1. Crash Binary : kernel') - self.print_crash_type(assertline) + logUtils.print_crash_type(self, assertline) if (self.crash_type_assert == False): print('3. Crash point (PC or LR)') is_kernel_crash = 1 if (self.crash_type_assert == False): print('\n\t[ Current location (PC) of assert ]') - self.format_output(result, "") + logUtils.format_output(result, "") if ((pc_value - 4) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(pc_value - 4)) result1 = f.read() if '??' not in result1 and '$d' not in result1: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result1, "of (pc - 4)") + logUtils.format_output(result1, "of (pc - 4)") if ((pc_value - 8) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(pc_value - 8)) result2 = f.read() if '??' not in result2 and '$d' not in result2: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result2, "of (pc - 8)") + logUtils.format_output(result2, "of (pc - 8)") # Scenario when up_registerdump() is not present in dump logs, use g_assertpc value to give crash point if (pc_value == 00000000 and self.g_assertpc): @@ -326,29 +277,29 @@ def find_crash_point(self): if '??' not in result and '$d' not in result: is_kernel_crash = 1 print('1. Crash Binary : kernel') - self.print_crash_type(assertline) + logUtils.print_crash_type(self, assertline) if (self.crash_type_assert == False): print('3. Crash point (PC or LR)') print('\n\t[ Current location (PC) of assert ]') - self.format_output(result, "") + logUtils.format_output(result, "") if ((self.g_assertpc - 4) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(self.g_assertpc - 4)) result1 = f.read() if '??' not in result1 and '$d' not in result1: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result1, "of (pc - 4)") + logUtils.format_output(result1, "of (pc - 4)") if ((self.g_assertpc - 8) > 0x0): f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(self.g_assertpc - 8)) result2 = f.read() if '??' not in result2 and '$d' not in result2: if (self.crash_type_assert == False): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - self.format_output(result2, "of (pc - 8)") + logUtils.format_output(result2, "of (pc - 8)") if (not is_app_crash) and (not is_kernel_crash): print('1. Crash Binary : NA') - self.print_crash_type(assertline) + logUtils.print_crash_type(self, assertline) if (self.crash_type_assert == False): print('3. Crash point (PC or LR)') # Parse the contents based on tokens in log file. @@ -508,7 +459,7 @@ def parse_call_stack(self): stack_val = int(sub_word,16) # Check if the stack address lies in kernel text address range - if (self.is_kernel_text_address(hex(stack_val))): + if (logUtils.is_kernel_text_address(self, hex(stack_val))): #If yes, print it's corresponding symbol utils.print_symbol(stack_addr, stack_val, 0, self.bin_path, self.app_name) else: @@ -519,7 +470,7 @@ def parse_call_stack(self): utils.print_symbol(stack_addr, stack_val, i + 1, self.bin_path, self.app_name) else: # Check if the stack address lies in application text address range - is_app_symbol = self.is_app_text_address(hex(stack_val)) # app index in case of application symbol + is_app_symbol = logUtils.is_app_text_address(self, hex(stack_val)) # app index in case of application symbol if (is_app_symbol): #If yes, print it's corresponding symbol if not self.xip_enabled: @@ -550,7 +501,7 @@ def print_wrong_sp(self): stack_val = int(sub_word,16) # Check if the stack address lies in kernel text address range - if (self.is_kernel_text_address(hex(stack_val))): + if (logUtils.is_kernel_text_address(self, hex(stack_val))): if (format_print): print('\t- SP is out of the stack range. Debug symbols corresponding to the wrong stack pointer addresses are given below:') print('Stack_address\t Symbol_address\t Symbol location {: <45} File_name'.format("Symbol_name")) @@ -558,7 +509,7 @@ def print_wrong_sp(self): #If yes, print it's corresponding symbol utils.print_symbol(stack_addr, stack_val, 0, self.bin_path, self.app_name) # Check if the stack address lies in application text address range - is_app_symbol = self.is_app_text_address(hex(stack_val)) # app index in case of application symbol + is_app_symbol = logUtils.is_app_text_address(self, hex(stack_val)) # app index in case of application symbol if (is_app_symbol): if (format_print): print('\t- SP is out of the stack range. Debug symbols corresponding to the wrong stack pointer addresses are given below:') @@ -587,7 +538,7 @@ def parse_tcb_info(self): if "State" in line: line = line.replace(" " + data, self.task_state[data.split()[0]]) elif "Syscall" in line: - app_idx = self.is_app_text_address(data) + app_idx = logUtils.is_app_text_address(self, data) if app_idx: if self.xip_enabled: addr = data @@ -597,7 +548,7 @@ def parse_tcb_info(self): result = f.read() if '??' not in result and '$d' not in result: line = line.replace(data, result.replace("\n"," ").split()[0]) - elif self.is_kernel_text_address(data): + elif logUtils.is_kernel_text_address(self, data): f = os.popen('arm-none-eabi-addr2line -f -e ' + self.elf + ' ' + hex(int(data, 16))) result = f.read() if '??' not in result and '$d' not in result: @@ -668,49 +619,6 @@ def parse_heap_info(self): if heapNode.parseCorruptHeapInfo(line, self.g_app_idx, self.g_stext_app, self.g_etext_app,self.app_name, self.elf, self.bin_path, self.xip_enabled, searchfile) == False: print("No app heap corruption detected.\n") - # API to parse TCB state to corresponding TCB state msg - def convert_stateno_statemsg(self): - # Convert task state number to corresponding task state message - config_smp = False - config_disable_signals = False - config_disable_mqueue = False - config_paging = False - with open(self.config_path) as configfile: - for line in configfile: - if "CONFIG_SMP=y" in line: - config_smp = True - elif "CONFIG_DISABLE_SIGNALS=y" in line: - config_disable_signals = True - elif "CONFIG_DISABLE_MQUEUE=y" in line: - config_disable_mqueue = True - elif "CONFIG_PAGING=y" in line: - config_paging = True - state_no = 0 - self.task_state[str(state_no)] = " Invalid" - state_no+=1 - self.task_state[str(state_no)] = " Pending preemption unlock" - state_no+=1 - self.task_state[str(state_no)] = " Wait to scheduling (Ready)" - state_no+=1 - self.task_state[str(state_no)] = " Assigned to CPU (Ready)" - state_no+=1 - self.task_state[str(state_no)] = " Running" - state_no+=1 - self.task_state[str(state_no)] = " Inactive" - state_no+=1 - self.task_state[str(state_no)] = " Wait Semaphore" - state_no+=1 - self.task_state[str(state_no)] = " Wait FIN" - state_no+=1 - self.task_state[str(state_no)] = " Wait Signal" - state_no+=1 - self.task_state[str(state_no)] = " Wait MQ Receive (MQ Empty)" - state_no+=1 - self.task_state[str(state_no)] = " Wait MQ Send (MQ Full)" - state_no+=1 - self.task_state[str(state_no)] = " Wait Page Fill" - state_no+=1 - # API to print all the runnning TCB in the system def print_task_list(self): #Parse content of file for displaying tasks @@ -741,6 +649,7 @@ def parse_log(self): os.system("nm --defined-only -n -C " + self.bin_path + "tinyara.axf > " + self.bin_path + "Kernel.map") utils.setup_symbol_table(self.bin_path + "Kernel.map", 0) + for idx in range(self.g_app_idx): os.system("nm --defined-only -n -C " + self.bin_path + self.app_name[idx] + "_dbg > " + self.bin_path + self.app_name[idx] + ".map") utils.setup_symbol_table(self.bin_path + self.app_name[idx] + ".map", idx + 1) @@ -767,3 +676,4 @@ def parse_log(self): # print current running task List self.print_task_list() + diff --git a/tools/trap/cli/logAnalyser/logUtils.py b/tools/trap/cli/logAnalyser/logUtils.py new file mode 100755 index 0000000000..9f4f2fb5b0 --- /dev/null +++ b/tools/trap/cli/logAnalyser/logUtils.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +# File : utils.py +# Description: Contains utility functions for log parsing including +# formatting, crash type detection, address validation, and state mapping + +from __future__ import print_function + + + +def format_output(res, string): + r = res.split('\n') + print('\t- symbol addr {0} : {1}'.format(string, r[0])) + print('\t- function name {0} : {1}'.format(string, r[1])) + print('\t- file {0} : {1}'.format(string, r[2])) + +def print_crash_type(parser, string): + if 'up_memfault' in string: + print('\n2. Crash type : memory fault') + elif 'up_busfault' in string: + print('\n2. Crash type : bus fault') + elif 'up_usagefault' in string: + print('\n2. Crash type : usage fault') + elif 'up_hardfault' in string: + print('\n2. Crash type : hard fault') + elif 'dataabort' in string: + print('\n2. Crash type : data abort') + elif 'prefetchabort' in string: + print('\n2. Crash type : prefetch abort') + elif 'undefinedinsn' in string: + print('\n2. Crash type : undefined instruction abort') + elif 'Assertion failed' in string: + parser.crash_type_assert = True + print('\n2. Crash type : code assertion by code ASSERT or PANIC') + else: + print('\n2. Crash type : etc') + if (parser.crash_type_assert == True): + print('\n3. Crash point\n\t-', string.split(': ',1)[1]) + else: + print(' Crash log\n\t-', string) + +def is_app_text_address(parser, address): + idx = 0 + # Check the application text address range + for idx in range(parser.g_app_idx): + if (address >= hex(parser.g_stext_app[idx]) and address < hex(parser.g_etext_app[idx])): + return (idx + 1) + if (idx == parser.g_app_idx): + return False + + +# Function to check if address lies in the kernel text address range +def is_kernel_text_address(parser, address): + + # Check the kernel text address range + if (address >= hex(parser.g_stext_ram) and address < hex(parser.g_etext_ram)) or (address >= hex(parser.g_stext_flash) and address < hex(parser.g_etext_flash)): + return True + else: + return False + + +def convert_stateno_statemsg(parser): + # Parse configuration to determine enabled features + config_smp = False + config_disable_signals = False + config_disable_mqueue = False + config_paging = False + + # Read configuration file + with open(parser.config_path) as configfile: + for line in configfile: + if "CONFIG_SMP=y" in line: + config_smp = True + elif "CONFIG_DISABLE_SIGNALS=y" in line: + config_disable_signals = True + elif "CONFIG_DISABLE_MQUEUE=y" in line: + config_disable_mqueue = True + elif "CONFIG_PAGING=y" in line: + config_paging = True + + # Build task state mapping + state_no = 0 + parser.task_state[str(state_no)] = " Invalid" + state_no+=1 + parser.task_state[str(state_no)] = " Pending preemption unlock" + state_no+=1 + parser.task_state[str(state_no)] = " Wait to scheduling (Ready)" + state_no+=1 + parser.task_state[str(state_no)] = " Assigned to CPU (Ready)" + state_no+=1 + parser.task_state[str(state_no)] = " Running" + state_no+=1 + parser.task_state[str(state_no)] = " Inactive" + state_no+=1 + parser.task_state[str(state_no)] = " Wait Semaphore" + state_no+=1 + parser.task_state[str(state_no)] = " Wait FIN" + state_no+=1 + parser.task_state[str(state_no)] = " Wait Signal" + state_no+=1 + parser.task_state[str(state_no)] = " Wait MQ Receive (MQ Empty)" + state_no+=1 + parser.task_state[str(state_no)] = " Wait MQ Send (MQ Full)" + state_no+=1 + parser.task_state[str(state_no)] = " Wait Page Fill" + state_no+=1 \ No newline at end of file From 8cb9376048c3ef895eb99d4f7d28d3b7511f9aec Mon Sep 17 00:00:00 2001 From: varghese-bibin Date: Mon, 13 Apr 2026 14:40:33 +0530 Subject: [PATCH 3/7] Refactor TRAP logParser : Splitting functions into dedicated modules a. split up the major/lengthy functions in logParser.py and kept them in separate files b. parse assert info -> assertInfo.py c. find crash point -> crashPoint.py d. parse tcb info -> tcbinfo.py e. cli/logAnalyser/log_parser : kept all these related files in this folder f. also moved heapNode.py to this folder, as its only called from logParser.py --- tools/trap/cli/logAnalyser/logParser.py | 679 ------------------ .../cli/logAnalyser/log_parser/assertInfo.py | 124 ++++ .../cli/logAnalyser/log_parser/crashPoint.py | 256 +++++++ .../{ => logAnalyser/log_parser}/heapNode.py | 0 .../cli/logAnalyser/log_parser/logParser.py | 278 +++++++ .../cli/logAnalyser/log_parser/tcbInfo.py | 121 ++++ tools/trap/cli/trap.py | 2 +- 7 files changed, 780 insertions(+), 680 deletions(-) delete mode 100755 tools/trap/cli/logAnalyser/logParser.py create mode 100755 tools/trap/cli/logAnalyser/log_parser/assertInfo.py create mode 100755 tools/trap/cli/logAnalyser/log_parser/crashPoint.py rename tools/trap/cli/{ => logAnalyser/log_parser}/heapNode.py (100%) create mode 100755 tools/trap/cli/logAnalyser/log_parser/logParser.py create mode 100755 tools/trap/cli/logAnalyser/log_parser/tcbInfo.py diff --git a/tools/trap/cli/logAnalyser/logParser.py b/tools/trap/cli/logAnalyser/logParser.py deleted file mode 100755 index 2377c3e0dc..0000000000 --- a/tools/trap/cli/logAnalyser/logParser.py +++ /dev/null @@ -1,679 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# Copyright 2024 Samsung Electronics All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -# either express or implied. See the License for the specific -# language governing permissions and limitations under the License. -# -########################################################################### -# File : logParser.py -# Description: It contains a logParser class which has APIs for parsing assert/crash log by using elfs. -# Gdb and NM tools are used to read the symbols - -from __future__ import print_function -import re -import os -import string -import sys, time -from array import * -import utils as utils -import logAnalyser.logUtils as logUtils -import heapNode - -# Global variables -assertion_details = "Assertion details\n" -stack_details = "Asserted task's stack details\n" -register_dump = "Asserted task's register dump\n" -BIN_ADDR_FXN = "Loading location information\n" -tcb_info = "Asserted task's TCB info" - -partition_string = "===========================================================" - -TCB_FLAG_TTYPE_SHIFT = (0) # Bits 0-1: thread type -TCB_FLAG_TTYPE_MASK = (3 << TCB_FLAG_TTYPE_SHIFT) -TCB_FLAG_TTYPE_TASK = (0 << TCB_FLAG_TTYPE_SHIFT) # Normal user task -TCB_FLAG_TTYPE_PTHREAD = (1 << TCB_FLAG_TTYPE_SHIFT) # User pthread -TCB_FLAG_TTYPE_KERNEL = (2 << TCB_FLAG_TTYPE_SHIFT) # Kernel thread -TCB_FLAG_NONCANCELABLE = (1 << 2) # Bit 2: Pthread is non-cancelable -TCB_FLAG_CANCEL_DEFERRED = (1 << 3) # Bit 3: Deferred (vs asynch) cancellation type -TCB_FLAG_CANCEL_PENDING = (1 << 4) # Bit 4: Pthread cancel is pending -TCB_FLAG_POLICY_SHIFT = (5) # Bit 5-6: Scheduling policy -TCB_FLAG_POLICY_MASK = (3 << TCB_FLAG_POLICY_SHIFT) -TCB_FLAG_SCHED_FIFO = (0 << TCB_FLAG_POLICY_SHIFT) # FIFO scheding policy -TCB_FLAG_ROUND_ROBIN = (1 << TCB_FLAG_POLICY_SHIFT) # Round robin scheding policy -TCB_FLAG_SCHED_SPORADIC = (2 << TCB_FLAG_POLICY_SHIFT) # Sporadic scheding policy -TCB_FLAG_SCHED_OTHER = (3 << TCB_FLAG_POLICY_SHIFT) # Other scheding policy -TCB_FLAG_CPU_LOCKED = (1 << 7) # Bit 7: Locked to this CPU -TCB_FLAG_EXIT_PROCESSING = (1 << 8) # Bit 8: Exitting -TCB_FLAG_SYSCALL = (1 << 10) # Bit 9: In a system call - -class logParser: - - # Init function for logParser Class - def __init__(self, elf = None, log_file = None, bin_path = None, config_path= None, xip_enabled = False, have_ram_kernel_text = False): - self.elf = elf # Elf file - self.log_file = log_file # Path of Log file passed as argument - - self.g_app_idx = 0 # To extract number of apps dynamically - self.g_assertpc = 0 # To extract the global PC value g_assertpc - self.g_stext_app = [0] * 10 - self.g_etext_app = [0] * 10 - self.app_name = [] - self.read_all_elf = False - self.crash_type_assert = False - self.task_state = dict() - - # Kernel text start & end addresses in FLASH & RAM regions - self.g_stext_flash = 0 - self.g_etext_flash = 0 - self.g_stext_ram = 0 - self.g_etext_ram = 0 - - self.bin_path = bin_path - self.config_path = config_path - self.xip_enabled = xip_enabled - self.have_ram_kernel_text = have_ram_kernel_text - - logUtils.convert_stateno_statemsg(self) - - # API to find crash binary, crash point and crash type from assert log - def find_crash_point(self): - pc_value = 0 - lr_value = 0 - is_app_crash = 0 - is_kernel_crash = 0 - assertline = "" - current_line = "" - - self.g_stext_flash = utils.get_address_of_symbol("_stext_flash") - self.g_etext_flash = utils.get_address_of_symbol("_etext_flash") - if (self.have_ram_kernel_text): - self.g_stext_ram = utils.get_address_of_symbol("_stext_ram") - self.g_etext_ram = utils.get_address_of_symbol("_etext_ram") - - # Parse the contents based on tokens in log file. - with open(self.log_file) as searchfile: - for line in searchfile: - if partition_string in line: - line = next(searchfile) - current_line = line - line = next(searchfile) - continue - if current_line == register_dump: - word = line.split(':') - - # word[1] contains the register name i.e R0 or R8 - reg = word[1].split() - - if reg[0] == 'R8': - t = word[2].split( ) - - # Check for corruption of PC value in logs - if (len(word[2].split( )) != 8): - print('\nAssert logs are corrupted, and we are not able to determine the value of PC.\n') - continue - - # Last subword of word[2] contains the PC value - pc_value = int(t[-1],16) - lr_value = int(t[-2],16) - continue - - # Get the assert location PC value - if current_line == assertion_details and "Assertion failed" in line: - assertline = line - if 'Assert location (PC) :' in line: - word = line.split(':') - #word[2] contains the g_assertpc value - self.g_assertpc = int(word[2].strip(),16) - - print('-----------------------------------------------------------------------------------------') - address1 = hex(lr_value) - address2 = hex(pc_value) - result = 0 - # Check for lr & pc values in application text address range - if (pc_value != 00000000): - for app_idx in range(self.g_app_idx): - if (address1 >= hex(self.g_stext_app[app_idx]) and address1 < hex(self.g_etext_app[app_idx])): - if self.xip_enabled: - addr = lr_value - else: - addr = lr_value - int(hex(self.g_stext_app[app_idx]), 16) - f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr)) - result = f.read() - if '??' not in result and '$d' not in result: - is_app_crash = 1 - print('\n1. Crash Binary : {0}'.format(self.app_name[app_idx])) - logUtils.print_crash_type(self, assertline) - if (self.crash_type_assert == False): - print('3. Crash point (PC or LR)') - print('\n\t[ Caller - return address (LR) - of the function which has caused the crash ]') - logUtils.format_output(result, "") - if (address2 >= hex(self.g_stext_app[app_idx]) and address2 < hex(self.g_etext_app[app_idx])): - if self.xip_enabled: - addr = pc_value - else: - addr = pc_value - int(hex(self.g_stext_app[app_idx]), 16) - f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr)) - result = f.read() - if '??' not in result and '$d' not in result: - if (not is_app_crash): - print('\n1. Crash Binary : {0}'.format(self.app_name[app_idx])) - logUtils.print_crash_type(self, assertline) - if (self.crash_type_assert == False): - print('3. Crash point (PC or LR)') - is_app_crash = 1 - if (self.crash_type_assert == False): - print('\n\t[ Current location (PC) of assert ]') - logUtils.format_output(result, "") - if ((addr - 4) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 4)) - result1 = f.read() - if '??' not in result1 and '$d' not in result1: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result1, "of (pc - 4)") - if ((addr - 8) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 8)) - result2 = f.read() - if '??' not in result2 and '$d' not in result2: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result2, "of (pc - 8)") - - # Scenario when up_registerdump() is not present in dump logs, use g_assertpc value to give crash point - else: - address1 = hex(self.g_assertpc) - for app_idx in range(self.g_app_idx): - if (address1 >= hex(self.g_stext_app[app_idx]) and address1 < hex(self.g_etext_app[app_idx])): - if self.xip_enabled: - addr = self.g_assertpc - else: - addr = self.g_assertpc - int(hex(self.g_stext_app[app_idx]), 16) - f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr)) - result = f.read() - if '??' not in result and '$d' not in result: - is_app_crash = 1 - print('\n1. Crash Binary : {0}'.format(self.app_name[app_idx])) - logUtils.print_crash_type(self, assertline) - if (self.crash_type_assert == False): - print('3. Crash point (PC or LR)') - print('\n\t[ Current location (PC) of assert ]') - logUtils.format_output(result, "") - if ((addr - 4) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 4)) - result1 = f.read() - if '??' not in result1 and '$d' not in result1: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result1, "of (pc - 4)") - if ((addr - 8) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e ' + self.bin_path + self.app_name[app_idx] + '_dbg ' + hex(addr - 8)) - result2 = f.read() - if '??' not in result2 and '$d' not in result2: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result2, "of (pc - 8)") - - # Check for lr & pc values in kernel text address range - if (not is_app_crash) and (pc_value != 00000000): - address1 = hex(lr_value) - if (address1 >= hex(self.g_stext_flash) and address1 < hex(self.g_etext_flash)) or (address1 >= hex(self.g_stext_ram) and address1 < hex(self.g_etext_ram)): - # If yes, print the crash point using addr2line - f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(lr_value)) - result = f.read() - if '??' not in result and '$d' not in result: - is_kernel_crash = 1 - print('1. Crash Binary : kernel') - logUtils.print_crash_type(self, assertline) - if (self.crash_type_assert == False): - print('3. Crash point (PC or LR)') - print('\n\t[ Caller - return address (LR) - of the function which has caused the crash ]') - logUtils.format_output(result, "") - if (address2 >= hex(self.g_stext_flash) and address2 < hex(self.g_etext_flash)) or (address2 >= hex(self.g_stext_ram) and address2 < hex(self.g_etext_ram)): - f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(pc_value)) - result = f.read() - if '??' not in result and '$d' not in result: - if (not is_kernel_crash): - print('1. Crash Binary : kernel') - logUtils.print_crash_type(self, assertline) - if (self.crash_type_assert == False): - print('3. Crash point (PC or LR)') - is_kernel_crash = 1 - if (self.crash_type_assert == False): - print('\n\t[ Current location (PC) of assert ]') - logUtils.format_output(result, "") - if ((pc_value - 4) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(pc_value - 4)) - result1 = f.read() - if '??' not in result1 and '$d' not in result1: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result1, "of (pc - 4)") - if ((pc_value - 8) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(pc_value - 8)) - result2 = f.read() - if '??' not in result2 and '$d' not in result2: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result2, "of (pc - 8)") - - # Scenario when up_registerdump() is not present in dump logs, use g_assertpc value to give crash point - if (pc_value == 00000000 and self.g_assertpc): - address1 = hex(self.g_assertpc) - if (address1 >= hex(self.g_stext_flash) and address1 < hex(self.g_etext_flash)) or (address1 >= hex(self.g_stext_ram) and address1 < hex(self.g_etext_ram)): - # If yes, print the crash point using addr2line - f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(self.g_assertpc)) - result = f.read() - if '??' not in result and '$d' not in result: - is_kernel_crash = 1 - print('1. Crash Binary : kernel') - logUtils.print_crash_type(self, assertline) - if (self.crash_type_assert == False): - print('3. Crash point (PC or LR)') - print('\n\t[ Current location (PC) of assert ]') - logUtils.format_output(result, "") - if ((self.g_assertpc - 4) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(self.g_assertpc - 4)) - result1 = f.read() - if '??' not in result1 and '$d' not in result1: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result1, "of (pc - 4)") - if ((self.g_assertpc - 8) > 0x0): - f = os.popen('arm-none-eabi-addr2line -a -f -e' + self.elf + ' ' + hex(self.g_assertpc - 8)) - result2 = f.read() - if '??' not in result2 and '$d' not in result2: - if (self.crash_type_assert == False): - print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') - logUtils.format_output(result2, "of (pc - 8)") - - if (not is_app_crash) and (not is_kernel_crash): - print('1. Crash Binary : NA') - logUtils.print_crash_type(self, assertline) - if (self.crash_type_assert == False): - print('3. Crash point (PC or LR)') - # Parse the contents based on tokens in log file. - with open(self.log_file) as searchfile: - for line in searchfile: - # If PC value is invalid, show invalid PC - if 'PC value might be invalid' in line: - print('\tPC value might be invalid.') - print('\t- PC & LR values not in any text range! No probable crash point detected.') - - # API to find point of assertion - def parse_assert_info(self): - is_interrupt_mode = 0 - current_line = "" - - with open(self.log_file) as searchfile: - for line in searchfile: - if 'ERROR: Stack pointer is not within any of the allocated stack' in line: - word = line.split(':') - print('\n\t-', word[1], ':', word[2]) - - print('\n4. Code asserted in:') - - # Parse the contents based on tokens in log file to determine point of assertion in details - with open(self.log_file) as searchfile: - found_type = 0 - for line in searchfile: - if 'Code asserted in nested IRQ state!' in line: - found_type = 1 - print('\n\t- Code asserted in Nested IRQ state.') - break - elif 'Code asserted in IRQ state!' in line: - found_type = 1 - print('\n\t- Code asserted in IRQ state.') - break - elif 'Running work function is' in line: - found_type = 1 - print('\n\t- Code asserted in workqueue.') - break - if (found_type == 0): - print('\n\t- Code asserted in normal thread.') - - # Parse the contents based on tokens in log file for assert during interrupt context - with open(self.log_file) as searchfile: - for line in searchfile: - # Print the interrupt data during crash (if crashed during interrupt context) - if 'IRQ num:' in line: - word = line.split(' ') - # Last word[-1] contains the interrupt number - irq_num = word[-1] - print('\n\t- Interrupt number\t\t\t:',irq_num) - if 'Code asserted in IRQ state!' in line: - is_interrupt_mode = 1 - # It displays the interrupt handler information corresponding to the Interrupt - print("\n5. Assertion Data during interrupt mode:\n") - print('\t- Interrupt handler at addr\t\tSymbol_name') - utils.print_interrupt_handler_data(self.log_file, self.bin_path, self.app_name) - - with open(self.log_file) as searchfile: - for line in searchfile: - if 'Nested IRQ stack:' in line: - is_interrupt_mode = 2 - print("- IRQ Stack information:\n") - if (is_interrupt_mode >= 2 and is_interrupt_mode < 9): - is_interrupt_mode = is_interrupt_mode + 1 - print("\033[F", line) - - if (is_interrupt_mode): - print('\n6. Call stack of last run thread') - else: - print('\n5. Call stack of last run thread') - - # Parse the contents based on tokens in log file for memory allocation failure data - with open(self.log_file) as searchfile: - mm_fail = 0 - for line in searchfile: - # Print the mm allocation failure data during crash (if crashed during mm allocation) - if ('mm_manage_alloc_fail:' in line) and (mm_fail == 0): - print('\nMemory allocation failure logs are as below:') - mm_fail = 1 - if (mm_fail == 1): - print(line) - - # Parse the contents based on tokens in log file. - with open(self.log_file) as searchfile: - for line in searchfile: - if partition_string in line: - line = next(searchfile) - current_line = line - line = next(searchfile) - continue - # Print the current stack pointer value - if current_line == stack_details and ' sp:' in line: - word = line.split(':') - # word[2] contains the current stack pointer - stack_curr = int(word[2], 16) - print("\n\t- Current stack pointer:\t\t", hex(stack_curr)) - # Print the current running work function - if 'Running work function is' in line: - word = line.split(' ') - # Last word[-1] contains the current running work function - wf = word[-1] - curr_worker = int(wf[:-2], 16) - print("\t- Current running work function is:\t", hex(curr_worker)) - # It displays the symbol corresponding to the current running work function - print('\nCurrent running work function\t\tFile_name') - utils.print_running_work_function(self.log_file, self.bin_path, self.app_name) - - # API to Parse the call stack from input log file and to print stack values - def parse_call_stack(self): - stack_addr = 0x00000000 - stack_val = 0x00000000 - current_line = "" - - print('Stack_address\t Symbol_address\t Symbol location {: <45} File_name'.format("Symbol_name")) - - # Parse the contents based on tokens in log file. - with open(self.log_file) as searchfile: - for line in searchfile: - if partition_string in line: - line = next(searchfile) - current_line = line - line = next(searchfile) - continue - if current_line == register_dump: - word = line.split(':') - # word[1] contains the register name i.e R0 or R8 - reg = word[1].split() - - if reg[0] == 'R8': - t = word[2].split() - # Check for corruption of PC value in logs - if (len(word[2].split()) != 8): - print('\nAssert logs are corrupted, and we are not able to determine the value of PC.\n') - continue - continue - - if 'stack:' in line: - word = line.split(':') - print(word[1]) - if 'stack dump:' in line: - word = line.split(':') - print(word[1]) - - # Read the stack contents of aborted stack and check for valid addresses - if current_line == stack_details and 'up_stackdump' in line: - word = line.split(':') - t = word[2].split() - stack_addr = int(word[1], 16) - for sub_word in t: - stack_addr = stack_addr + 4 - # Check for valid address, else move to the next sub_word - if (sub_word == 'xxxxxxxx'): - continue - if (utils.bytes_needed(int(sub_word,16)) != 4): - continue - - stack_val = int(sub_word,16) - # Check if the stack address lies in kernel text address range - if (logUtils.is_kernel_text_address(self, hex(stack_val))): - #If yes, print it's corresponding symbol - utils.print_symbol(stack_addr, stack_val, 0, self.bin_path, self.app_name) - else: - if self.read_all_elf: - if not self.xip_enabled: - stack_val = stack_val - int(self.g_stext_app[is_app_symbol - 1]) - for i in range(self.g_app_idx): - utils.print_symbol(stack_addr, stack_val, i + 1, self.bin_path, self.app_name) - else: - # Check if the stack address lies in application text address range - is_app_symbol = logUtils.is_app_text_address(self, hex(stack_val)) # app index in case of application symbol - if (is_app_symbol): - #If yes, print it's corresponding symbol - if not self.xip_enabled: - stack_val = stack_val - int(self.g_stext_app[is_app_symbol - 1]) - utils.print_symbol(stack_addr, stack_val, is_app_symbol, self.bin_path, self.app_name) - - # Function to Parse the input log file (which contains wrong stackdump during assert) - def print_wrong_sp(self): - stack_addr = 0x00000000 - format_print = True - - # Parse the contents based on tokens in log file. - with open(self.log_file) as searchfile: - for line in searchfile: - # Read the stack contents of aborted stack and check for valid addresses - if 'Wrong Stack pointer' in line: - word = line.split(':') - t = word[2].split() - s = word[1].split() - stack_addr = int(s[-1], 16) - for sub_word in t: - stack_addr = stack_addr + 4 - # Check for valid address, else move to the next sub_word - if (sub_word == 'xxxxxxxx'): - continue - if (utils.bytes_needed(int(sub_word,16)) != 4): - continue - - stack_val = int(sub_word,16) - # Check if the stack address lies in kernel text address range - if (logUtils.is_kernel_text_address(self, hex(stack_val))): - if (format_print): - print('\t- SP is out of the stack range. Debug symbols corresponding to the wrong stack pointer addresses are given below:') - print('Stack_address\t Symbol_address\t Symbol location {: <45} File_name'.format("Symbol_name")) - format_print = False - #If yes, print it's corresponding symbol - utils.print_symbol(stack_addr, stack_val, 0, self.bin_path, self.app_name) - # Check if the stack address lies in application text address range - is_app_symbol = logUtils.is_app_text_address(self, hex(stack_val)) # app index in case of application symbol - if (is_app_symbol): - if (format_print): - print('\t- SP is out of the stack range. Debug symbols corresponding to the wrong stack pointer addresses are given below:') - print('Stack_address\t Symbol_address\t Symbol location {: <45} File_name'.format("Symbol_name")) - format_print = False - #If yes, print it's corresponding symbol - if not self.xip_enabled: - stack_val = stack_val - int(self.g_stext_app[is_app_symbol - 1]) - utils.print_symbol(stack_addr, stack_val, is_app_symbol, self.bin_path, self.app_name) - - # API to parse asserted tcb information - def parse_tcb_info(self): - current_line = "" - print("\nt. Asserted TCB info:\n") - with open(self.log_file) as searchfile: - for line in searchfile: - if partition_string in line: - line = next(searchfile) - current_line = line - line = next(searchfile) - continue - if tcb_info in current_line: - while '==================' not in line: - data = line.replace("|", " ").replace("/", " ").replace("\n", " ").split(":")[2] - data = str(list(filter(None, data.split(" ")))[0]) - if "State" in line: - line = line.replace(" " + data, self.task_state[data.split()[0]]) - elif "Syscall" in line: - app_idx = logUtils.is_app_text_address(self, data) - if app_idx: - if self.xip_enabled: - addr = data - else: - addr = data - int(hex(self.g_stext_app[app_idx - 1]), 16) - f = os.popen('arm-none-eabi-addr2line -f -e ' + self.bin_path + self.app_name[app_idx - 1] + '_dbg ' + hex(int(addr, 16))) - result = f.read() - if '??' not in result and '$d' not in result: - line = line.replace(data, result.replace("\n"," ").split()[0]) - elif logUtils.is_kernel_text_address(self, data): - f = os.popen('arm-none-eabi-addr2line -f -e ' + self.elf + ' ' + hex(int(data, 16))) - result = f.read() - if '??' not in result and '$d' not in result: - line = line.replace(data, result.replace("\n"," ").split()[0]) - elif "Flags" in line: - flag = int(data) - line = line.replace("\n","") - if (TCB_FLAG_TTYPE_TASK & flag): - line += "\t - User Task" - elif TCB_FLAG_TTYPE_PTHREAD & flag: - line += "\t - User Pthread" - elif TCB_FLAG_TTYPE_KERNEL & flag: - line += "\t - Kernel Thread" - if TCB_FLAG_NONCANCELABLE & flag: - line += "\t - Non-cancellable Pthread" - if TCB_FLAG_CANCEL_DEFERRED & flag: - line += "\t - Cancel Deffered" - if TCB_FLAG_CANCEL_PENDING & flag: - line += "\t - Cancel Pending" - if TCB_FLAG_ROUND_ROBIN & flag: - line += "\t - Round Robin" - elif TCB_FLAG_SCHED_FIFO & flag: - line += "\t - FIFO" - elif TCB_FLAG_SCHED_SPORADIC & flag: - line += "\t - Sporadic" - if TCB_FLAG_CPU_LOCKED & flag: - line += "\t - CPU Locked" - if TCB_FLAG_EXIT_PROCESSING & flag: - line += "\t - Exiting" - if TCB_FLAG_SYSCALL & flag: - line += "\t - In a System Call" - line += "\n" - print(' '.join(line.split(":", 1)[1:]), end = "") - line = next(searchfile) - break - - # API to parse heap region information and check heap corruption details - def parse_heap_info(self): - current_line = "" - print('\nh. Heap Region information:\n') - - with open(self.log_file) as searchfile: - heap_corr = 0 - for line in searchfile: - # Check if there is heap corruption or not - if 'Heap corruption detected' in line: - heap_corr = 1 - if (heap_corr == 1): - print('\t!!!! HEAP CORRUPTION DETECTED !!!!\n\n') - else: - print('\t!!!! NO HEAP CORRUPTION DETECTED !!!!\n\n') - - with open(self.log_file) as searchfile: - # Parse the contents based on tokens in log file for heap corruption - for line in searchfile: - # Print the heap corruption data (if any) - if 'Checking kernel heap for corruption' in line: - print("Checking kernel heap for corruption") - line = next(searchfile) - line = next(searchfile) - if heapNode.parseCorruptHeapInfo(line, self.g_app_idx, self.g_stext_app, self.g_etext_app,self.app_name, self.elf, self.bin_path, self.xip_enabled, searchfile) == False: - print("No Kernel heap corruption detected.\n") - - if 'Checking app heap for corruption' in line: - print("Checking application heap for corruption") - line = next(searchfile) - line = next(searchfile) - if heapNode.parseCorruptHeapInfo(line, self.g_app_idx, self.g_stext_app, self.g_etext_app,self.app_name, self.elf, self.bin_path, self.xip_enabled, searchfile) == False: - print("No app heap corruption detected.\n") - - # API to print all the runnning TCB in the system - def print_task_list(self): - #Parse content of file for displaying tasks - with open(self.log_file) as searchfile: - for line in searchfile: - if 'Stack overflow error has occurred' in line: - print("\n!! Stack overflow error has occurred !!") - if 'List of all tasks in the system' in line: - print("\nList of all tasks in the system:\n") - line = next(searchfile) - line = next(searchfile) - print(line[0:], end = "") - line = next(searchfile) - while line != "No more lines" and '==================' not in line: - if '----------------------------' in line: - print(line[0:], end = "") - line = next(searchfile, "No more lines") - continue - current_line = line.replace("|", " ").replace("/", " ").replace("\n", " ").split(" ") - current_line = list(filter(None, current_line)) - state = current_line[len(current_line) - 1] - line = line.replace(" " + state, self.task_state[state]) - print(line[0:], end = "") - line = next(searchfile, "No more lines") - - # Function to Parse the i/p log file in case of app crashes to corresponding app display debug symbols - def parse_log(self): - - os.system("nm --defined-only -n -C " + self.bin_path + "tinyara.axf > " + self.bin_path + "Kernel.map") - utils.setup_symbol_table(self.bin_path + "Kernel.map", 0) - - for idx in range(self.g_app_idx): - os.system("nm --defined-only -n -C " + self.bin_path + self.app_name[idx] + "_dbg > " + self.bin_path + self.app_name[idx] + ".map") - utils.setup_symbol_table(self.bin_path + self.app_name[idx] + ".map", idx + 1) - - # Find crash Point - self.find_crash_point() - - # Find point of assertion and assertion details - self.parse_assert_info() - - # It displays the debug symbols corresponding to all the addresses in the kernel and application text address range - self.parse_call_stack() - - # Parse heap region information - self.parse_heap_info() - - # Parse asserted tcb information - self.parse_tcb_info() - - print('\nx. Miscellaneous information:') - - # It displays the debug symbols corresponding to all the wrong sp addresses (if any) - self.print_wrong_sp() - - # print current running task List - self.print_task_list() - diff --git a/tools/trap/cli/logAnalyser/log_parser/assertInfo.py b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py new file mode 100755 index 0000000000..bf5e2fc58b --- /dev/null +++ b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +# File : assertInfo.py +# Description: Contains functions for parsing assertion information from log files + +from __future__ import print_function +import utils + + +stack_details = "Asserted task's stack details\n" + +partition_string = "===========================================================" + + +# API to find point of assertion +def parse_assert_info(parser): + is_interrupt_mode = 0 + current_line = "" + + with open(parser.log_file) as searchfile: + for line in searchfile: + if 'ERROR: Stack pointer is not within any of the allocated stack' in line: + word = line.split(':') + print('\n\t-', word[1], ':', word[2]) + + print('\n4. Code asserted in:') + + # Parse the contents based on tokens in log file to determine point of assertion in details + with open(parser.log_file) as searchfile: + found_type = 0 + for line in searchfile: + if 'Code asserted in nested IRQ state!' in line: + found_type = 1 + print('\n\t- Code asserted in Nested IRQ state.') + break + elif 'Code asserted in IRQ state!' in line: + found_type = 1 + print('\n\t- Code asserted in IRQ state.') + break + elif 'Running work function is' in line: + found_type = 1 + print('\n\t- Code asserted in workqueue.') + break + if (found_type == 0): + print('\n\t- Code asserted in normal thread.') + + # Parse the contents based on tokens in log file for assert during interrupt context + with open(parser.log_file) as searchfile: + for line in searchfile: + # Print the interrupt data during crash (if crashed during interrupt context) + if 'IRQ num:' in line: + word = line.split(' ') + # Last word[-1] contains the interrupt number + irq_num = word[-1] + print('\n\t- Interrupt number\t\t\t:',irq_num) + if 'Code asserted in IRQ state!' in line: + is_interrupt_mode = 1 + # It displays the interrupt handler information corresponding to the Interrupt + print("\n5. Assertion Data during interrupt mode:\n") + print('\t- Interrupt handler at addr\t\tSymbol_name') + utils.print_interrupt_handler_data(parser.log_file, parser.bin_path, parser.app_name) + + with open(parser.log_file) as searchfile: + for line in searchfile: + if 'Nested IRQ stack:' in line: + is_interrupt_mode = 2 + print("- IRQ Stack information:\n") + if (is_interrupt_mode >= 2 and is_interrupt_mode < 9): + is_interrupt_mode = is_interrupt_mode + 1 + print("\033[F", line) + + if (is_interrupt_mode): + print('\n6. Call stack of last run thread') + else: + print('\n5. Call stack of last run thread') + + # Parse the contents based on tokens in log file for memory allocation failure data + with open(parser.log_file) as searchfile: + mm_fail = 0 + for line in searchfile: + # Print the mm allocation failure data during crash (if crashed during mm allocation) + if ('mm_manage_alloc_fail:' in line) and (mm_fail == 0): + print('\nMemory allocation failure logs are as below:') + mm_fail = 1 + if (mm_fail == 1): + print(line) + + # Parse the contents based on tokens in log file. + with open(parser.log_file) as searchfile: + for line in searchfile: + + # Print the current stack pointer value + if current_line == stack_details and ' sp:' in line: + word = line.split(':') + # word[2] contains the current stack pointer + stack_curr = int(word[2], 16) + print("\n\t- Current stack pointer:\t\t", hex(stack_curr)) + # Print the current running work function + if 'Running work function is' in line: + word = line.split(' ') + # Last word[-1] contains the current running work function + wf = word[-1] + curr_worker = int(wf[:-2], 16) + print("\t- Current running work function is:\t", hex(curr_worker)) + # It displays the symbol corresponding to the current running work function + print('\nCurrent running work function\t\tFile_name') + utils.print_running_work_function(parser.log_file, parser.bin_path, parser.app_name) + diff --git a/tools/trap/cli/logAnalyser/log_parser/crashPoint.py b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py new file mode 100755 index 0000000000..8481908e4e --- /dev/null +++ b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +# File : crashPoint.py +# Description: Contains functions for finding crash point and crash type from log files + +from __future__ import print_function +import os +import logAnalyser.logUtils as logUtils +import utils + + +assertion_details = "Assertion details\n" +register_dump = "Asserted task's register dump\n" + +partition_string = "===========================================================" + + +# API to find crash binary, crash point and crash type from assert log +def find_crash_point(parser): + pc_value = 0 + lr_value = 0 + is_app_crash = 0 + is_kernel_crash = 0 + assertline = "" + current_line = "" + + parser.g_stext_flash = utils.get_address_of_symbol("_stext_flash") + parser.g_etext_flash = utils.get_address_of_symbol("_etext_flash") + if (parser.have_ram_kernel_text): + parser.g_stext_ram = utils.get_address_of_symbol("_stext_ram") + parser.g_etext_ram = utils.get_address_of_symbol("_etext_ram") + + # Parse the contents based on tokens in log file. + with open(parser.log_file) as searchfile: + for line in searchfile: + if partition_string in line: + line = next(searchfile) + current_line = line + line = next(searchfile) + continue + if current_line == register_dump: + word = line.split(':') + + # word[1] contains the register name i.e R0 or R8 + reg = word[1].split() + + if reg[0] == 'R8': + t = word[2].split( ) + + # Check for corruption of PC value in logs + if (len(word[2].split( )) != 8): + print('\nAssert logs are corrupted, and we are not able to determine the value of PC.\n') + continue + + # Last subword of word[2] contains the PC value + pc_value = int(t[-1],16) + lr_value = int(t[-2],16) + continue + + # Get the assert location PC value + if current_line == assertion_details and "Assertion failed" in line: + assertline = line + if 'Assert location (PC) :' in line: + word = line.split(':') + #word[2] contains the g_assertpc value + parser.g_assertpc = int(word[2].strip(),16) + + print('-----------------------------------------------------------------------------------------') + address1 = hex(lr_value) + address2 = hex(pc_value) + result = 0 + # Check for lr & pc values in application text address range + if (pc_value != 00000000): + for app_idx in range(parser.g_app_idx): + if (address1 >= hex(parser.g_stext_app[app_idx]) and address1 < hex(parser.g_etext_app[app_idx])): + if parser.xip_enabled: + addr = lr_value + else: + addr = lr_value - int(hex(parser.g_stext_app[app_idx]), 16) + f = os.popen('arm-none-eabi-addr2line -a -f -e ' + parser.bin_path + parser.app_name[app_idx] + '_dbg ' + hex(addr)) + result = f.read() + if '??' not in result and '$d' not in result: + is_app_crash = 1 + print('\n1. Crash Binary : {0}'.format(parser.app_name[app_idx])) + logUtils.print_crash_type(parser, assertline) + if (parser.crash_type_assert == False): + print('3. Crash point (PC or LR)') + print('\n\t[ Caller - return address (LR) - of the function which has caused the crash ]') + logUtils.format_output(result, "") + if (address2 >= hex(parser.g_stext_app[app_idx]) and address2 < hex(parser.g_etext_app[app_idx])): + if parser.xip_enabled: + addr = pc_value + else: + addr = pc_value - int(hex(parser.g_stext_app[app_idx]), 16) + f = os.popen('arm-none-eabi-addr2line -a -f -e ' + parser.bin_path + parser.app_name[app_idx] + '_dbg ' + hex(addr)) + result = f.read() + if '??' not in result and '$d' not in result: + if (not is_app_crash): + print('\n1. Crash Binary : {0}'.format(parser.app_name[app_idx])) + logUtils.print_crash_type(parser, assertline) + if (parser.crash_type_assert == False): + print('3. Crash point (PC or LR)') + is_app_crash = 1 + if (parser.crash_type_assert == False): + print('\n\t[ Current location (PC) of assert ]') + logUtils.format_output(result, "") + if ((addr - 4) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e ' + parser.bin_path + parser.app_name[app_idx] + '_dbg ' + hex(addr - 4)) + result1 = f.read() + if '??' not in result1 and '$d' not in result1: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result1, "of (pc - 4)") + if ((addr - 8) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e ' + parser.bin_path + parser.app_name[app_idx] + '_dbg ' + hex(addr - 8)) + result2 = f.read() + if '??' not in result2 and '$d' not in result2: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result2, "of (pc - 8)") + + # Scenario when up_registerdump() is not present in dump logs, use g_assertpc value to give crash point + else: + address1 = hex(parser.g_assertpc) + for app_idx in range(parser.g_app_idx): + if (address1 >= hex(parser.g_stext_app[app_idx]) and address1 < hex(parser.g_etext_app[app_idx])): + if parser.xip_enabled: + addr = parser.g_assertpc + else: + addr = parser.g_assertpc - int(hex(parser.g_stext_app[app_idx]), 16) + f = os.popen('arm-none-eabi-addr2line -a -f -e ' + parser.bin_path + parser.app_name[app_idx] + '_dbg ' + hex(addr)) + result = f.read() + if '??' not in result and '$d' not in result: + is_app_crash = 1 + print('\n1. Crash Binary : {0}'.format(parser.app_name[app_idx])) + logUtils.print_crash_type(parser, assertline) + if (parser.crash_type_assert == False): + print('3. Crash point (PC or LR)') + print('\n\t[ Current location (PC) of assert ]') + logUtils.format_output(result, "") + if ((addr - 4) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e ' + parser.bin_path + parser.app_name[app_idx] + '_dbg ' + hex(addr - 4)) + result1 = f.read() + if '??' not in result1 and '$d' not in result1: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result1, "of (pc - 4)") + if ((addr - 8) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e ' + parser.bin_path + parser.app_name[app_idx] + '_dbg ' + hex(addr - 8)) + result2 = f.read() + if '??' not in result2 and '$d' not in result2: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result2, "of (pc - 8)") + + # Check for lr & pc values in kernel text address range + if (not is_app_crash) and (pc_value != 00000000): + address1 = hex(lr_value) + if (address1 >= hex(parser.g_stext_flash) and address1 < hex(parser.g_etext_flash)) or (address1 >= hex(parser.g_stext_ram) and address1 < hex(parser.g_etext_ram)): + # If yes, print the crash point using addr2line + f = os.popen('arm-none-eabi-addr2line -a -f -e' + parser.elf + ' ' + hex(lr_value)) + result = f.read() + if '??' not in result and '$d' not in result: + is_kernel_crash = 1 + print('1. Crash Binary : kernel') + logUtils.print_crash_type(parser, assertline) + if (parser.crash_type_assert == False): + print('3. Crash point (PC or LR)') + print('\n\t[ Caller - return address (LR) - of the function which has caused the crash ]') + logUtils.format_output(result, "") + if (address2 >= hex(parser.g_stext_flash) and address2 < hex(parser.g_etext_flash)) or (address2 >= hex(parser.g_stext_ram) and address2 < hex(parser.g_etext_ram)): + f = os.popen('arm-none-eabi-addr2line -a -f -e' + parser.elf + ' ' + hex(pc_value)) + result = f.read() + if '??' not in result and '$d' not in result: + if (not is_kernel_crash): + print('1. Crash Binary : kernel') + logUtils.print_crash_type(parser, assertline) + if (parser.crash_type_assert == False): + print('3. Crash point (PC or LR)') + is_kernel_crash = 1 + if (parser.crash_type_assert == False): + print('\n\t[ Current location (PC) of assert ]') + logUtils.format_output(result, "") + if ((pc_value - 4) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e' + parser.elf + ' ' + hex(pc_value - 4)) + result1 = f.read() + if '??' not in result1 and '$d' not in result1: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result1, "of (pc - 4)") + if ((pc_value - 8) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e' + parser.elf + ' ' + hex(pc_value - 8)) + result2 = f.read() + if '??' not in result2 and '$d' not in result2: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result2, "of (pc - 8)") + + # Scenario when up_registerdump() is not present in dump logs, use g_assertpc value to give crash point + if (pc_value == 00000000 and parser.g_assertpc): + address1 = hex(parser.g_assertpc) + if (address1 >= hex(parser.g_stext_flash) and address1 < hex(parser.g_etext_flash)) or (address1 >= hex(parser.g_stext_ram) and address1 < hex(parser.g_etext_ram)): + # If yes, print the crash point using addr2line + f = os.popen('arm-none-eabi-addr2line -a -f -e' + parser.elf + ' ' + hex(parser.g_assertpc)) + result = f.read() + if '??' not in result and '$d' not in result: + is_kernel_crash = 1 + print('1. Crash Binary : kernel') + logUtils.print_crash_type(parser, assertline) + if (parser.crash_type_assert == False): + print('3. Crash point (PC or LR)') + print('\n\t[ Current location (PC) of assert ]') + logUtils.format_output(result, "") + if ((parser.g_assertpc - 4) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e' + parser.elf + ' ' + hex(parser.g_assertpc - 4)) + result1 = f.read() + if '??' not in result1 and '$d' not in result1: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result1, "of (pc - 4)") + if ((parser.g_assertpc - 8) > 0x0): + f = os.popen('arm-none-eabi-addr2line -a -f -e' + parser.elf + ' ' + hex(parser.g_assertpc - 8)) + result2 = f.read() + if '??' not in result2 and '$d' not in result2: + if (parser.crash_type_assert == False): + print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') + logUtils.format_output(result2, "of (pc - 8)") + + if (not is_app_crash) and (not is_kernel_crash): + print('1. Crash Binary : NA') + logUtils.print_crash_type(parser, assertline) + if (parser.crash_type_assert == False): + print('3. Crash point (PC or LR)') + # Parse the contents based on tokens in log file. + with open(parser.log_file) as searchfile: + for line in searchfile: + # If PC value is invalid, show invalid PC + if 'PC value might be invalid' in line: + print('\tPC value might be invalid.') + print('\t- PC & LR values not in any text range! No probable crash point detected.') diff --git a/tools/trap/cli/heapNode.py b/tools/trap/cli/logAnalyser/log_parser/heapNode.py similarity index 100% rename from tools/trap/cli/heapNode.py rename to tools/trap/cli/logAnalyser/log_parser/heapNode.py diff --git a/tools/trap/cli/logAnalyser/log_parser/logParser.py b/tools/trap/cli/logAnalyser/log_parser/logParser.py new file mode 100755 index 0000000000..ac973b4df1 --- /dev/null +++ b/tools/trap/cli/logAnalyser/log_parser/logParser.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +# File : logParser.py +# Description: It contains a logParser class which has APIs for parsing assert/crash log by using elfs. +# Gdb and NM tools are used to read the symbols + +from __future__ import print_function +import re +import os +import string +import sys, time +from array import * +import utils as utils +import logAnalyser.logUtils as logUtils +import logAnalyser.log_parser.heapNode as heapNode +import logAnalyser.log_parser.assertInfo as assertInfo +import logAnalyser.log_parser.crashPoint as crashPoint +import logAnalyser.log_parser.tcbInfo as tcbInfo + +# Global variables +assertion_details = "Assertion details\n" +stack_details = "Asserted task's stack details\n" +register_dump = "Asserted task's register dump\n" +BIN_ADDR_FXN = "Loading location information\n" +tcb_info = "Asserted task's TCB info" + +partition_string = "===========================================================" + + +class logParser: + + # Init function for logParser Class + def __init__(self, elf = None, log_file = None, bin_path = None, config_path= None, xip_enabled = False, have_ram_kernel_text = False): + self.elf = elf # Elf file + self.log_file = log_file # Path of Log file passed as argument + + self.g_app_idx = 0 # To extract number of apps dynamically + self.g_assertpc = 0 # To extract the global PC value g_assertpc + self.g_stext_app = [0] * 10 + self.g_etext_app = [0] * 10 + self.app_name = [] + self.read_all_elf = False + self.crash_type_assert = False + self.task_state = dict() + + # Kernel text start & end addresses in FLASH & RAM regions + self.g_stext_flash = 0 + self.g_etext_flash = 0 + self.g_stext_ram = 0 + self.g_etext_ram = 0 + + self.bin_path = bin_path + self.config_path = config_path + self.xip_enabled = xip_enabled + self.have_ram_kernel_text = have_ram_kernel_text + + logUtils.convert_stateno_statemsg(self) + + # API to Parse the call stack from input log file and to print stack values + def parse_call_stack(self): + stack_addr = 0x00000000 + stack_val = 0x00000000 + current_line = "" + + print('Stack_address\t Symbol_address\t Symbol location {: <45} File_name'.format("Symbol_name")) + + # Parse the contents based on tokens in log file. + with open(self.log_file) as searchfile: + for line in searchfile: + if partition_string in line: + line = next(searchfile) + current_line = line + line = next(searchfile) + continue + if current_line == register_dump: + word = line.split(':') + # word[1] contains the register name i.e R0 or R8 + reg = word[1].split() + + if reg[0] == 'R8': + t = word[2].split() + # Check for corruption of PC value in logs + if (len(word[2].split()) != 8): + print('\nAssert logs are corrupted, and we are not able to determine the value of PC.\n') + continue + continue + + if 'stack:' in line: + word = line.split(':') + print(word[1]) + if 'stack dump:' in line: + word = line.split(':') + print(word[1]) + + # Read the stack contents of aborted stack and check for valid addresses + if current_line == stack_details and 'up_stackdump' in line: + word = line.split(':') + t = word[2].split() + stack_addr = int(word[1], 16) + for sub_word in t: + stack_addr = stack_addr + 4 + # Check for valid address, else move to the next sub_word + if (sub_word == 'xxxxxxxx'): + continue + if (utils.bytes_needed(int(sub_word,16)) != 4): + continue + + stack_val = int(sub_word,16) + # Check if the stack address lies in kernel text address range + if (logUtils.is_kernel_text_address(self, hex(stack_val))): + #If yes, print it's corresponding symbol + utils.print_symbol(stack_addr, stack_val, 0, self.bin_path, self.app_name) + else: + if self.read_all_elf: + if not self.xip_enabled: + stack_val = stack_val - int(self.g_stext_app[is_app_symbol - 1]) + for i in range(self.g_app_idx): + utils.print_symbol(stack_addr, stack_val, i + 1, self.bin_path, self.app_name) + else: + # Check if the stack address lies in application text address range + is_app_symbol = logUtils.is_app_text_address(self, hex(stack_val)) # app index in case of application symbol + if (is_app_symbol): + #If yes, print it's corresponding symbol + if not self.xip_enabled: + stack_val = stack_val - int(self.g_stext_app[is_app_symbol - 1]) + utils.print_symbol(stack_addr, stack_val, is_app_symbol, self.bin_path, self.app_name) + + # Function to Parse the input log file (which contains wrong stackdump during assert) + def print_wrong_sp(self): + stack_addr = 0x00000000 + format_print = True + + # Parse the contents based on tokens in log file. + with open(self.log_file) as searchfile: + for line in searchfile: + # Read the stack contents of aborted stack and check for valid addresses + if 'Wrong Stack pointer' in line: + word = line.split(':') + t = word[2].split() + s = word[1].split() + stack_addr = int(s[-1], 16) + for sub_word in t: + stack_addr = stack_addr + 4 + # Check for valid address, else move to the next sub_word + if (sub_word == 'xxxxxxxx'): + continue + if (utils.bytes_needed(int(sub_word,16)) != 4): + continue + + stack_val = int(sub_word,16) + # Check if the stack address lies in kernel text address range + if (logUtils.is_kernel_text_address(self, hex(stack_val))): + if (format_print): + print('\t- SP is out of the stack range. Debug symbols corresponding to the wrong stack pointer addresses are given below:') + print('Stack_address\t Symbol_address\t Symbol location {: <45} File_name'.format("Symbol_name")) + format_print = False + #If yes, print it's corresponding symbol + utils.print_symbol(stack_addr, stack_val, 0, self.bin_path, self.app_name) + # Check if the stack address lies in application text address range + is_app_symbol = logUtils.is_app_text_address(self, hex(stack_val)) # app index in case of application symbol + if (is_app_symbol): + if (format_print): + print('\t- SP is out of the stack range. Debug symbols corresponding to the wrong stack pointer addresses are given below:') + print('Stack_address\t Symbol_address\t Symbol location {: <45} File_name'.format("Symbol_name")) + format_print = False + #If yes, print it's corresponding symbol + if not self.xip_enabled: + stack_val = stack_val - int(self.g_stext_app[is_app_symbol - 1]) + utils.print_symbol(stack_addr, stack_val, is_app_symbol, self.bin_path, self.app_name) + + # API to parse heap region information and check heap corruption details + def parse_heap_info(self): + current_line = "" + print('\nh. Heap Region information:\n') + + with open(self.log_file) as searchfile: + heap_corr = 0 + for line in searchfile: + # Check if there is heap corruption or not + if 'Heap corruption detected' in line: + heap_corr = 1 + if (heap_corr == 1): + print('\t!!!! HEAP CORRUPTION DETECTED !!!!\n\n') + else: + print('\t!!!! NO HEAP CORRUPTION DETECTED !!!!\n\n') + + with open(self.log_file) as searchfile: + # Parse the contents based on tokens in log file for heap corruption + for line in searchfile: + # Print the heap corruption data (if any) + if 'Checking kernel heap for corruption' in line: + print("Checking kernel heap for corruption") + line = next(searchfile) + line = next(searchfile) + if heapNode.parseCorruptHeapInfo(line, self.g_app_idx, self.g_stext_app, self.g_etext_app,self.app_name, self.elf, self.bin_path, self.xip_enabled, searchfile) == False: + print("No Kernel heap corruption detected.\n") + + if 'Checking app heap for corruption' in line: + print("Checking application heap for corruption") + line = next(searchfile) + line = next(searchfile) + if heapNode.parseCorruptHeapInfo(line, self.g_app_idx, self.g_stext_app, self.g_etext_app,self.app_name, self.elf, self.bin_path, self.xip_enabled, searchfile) == False: + print("No app heap corruption detected.\n") + + # API to print all the runnning TCB in the system + def print_task_list(self): + #Parse content of file for displaying tasks + with open(self.log_file) as searchfile: + for line in searchfile: + if 'Stack overflow error has occurred' in line: + print("\n!! Stack overflow error has occurred !!") + if 'List of all tasks in the system' in line: + print("\nList of all tasks in the system:\n") + line = next(searchfile) + line = next(searchfile) + print(line[0:], end = "") + line = next(searchfile) + while line != "No more lines" and '==================' not in line: + if '----------------------------' in line: + print(line[0:], end = "") + line = next(searchfile, "No more lines") + continue + current_line = line.replace("|", " ").replace("/", " ").replace("\n", " ").split(" ") + current_line = list(filter(None, current_line)) + state = current_line[len(current_line) - 1] + line = line.replace(" " + state, self.task_state[state]) + print(line[0:], end = "") + line = next(searchfile, "No more lines") + + # Function to Parse the i/p log file in case of app crashes to corresponding app display debug symbols + def parse_log(self): + + os.system("nm --defined-only -n -C " + self.bin_path + "tinyara.axf > " + self.bin_path + "Kernel.map") + utils.setup_symbol_table(self.bin_path + "Kernel.map", 0) + + for idx in range(self.g_app_idx): + os.system("nm --defined-only -n -C " + self.bin_path + self.app_name[idx] + "_dbg > " + self.bin_path + self.app_name[idx] + ".map") + utils.setup_symbol_table(self.bin_path + self.app_name[idx] + ".map", idx + 1) + + # Find crash Point + crashPoint.find_crash_point(self) + + # Find point of assertion and assertion details + assertInfo.parse_assert_info(self) + + # It displays the debug symbols corresponding to all the addresses in the kernel and application text address range + self.parse_call_stack() + + # Parse heap region information + self.parse_heap_info() + + # Parse asserted tcb information + tcbInfo.parse_tcb_info(self) + + print('\nx. Miscellaneous information:') + + # It displays the debug symbols corresponding to all the wrong sp addresses (if any) + self.print_wrong_sp() + + # print current running task List + self.print_task_list() + diff --git a/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py b/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py new file mode 100755 index 0000000000..9a97846676 --- /dev/null +++ b/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +# File : tcbInfo.py +# Description: Contains functions for parsing TCB (Task Control Block) information from log files. +# This module extracts and displays detailed information about the asserted task including +# its state, syscall information, and thread flags. It uses addr2line to resolve addresses +# to symbol names for both kernel and application text sections. +# +# The parse_tcb_info function is the main API that: +# - Parses the asserted task's TCB information from the crash log +# - Decodes task state values to human-readable format +# - Resolves syscall addresses to function names +# - Interprets TCB flags to show thread type, scheduling policy, and various states + +from __future__ import print_function +import os +import logAnalyser.logUtils as logUtils + +tcb_info = "Asserted task's TCB info" + +partition_string = "===========================================================" + +TCB_FLAG_TTYPE_SHIFT = (0) # Bits 0-1: thread type +TCB_FLAG_TTYPE_MASK = (3 << TCB_FLAG_TTYPE_SHIFT) +TCB_FLAG_TTYPE_TASK = (0 << TCB_FLAG_TTYPE_SHIFT) # Normal user task +TCB_FLAG_TTYPE_PTHREAD = (1 << TCB_FLAG_TTYPE_SHIFT) # User pthread +TCB_FLAG_TTYPE_KERNEL = (2 << TCB_FLAG_TTYPE_SHIFT) # Kernel thread +TCB_FLAG_NONCANCELABLE = (1 << 2) # Bit 2: Pthread is non-cancelable +TCB_FLAG_CANCEL_DEFERRED = (1 << 3) # Bit 3: Deferred (vs asynch) cancellation type +TCB_FLAG_CANCEL_PENDING = (1 << 4) # Bit 4: Pthread cancel is pending +TCB_FLAG_POLICY_SHIFT = (5) # Bit 5-6: Scheduling policy +TCB_FLAG_POLICY_MASK = (3 << TCB_FLAG_POLICY_SHIFT) +TCB_FLAG_SCHED_FIFO = (0 << TCB_FLAG_POLICY_SHIFT) # FIFO scheding policy +TCB_FLAG_ROUND_ROBIN = (1 << TCB_FLAG_POLICY_SHIFT) # Round robin scheding policy +TCB_FLAG_SCHED_SPORADIC = (2 << TCB_FLAG_POLICY_SHIFT) # Sporadic scheding policy +TCB_FLAG_SCHED_OTHER = (3 << TCB_FLAG_POLICY_SHIFT) # Other scheding policy +TCB_FLAG_CPU_LOCKED = (1 << 7) # Bit 7: Locked to this CPU +TCB_FLAG_EXIT_PROCESSING = (1 << 8) # Bit 8: Exitting +TCB_FLAG_SYSCALL = (1 << 10) # Bit 9: In a system call + + +# API to parse asserted tcb information +def parse_tcb_info(parser): + current_line = "" + print("\nt. Asserted TCB info:\n") + with open(parser.log_file) as searchfile: + for line in searchfile: + if partition_string in line: + line = next(searchfile) + current_line = line + line = next(searchfile) + continue + if tcb_info in current_line: + while '==================' not in line: + data = line.replace("|", " ").replace("/", " ").replace("\n", " ").split(":")[2] + data = str(list(filter(None, data.split(" ")))[0]) + if "State" in line: + line = line.replace(" " + data, parser.task_state[data.split()[0]]) + elif "Syscall" in line: + app_idx = logUtils.is_app_text_address(parser, data) + if app_idx: + if parser.xip_enabled: + addr = data + else: + addr = data - int(hex(parser.g_stext_app[app_idx - 1]), 16) + f = os.popen('arm-none-eabi-addr2line -f -e ' + parser.bin_path + parser.app_name[app_idx - 1] + '_dbg ' + hex(int(addr, 16))) + result = f.read() + if '??' not in result and '$d' not in result: + line = line.replace(data, result.replace("\n"," ").split()[0]) + elif logUtils.is_kernel_text_address(parser, data): + f = os.popen('arm-none-eabi-addr2line -f -e ' + parser.elf + ' ' + hex(int(data, 16))) + result = f.read() + if '??' not in result and '$d' not in result: + line = line.replace(data, result.replace("\n"," ").split()[0]) + elif "Flags" in line: + flag = int(data) + line = line.replace("\n","") + if (TCB_FLAG_TTYPE_TASK & flag): + line += "\t - User Task" + elif TCB_FLAG_TTYPE_PTHREAD & flag: + line += "\t - User Pthread" + elif TCB_FLAG_TTYPE_KERNEL & flag: + line += "\t - Kernel Thread" + if TCB_FLAG_NONCANCELABLE & flag: + line += "\t - Non-cancellable Pthread" + if TCB_FLAG_CANCEL_DEFERRED & flag: + line += "\t - Cancel Deffered" + if TCB_FLAG_CANCEL_PENDING & flag: + line += "\t - Cancel Pending" + if TCB_FLAG_ROUND_ROBIN & flag: + line += "\t - Round Robin" + elif TCB_FLAG_SCHED_FIFO & flag: + line += "\t - FIFO" + elif TCB_FLAG_SCHED_SPORADIC & flag: + line += "\t - Sporadic" + if TCB_FLAG_CPU_LOCKED & flag: + line += "\t - CPU Locked" + if TCB_FLAG_EXIT_PROCESSING & flag: + line += "\t - Exiting" + if TCB_FLAG_SYSCALL & flag: + line += "\t - In a System Call" + line += "\n" + print(' '.join(line.split(":", 1)[1:]), end = "") + line = next(searchfile) + break diff --git a/tools/trap/cli/trap.py b/tools/trap/cli/trap.py index b1b3cb2221..fa91a72e3a 100755 --- a/tools/trap/cli/trap.py +++ b/tools/trap/cli/trap.py @@ -27,7 +27,7 @@ import sys, time from getopt import GetoptError, getopt as GetOpt from dumpParser import dumpParser -from logAnalyser.logParser import logParser +from logAnalyser.log_parser.logParser import logParser from logAnalyser.logPreprocessor import preprocessLogFile # Global variables From 860f4bdc8b62a2ee4137aa1dfcd9c30c438196b9 Mon Sep 17 00:00:00 2001 From: varghese-bibin Date: Fri, 17 Apr 2026 12:28:51 +0530 Subject: [PATCH 4/7] Refactor TRAP logParser : code review changes a. changed Copyright 2024 -> 2026 for new files b. added newline at the end of the files c. kept all the strings used in cli/logAnalyser in cli/logAnalyser/logUtils.py --- tools/trap/cli/logAnalyser/logPreprocessor.py | 11 +- tools/trap/cli/logAnalyser/logUtils.py | 256 +++++++++--------- .../cli/logAnalyser/log_parser/assertInfo.py | 7 +- .../cli/logAnalyser/log_parser/crashPoint.py | 8 +- .../cli/logAnalyser/log_parser/heapNode.py | 3 +- .../cli/logAnalyser/log_parser/logParser.py | 13 +- .../cli/logAnalyser/log_parser/tcbInfo.py | 6 +- 7 files changed, 158 insertions(+), 146 deletions(-) diff --git a/tools/trap/cli/logAnalyser/logPreprocessor.py b/tools/trap/cli/logAnalyser/logPreprocessor.py index 066601e571..974684d561 100755 --- a/tools/trap/cli/logAnalyser/logPreprocessor.py +++ b/tools/trap/cli/logAnalyser/logPreprocessor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ########################################################################### # -# Copyright 2024 Samsung Electronics All Rights Reserved. +# Copyright 2026 Samsung Electronics All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,14 +23,15 @@ from __future__ import print_function import os import sys +import logAnalyser.logUtils as logUtils from array import * # Global variables -assertion_details = "Assertion details\n" -BIN_ADDR_FXN = "Loading location information\n" +assertion_details = logUtils.assertion_details +BIN_ADDR_FXN = logUtils.BIN_ADDR_FXN -partition_string = "===========================================================" +partition_string = logUtils.partition_string # Function to format logs and delete the timestamp (supported formats-|xxxxxxxxx| and [xxxxxxxxx]) if it consists of timestamp at the start of each log line @@ -150,4 +151,4 @@ def preprocessLogFile(lparser): format_log_file(lparser.log_file) # Get the number of application binaries, names, text address and sizes - find_number_of_binaries(lparser) \ No newline at end of file + find_number_of_binaries(lparser) diff --git a/tools/trap/cli/logAnalyser/logUtils.py b/tools/trap/cli/logAnalyser/logUtils.py index 9f4f2fb5b0..c8970cbb13 100755 --- a/tools/trap/cli/logAnalyser/logUtils.py +++ b/tools/trap/cli/logAnalyser/logUtils.py @@ -1,122 +1,134 @@ -#!/usr/bin/env python -########################################################################### -# -# Copyright 2024 Samsung Electronics All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -# either express or implied. See the License for the specific -# language governing permissions and limitations under the License. -# -########################################################################### -# File : utils.py -# Description: Contains utility functions for log parsing including -# formatting, crash type detection, address validation, and state mapping - -from __future__ import print_function - - - -def format_output(res, string): - r = res.split('\n') - print('\t- symbol addr {0} : {1}'.format(string, r[0])) - print('\t- function name {0} : {1}'.format(string, r[1])) - print('\t- file {0} : {1}'.format(string, r[2])) - -def print_crash_type(parser, string): - if 'up_memfault' in string: - print('\n2. Crash type : memory fault') - elif 'up_busfault' in string: - print('\n2. Crash type : bus fault') - elif 'up_usagefault' in string: - print('\n2. Crash type : usage fault') - elif 'up_hardfault' in string: - print('\n2. Crash type : hard fault') - elif 'dataabort' in string: - print('\n2. Crash type : data abort') - elif 'prefetchabort' in string: - print('\n2. Crash type : prefetch abort') - elif 'undefinedinsn' in string: - print('\n2. Crash type : undefined instruction abort') - elif 'Assertion failed' in string: - parser.crash_type_assert = True - print('\n2. Crash type : code assertion by code ASSERT or PANIC') - else: - print('\n2. Crash type : etc') - if (parser.crash_type_assert == True): - print('\n3. Crash point\n\t-', string.split(': ',1)[1]) - else: - print(' Crash log\n\t-', string) - -def is_app_text_address(parser, address): - idx = 0 - # Check the application text address range - for idx in range(parser.g_app_idx): - if (address >= hex(parser.g_stext_app[idx]) and address < hex(parser.g_etext_app[idx])): - return (idx + 1) - if (idx == parser.g_app_idx): - return False - - -# Function to check if address lies in the kernel text address range -def is_kernel_text_address(parser, address): - - # Check the kernel text address range - if (address >= hex(parser.g_stext_ram) and address < hex(parser.g_etext_ram)) or (address >= hex(parser.g_stext_flash) and address < hex(parser.g_etext_flash)): - return True - else: - return False - - -def convert_stateno_statemsg(parser): - # Parse configuration to determine enabled features - config_smp = False - config_disable_signals = False - config_disable_mqueue = False - config_paging = False - - # Read configuration file - with open(parser.config_path) as configfile: - for line in configfile: - if "CONFIG_SMP=y" in line: - config_smp = True - elif "CONFIG_DISABLE_SIGNALS=y" in line: - config_disable_signals = True - elif "CONFIG_DISABLE_MQUEUE=y" in line: - config_disable_mqueue = True - elif "CONFIG_PAGING=y" in line: - config_paging = True - - # Build task state mapping - state_no = 0 - parser.task_state[str(state_no)] = " Invalid" - state_no+=1 - parser.task_state[str(state_no)] = " Pending preemption unlock" - state_no+=1 - parser.task_state[str(state_no)] = " Wait to scheduling (Ready)" - state_no+=1 - parser.task_state[str(state_no)] = " Assigned to CPU (Ready)" - state_no+=1 - parser.task_state[str(state_no)] = " Running" - state_no+=1 - parser.task_state[str(state_no)] = " Inactive" - state_no+=1 - parser.task_state[str(state_no)] = " Wait Semaphore" - state_no+=1 - parser.task_state[str(state_no)] = " Wait FIN" - state_no+=1 - parser.task_state[str(state_no)] = " Wait Signal" - state_no+=1 - parser.task_state[str(state_no)] = " Wait MQ Receive (MQ Empty)" - state_no+=1 - parser.task_state[str(state_no)] = " Wait MQ Send (MQ Full)" - state_no+=1 - parser.task_state[str(state_no)] = " Wait Page Fill" - state_no+=1 \ No newline at end of file +#!/usr/bin/env python +########################################################################### +# +# Copyright 2026 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +# File : utils.py +# Description: Contains utility functions for log parsing including +# formatting, crash type detection, address validation, and state mapping + +from __future__ import print_function + +# strings used for log analysis +assertion_details = "Assertion details\n" +stack_details = "Asserted task's stack details\n" +register_dump = "Asserted task's register dump\n" +BIN_ADDR_FXN = "Loading location information\n" +tcb_info = "Asserted task's TCB info" + +partition_string = "===========================================================" + +# used in heapNode +closing_log_line = "##########################################################################################################################################" + + + +def format_output(res, string): + r = res.split('\n') + print('\t- symbol addr {0} : {1}'.format(string, r[0])) + print('\t- function name {0} : {1}'.format(string, r[1])) + print('\t- file {0} : {1}'.format(string, r[2])) + +def print_crash_type(parser, string): + if 'up_memfault' in string: + print('\n2. Crash type : memory fault') + elif 'up_busfault' in string: + print('\n2. Crash type : bus fault') + elif 'up_usagefault' in string: + print('\n2. Crash type : usage fault') + elif 'up_hardfault' in string: + print('\n2. Crash type : hard fault') + elif 'dataabort' in string: + print('\n2. Crash type : data abort') + elif 'prefetchabort' in string: + print('\n2. Crash type : prefetch abort') + elif 'undefinedinsn' in string: + print('\n2. Crash type : undefined instruction abort') + elif 'Assertion failed' in string: + parser.crash_type_assert = True + print('\n2. Crash type : code assertion by code ASSERT or PANIC') + else: + print('\n2. Crash type : etc') + if (parser.crash_type_assert == True): + print('\n3. Crash point\n\t-', string.split(': ',1)[1]) + else: + print(' Crash log\n\t-', string) + +def is_app_text_address(parser, address): + idx = 0 + # Check the application text address range + for idx in range(parser.g_app_idx): + if (address >= hex(parser.g_stext_app[idx]) and address < hex(parser.g_etext_app[idx])): + return (idx + 1) + if (idx == parser.g_app_idx): + return False + + +# Function to check if address lies in the kernel text address range +def is_kernel_text_address(parser, address): + + # Check the kernel text address range + if (address >= hex(parser.g_stext_ram) and address < hex(parser.g_etext_ram)) or (address >= hex(parser.g_stext_flash) and address < hex(parser.g_etext_flash)): + return True + else: + return False + + +def convert_stateno_statemsg(parser): + # Parse configuration to determine enabled features + config_smp = False + config_disable_signals = False + config_disable_mqueue = False + config_paging = False + + # Read configuration file + with open(parser.config_path) as configfile: + for line in configfile: + if "CONFIG_SMP=y" in line: + config_smp = True + elif "CONFIG_DISABLE_SIGNALS=y" in line: + config_disable_signals = True + elif "CONFIG_DISABLE_MQUEUE=y" in line: + config_disable_mqueue = True + elif "CONFIG_PAGING=y" in line: + config_paging = True + + # Build task state mapping + state_no = 0 + parser.task_state[str(state_no)] = " Invalid" + state_no+=1 + parser.task_state[str(state_no)] = " Pending preemption unlock" + state_no+=1 + parser.task_state[str(state_no)] = " Wait to scheduling (Ready)" + state_no+=1 + parser.task_state[str(state_no)] = " Assigned to CPU (Ready)" + state_no+=1 + parser.task_state[str(state_no)] = " Running" + state_no+=1 + parser.task_state[str(state_no)] = " Inactive" + state_no+=1 + parser.task_state[str(state_no)] = " Wait Semaphore" + state_no+=1 + parser.task_state[str(state_no)] = " Wait FIN" + state_no+=1 + parser.task_state[str(state_no)] = " Wait Signal" + state_no+=1 + parser.task_state[str(state_no)] = " Wait MQ Receive (MQ Empty)" + state_no+=1 + parser.task_state[str(state_no)] = " Wait MQ Send (MQ Full)" + state_no+=1 + parser.task_state[str(state_no)] = " Wait Page Fill" + state_no+=1 diff --git a/tools/trap/cli/logAnalyser/log_parser/assertInfo.py b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py index bf5e2fc58b..eade69bc9f 100755 --- a/tools/trap/cli/logAnalyser/log_parser/assertInfo.py +++ b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ########################################################################### # -# Copyright 2024 Samsung Electronics All Rights Reserved. +# Copyright 2026 Samsung Electronics All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,11 +21,12 @@ from __future__ import print_function import utils +import logAnalyser.logUtils as logUtils -stack_details = "Asserted task's stack details\n" +stack_details = logUtils.stack_details -partition_string = "===========================================================" +partition_string = logUtils.partition_string # API to find point of assertion diff --git a/tools/trap/cli/logAnalyser/log_parser/crashPoint.py b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py index 8481908e4e..937197d0d8 100755 --- a/tools/trap/cli/logAnalyser/log_parser/crashPoint.py +++ b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ########################################################################### # -# Copyright 2024 Samsung Electronics All Rights Reserved. +# Copyright 2026 Samsung Electronics All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,10 +25,10 @@ import utils -assertion_details = "Assertion details\n" -register_dump = "Asserted task's register dump\n" +assertion_details = logUtils.assertion_details +register_dump = logUtils.register_dump -partition_string = "===========================================================" +partition_string = logUtils.partition_string # API to find crash binary, crash point and crash type from assert log diff --git a/tools/trap/cli/logAnalyser/log_parser/heapNode.py b/tools/trap/cli/logAnalyser/log_parser/heapNode.py index 750a65ace2..8803dcf8c0 100755 --- a/tools/trap/cli/logAnalyser/log_parser/heapNode.py +++ b/tools/trap/cli/logAnalyser/log_parser/heapNode.py @@ -21,8 +21,9 @@ from __future__ import print_function import os +import logAnalyser.logUtils as logUtils -closing_log_line = "##########################################################################################################################################" +closing_log_line = logUtils.closing_log_line class heap_node: diff --git a/tools/trap/cli/logAnalyser/log_parser/logParser.py b/tools/trap/cli/logAnalyser/log_parser/logParser.py index ac973b4df1..1d2420fcec 100755 --- a/tools/trap/cli/logAnalyser/log_parser/logParser.py +++ b/tools/trap/cli/logAnalyser/log_parser/logParser.py @@ -33,14 +33,11 @@ import logAnalyser.log_parser.crashPoint as crashPoint import logAnalyser.log_parser.tcbInfo as tcbInfo -# Global variables -assertion_details = "Assertion details\n" -stack_details = "Asserted task's stack details\n" -register_dump = "Asserted task's register dump\n" -BIN_ADDR_FXN = "Loading location information\n" -tcb_info = "Asserted task's TCB info" - -partition_string = "===========================================================" + +stack_details = logUtils.stack_details +register_dump = logUtils.register_dump + +partition_string = logUtils.partition_string class logParser: diff --git a/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py b/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py index 9a97846676..8ef5f637d7 100755 --- a/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py +++ b/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ########################################################################### # -# Copyright 2024 Samsung Electronics All Rights Reserved. +# Copyright 2026 Samsung Electronics All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,9 +32,9 @@ import os import logAnalyser.logUtils as logUtils -tcb_info = "Asserted task's TCB info" +tcb_info = logUtils.tcb_info -partition_string = "===========================================================" +partition_string = logUtils.partition_string TCB_FLAG_TTYPE_SHIFT = (0) # Bits 0-1: thread type TCB_FLAG_TTYPE_MASK = (3 << TCB_FLAG_TTYPE_SHIFT) From c42ce09d081d2461cca479230713c8c1bfdaa264 Mon Sep 17 00:00:00 2001 From: varghese-bibin Date: Fri, 17 Apr 2026 15:03:08 +0530 Subject: [PATCH 5/7] Refactor TRAP logParser : modularize large functions into smaller ones. a. Modified `parse_assert_info()` in `cli/logAnalyser/log_parser/assertInfo.py` b. Refactored monolithic function into modular design for improved maintainability c. Added preprocessing stage: `format_log_file()` and `find_number_of_binaries()` d. Split assert info parsing into dedicated handlers: `parse_assert_info()`, `get_assert_location()`, `check_IRQ_assertion()`, `memory_allocation_failure()`, `print_current_task()` --- .../cli/logAnalyser/log_parser/assertInfo.py | 85 ++++++++++++------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/tools/trap/cli/logAnalyser/log_parser/assertInfo.py b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py index eade69bc9f..df40a2dcc0 100755 --- a/tools/trap/cli/logAnalyser/log_parser/assertInfo.py +++ b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py @@ -28,22 +28,8 @@ partition_string = logUtils.partition_string - -# API to find point of assertion -def parse_assert_info(parser): - is_interrupt_mode = 0 - current_line = "" - - with open(parser.log_file) as searchfile: - for line in searchfile: - if 'ERROR: Stack pointer is not within any of the allocated stack' in line: - word = line.split(':') - print('\n\t-', word[1], ':', word[2]) - - print('\n4. Code asserted in:') - - # Parse the contents based on tokens in log file to determine point of assertion in details - with open(parser.log_file) as searchfile: +def get_assert_location(log_file): + with open(log_file) as searchfile: found_type = 0 for line in searchfile: if 'Code asserted in nested IRQ state!' in line: @@ -61,8 +47,10 @@ def parse_assert_info(parser): if (found_type == 0): print('\n\t- Code asserted in normal thread.') - # Parse the contents based on tokens in log file for assert during interrupt context - with open(parser.log_file) as searchfile: + +def check_IRQ_assertion(log_file, bin_path, app_name): + is_interrupt_mode = 0 + with open(log_file) as searchfile: for line in searchfile: # Print the interrupt data during crash (if crashed during interrupt context) if 'IRQ num:' in line: @@ -75,9 +63,9 @@ def parse_assert_info(parser): # It displays the interrupt handler information corresponding to the Interrupt print("\n5. Assertion Data during interrupt mode:\n") print('\t- Interrupt handler at addr\t\tSymbol_name') - utils.print_interrupt_handler_data(parser.log_file, parser.bin_path, parser.app_name) + utils.print_interrupt_handler_data(log_file, bin_path, app_name) - with open(parser.log_file) as searchfile: + with open(log_file) as searchfile: for line in searchfile: if 'Nested IRQ stack:' in line: is_interrupt_mode = 2 @@ -86,13 +74,11 @@ def parse_assert_info(parser): is_interrupt_mode = is_interrupt_mode + 1 print("\033[F", line) - if (is_interrupt_mode): - print('\n6. Call stack of last run thread') - else: - print('\n5. Call stack of last run thread') + return is_interrupt_mode - # Parse the contents based on tokens in log file for memory allocation failure data - with open(parser.log_file) as searchfile: + +def memory_allocation_failure(log_file): + with open(log_file) as searchfile: mm_fail = 0 for line in searchfile: # Print the mm allocation failure data during crash (if crashed during mm allocation) @@ -102,9 +88,17 @@ def parse_assert_info(parser): if (mm_fail == 1): print(line) - # Parse the contents based on tokens in log file. - with open(parser.log_file) as searchfile: + +def print_current_task(log_file, bin_path, app_name): + current_line = "" + + with open(log_file) as searchfile: for line in searchfile: + if partition_string in line: + line = next(searchfile) + current_line = line + line = next(searchfile) + continue # Print the current stack pointer value if current_line == stack_details and ' sp:' in line: @@ -121,5 +115,38 @@ def parse_assert_info(parser): print("\t- Current running work function is:\t", hex(curr_worker)) # It displays the symbol corresponding to the current running work function print('\nCurrent running work function\t\tFile_name') - utils.print_running_work_function(parser.log_file, parser.bin_path, parser.app_name) + utils.print_running_work_function(log_file, bin_path, app_name) + + +# API to find point of assertion +def parse_assert_info(parser): + log_file, bin_path, app_name = parser.log_file, parser.bin_path, parser.app_name + + with open(parser.log_file) as searchfile: + for line in searchfile: + if 'ERROR: Stack pointer is not within any of the allocated stack' in line: + word = line.split(':') + print('\n\t-', word[1], ':', word[2]) + + print('\n4. Code asserted in:') + # Parse the contents based on tokens in log file to determine point of assertion in details + get_assert_location(log_file) + + + # Parse the contents based on tokens in log file for assert during interrupt context + is_interrupt_mode = check_IRQ_assertion(log_file, bin_path, app_name) + + + if (is_interrupt_mode): + print('\n6. Call stack of last run thread') + else: + print('\n5. Call stack of last run thread') + + # Parse the contents based on tokens in log file for memory allocation failure data + memory_allocation_failure(log_file) + + + # Parse the contents based on tokens in log file. + print_current_task(log_file, bin_path, app_name) + \ No newline at end of file From c11d8d15d4dc6b8b681afad77b649595ec061394 Mon Sep 17 00:00:00 2001 From: varghese-bibin Date: Mon, 20 Apr 2026 08:54:41 +0530 Subject: [PATCH 6/7] Refactor TRAP logParser : Refactoring crashInfo.py a. Modified `find_crash_point()` in `cli/logAnalyser/log_parser/crashPoint.py` b. Refactored monolithic function into modular design for improved maintainability c. Split crash point detection into dedicated handlers: `find_crash_point()`, `extract_crash_info()`, `check_app_crash()`, `check_kernel_crash()` --- tools/trap/cli/logAnalyser/logPreprocessor.py | 1 + tools/trap/cli/logAnalyser/logUtils.py | 2 + .../cli/logAnalyser/log_parser/assertInfo.py | 3 +- .../cli/logAnalyser/log_parser/crashPoint.py | 63 +++++++++++++++---- .../cli/logAnalyser/log_parser/logParser.py | 1 + .../cli/logAnalyser/log_parser/tcbInfo.py | 2 + 6 files changed, 60 insertions(+), 12 deletions(-) diff --git a/tools/trap/cli/logAnalyser/logPreprocessor.py b/tools/trap/cli/logAnalyser/logPreprocessor.py index 974684d561..b38472c3a3 100755 --- a/tools/trap/cli/logAnalyser/logPreprocessor.py +++ b/tools/trap/cli/logAnalyser/logPreprocessor.py @@ -152,3 +152,4 @@ def preprocessLogFile(lparser): # Get the number of application binaries, names, text address and sizes find_number_of_binaries(lparser) + diff --git a/tools/trap/cli/logAnalyser/logUtils.py b/tools/trap/cli/logAnalyser/logUtils.py index c8970cbb13..09987eb9fb 100755 --- a/tools/trap/cli/logAnalyser/logUtils.py +++ b/tools/trap/cli/logAnalyser/logUtils.py @@ -132,3 +132,5 @@ def convert_stateno_statemsg(parser): state_no+=1 parser.task_state[str(state_no)] = " Wait Page Fill" state_no+=1 + + diff --git a/tools/trap/cli/logAnalyser/log_parser/assertInfo.py b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py index df40a2dcc0..f1b6935822 100755 --- a/tools/trap/cli/logAnalyser/log_parser/assertInfo.py +++ b/tools/trap/cli/logAnalyser/log_parser/assertInfo.py @@ -149,4 +149,5 @@ def parse_assert_info(parser): # Parse the contents based on tokens in log file. print_current_task(log_file, bin_path, app_name) - \ No newline at end of file + + diff --git a/tools/trap/cli/logAnalyser/log_parser/crashPoint.py b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py index 937197d0d8..d2b27cfd9c 100755 --- a/tools/trap/cli/logAnalyser/log_parser/crashPoint.py +++ b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py @@ -31,15 +31,17 @@ partition_string = logUtils.partition_string -# API to find crash binary, crash point and crash type from assert log -def find_crash_point(parser): - pc_value = 0 - lr_value = 0 - is_app_crash = 0 - is_kernel_crash = 0 - assertline = "" - current_line = "" - +pc_value = 0 +lr_value = 0 +is_app_crash = 0 +is_kernel_crash = 0 +assertline = "" + + +def extract_crash_info(parser): + global pc_value, lr_value, assertline + current_line = "" + parser.g_stext_flash = utils.get_address_of_symbol("_stext_flash") parser.g_etext_flash = utils.get_address_of_symbol("_etext_flash") if (parser.have_ram_kernel_text): @@ -81,7 +83,11 @@ def find_crash_point(parser): #word[2] contains the g_assertpc value parser.g_assertpc = int(word[2].strip(),16) - print('-----------------------------------------------------------------------------------------') + + +def check_app_crash(parser): + global pc_value, lr_value, is_app_crash, assertline + address1 = hex(lr_value) address2 = hex(pc_value) result = 0 @@ -169,7 +175,15 @@ def find_crash_point(parser): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') logUtils.format_output(result2, "of (pc - 8)") - # Check for lr & pc values in kernel text address range + + +def check_kernel_crash(parser): + global pc_value, lr_value, is_kernel_crash, assertline + + address1 = hex(lr_value) + address2 = hex(pc_value) + result = 0 + if (not is_app_crash) and (pc_value != 00000000): address1 = hex(lr_value) if (address1 >= hex(parser.g_stext_flash) and address1 < hex(parser.g_etext_flash)) or (address1 >= hex(parser.g_stext_ram) and address1 < hex(parser.g_etext_ram)): @@ -242,6 +256,32 @@ def find_crash_point(parser): print('\n\t[ Exact crash point might be -4 or -8 bytes from the PC ]') logUtils.format_output(result2, "of (pc - 8)") + + +# API to find crash binary, crash point and crash type from assert log +def find_crash_point(parser): + global is_app_crash, is_kernel_crash, assertline + + parser.g_stext_flash = utils.get_address_of_symbol("_stext_flash") + parser.g_etext_flash = utils.get_address_of_symbol("_etext_flash") + if (parser.have_ram_kernel_text): + parser.g_stext_ram = utils.get_address_of_symbol("_stext_ram") + parser.g_etext_ram = utils.get_address_of_symbol("_etext_ram") + + # Parse the contents based on tokens in log file. + # pc_value, lr_value, assertline + extract_crash_info(parser) + + + print('-----------------------------------------------------------------------------------------') + + # Check for application crash + check_app_crash(parser) + + # Check for kernel crash + check_kernel_crash(parser) + + if (not is_app_crash) and (not is_kernel_crash): print('1. Crash Binary : NA') logUtils.print_crash_type(parser, assertline) @@ -254,3 +294,4 @@ def find_crash_point(parser): if 'PC value might be invalid' in line: print('\tPC value might be invalid.') print('\t- PC & LR values not in any text range! No probable crash point detected.') + diff --git a/tools/trap/cli/logAnalyser/log_parser/logParser.py b/tools/trap/cli/logAnalyser/log_parser/logParser.py index 1d2420fcec..d28d057665 100755 --- a/tools/trap/cli/logAnalyser/log_parser/logParser.py +++ b/tools/trap/cli/logAnalyser/log_parser/logParser.py @@ -273,3 +273,4 @@ def parse_log(self): # print current running task List self.print_task_list() + diff --git a/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py b/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py index 8ef5f637d7..48731ee9b8 100755 --- a/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py +++ b/tools/trap/cli/logAnalyser/log_parser/tcbInfo.py @@ -119,3 +119,5 @@ def parse_tcb_info(parser): print(' '.join(line.split(":", 1)[1:]), end = "") line = next(searchfile) break + + From 9881e800da248838655aaf7084bfaab8d8d11830 Mon Sep 17 00:00:00 2001 From: varghese-bibin Date: Tue, 21 Apr 2026 11:30:26 +0530 Subject: [PATCH 7/7] Refactor TRAP logParser : Removed duplicate code --- tools/trap/cli/logAnalyser/log_parser/crashPoint.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) mode change 100755 => 100644 tools/trap/cli/logAnalyser/log_parser/crashPoint.py diff --git a/tools/trap/cli/logAnalyser/log_parser/crashPoint.py b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py old mode 100755 new mode 100644 index d2b27cfd9c..ca90c8251b --- a/tools/trap/cli/logAnalyser/log_parser/crashPoint.py +++ b/tools/trap/cli/logAnalyser/log_parser/crashPoint.py @@ -42,12 +42,6 @@ def extract_crash_info(parser): global pc_value, lr_value, assertline current_line = "" - parser.g_stext_flash = utils.get_address_of_symbol("_stext_flash") - parser.g_etext_flash = utils.get_address_of_symbol("_etext_flash") - if (parser.have_ram_kernel_text): - parser.g_stext_ram = utils.get_address_of_symbol("_stext_ram") - parser.g_etext_ram = utils.get_address_of_symbol("_etext_ram") - # Parse the contents based on tokens in log file. with open(parser.log_file) as searchfile: for line in searchfile: @@ -184,6 +178,7 @@ def check_kernel_crash(parser): address2 = hex(pc_value) result = 0 + # Check for lr & pc values in kernel text address range if (not is_app_crash) and (pc_value != 00000000): address1 = hex(lr_value) if (address1 >= hex(parser.g_stext_flash) and address1 < hex(parser.g_etext_flash)) or (address1 >= hex(parser.g_stext_ram) and address1 < hex(parser.g_etext_ram)):