mirror of https://github.com/ARMmbed/mbed-os.git
				
				
				
			
		
			
				
	
	
		
			214 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
#!/usr/bin/env python
 | 
						|
"""
 | 
						|
mbed SDK
 | 
						|
Copyright (c) 2017-2019 ARM Limited
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
LIBRARIES BUILD
 | 
						|
"""
 | 
						|
 | 
						|
from __future__ import print_function
 | 
						|
from os import path
 | 
						|
import re
 | 
						|
import bisect
 | 
						|
from subprocess import check_output
 | 
						|
import sys
 | 
						|
 | 
						|
#arm-none-eabi-nm -nl <elf file>
 | 
						|
_NM_EXEC = "arm-none-eabi-nm"
 | 
						|
_OPT = "-nlC"
 | 
						|
_PTN = re.compile("([0-9a-f]*) ([Tt]) ([^\t\n]*)(?:\t(.*):([0-9]*))?")
 | 
						|
 | 
						|
class ElfHelper(object):
 | 
						|
    def __init__(self, elf_file, map_file):
 | 
						|
    
 | 
						|
        op = check_output([_NM_EXEC, _OPT, elf_file.name])
 | 
						|
        self.maplines = map_file.readlines()
 | 
						|
        self.matches = _PTN.findall(op)
 | 
						|
        self.addrs = [int(x[0], 16) for x in self.matches]
 | 
						|
                
 | 
						|
    def function_addrs(self):
 | 
						|
        return self.addrs
 | 
						|
    
 | 
						|
    def function_name_for_addr(self, addr):
 | 
						|
        i = bisect.bisect_right(self.addrs, addr)
 | 
						|
        funcname = self.matches[i-1][2]
 | 
						|
        return funcname
 | 
						|
 | 
						|
def print_HFSR_info(hfsr):
 | 
						|
    if int(hfsr, 16) & 0x80000000:
 | 
						|
        print("\t\tDebug Event Occurred")
 | 
						|
    if int(hfsr, 16) & 0x40000000:
 | 
						|
        print("\t\tForced exception, a fault with configurable priority has been escalated to HardFault")    
 | 
						|
    if int(hfsr, 16) & 0x2:
 | 
						|
        print("\t\tVector table read fault has occurred")        
 | 
						|
 | 
						|
def print_MMFSR_info(mmfsr, mmfar):            
 | 
						|
    if int(mmfsr, 16) & 0x20:
 | 
						|
        print("\t\tA MemManage fault occurred during FP lazy state preservation")
 | 
						|
    if int(mmfsr, 16) & 0x10:
 | 
						|
        print("\t\tA derived MemManage fault occurred on exception entry")            
 | 
						|
    if int(mmfsr, 16) & 0x8:
 | 
						|
        print("\t\tA derived MemManage fault occurred on exception return")                
 | 
						|
    if int(mmfsr, 16) & 0x2:
 | 
						|
        if int(mmfsr, 16) & 0x80:
 | 
						|
            print("\t\tData access violation. Faulting address: %s"%(str(mmfar)))                    
 | 
						|
        else:     
 | 
						|
            print("\t\tData access violation. WARNING: Fault address in MMFAR is NOT valid")                    
 | 
						|
    if int(mmfsr, 16) & 0x1:
 | 
						|
        print("\t\tMPU or Execute Never (XN) default memory map access violation on an instruction fetch has occurred")                        
 | 
						|
    
 | 
						|
def print_BFSR_info(bfsr, bfar):
 | 
						|
    if int(bfsr, 16) & 0x20:
 | 
						|
        print("\t\tA bus fault occurred during FP lazy state preservation")
 | 
						|
    if int(bfsr, 16) & 0x10:
 | 
						|
        print("\t\tA derived bus fault has occurred on exception entry")  
 | 
						|
    if int(bfsr, 16) & 0x8:
 | 
						|
        print("\t\tA derived bus fault has occurred on exception return") 
 | 
						|
    if int(bfsr, 16) & 0x4:
 | 
						|
        print("\t\tImprecise data access error has occurred")               
 | 
						|
    if int(bfsr, 16) & 0x2:
 | 
						|
        if int(bfsr,16) & 0x80:
 | 
						|
            print("\t\tA precise data access error has occurred. Faulting address: %s"%(str(bfar)))                    
 | 
						|
        else:     
 | 
						|
            print("\t\tA precise data access error has occurred. WARNING: Fault address in BFAR is NOT valid")             
 | 
						|
    if int(bfsr, 16) & 0x1:
 | 
						|
        print("\t\tA bus fault on an instruction prefetch has occurred")                           
 | 
						|
 | 
						|
def print_UFSR_info(ufsr):            
 | 
						|
    if int(ufsr, 16) & 0x200:
 | 
						|
        print("\t\tDivide by zero error has occurred")
 | 
						|
    if int(ufsr, 16) & 0x100:
 | 
						|
        print("\t\tUnaligned access error has occurred")
 | 
						|
    if int(ufsr, 16) & 0x8:
 | 
						|
        print("\t\tA coprocessor access error has occurred. This shows that the coprocessor is disabled or not present")
 | 
						|
    if int(ufsr, 16) & 0x4:
 | 
						|
        print("\t\tAn integrity check error has occurred on EXC_RETURN")
 | 
						|
    if int(ufsr, 16) & 0x2:
 | 
						|
        print("\t\tInstruction executed with invalid EPSR.T or EPSR.IT field( This may be caused by Thumb bit not being set in branching instruction )")    
 | 
						|
    if int(ufsr, 16) & 0x1:
 | 
						|
        print("\t\tThe processor has attempted to execute an undefined instruction")        
 | 
						|
    
 | 
						|
def print_CPUID_info(cpuid):            
 | 
						|
    if (int(cpuid, 16) & 0xF0000) == 0xC0000:
 | 
						|
        print("\t\tProcessor Arch: ARM-V6M")
 | 
						|
    else:        
 | 
						|
        print("\t\tProcessor Arch: ARM-V7M or above")
 | 
						|
    
 | 
						|
    print("\t\tProcessor Variant: %X" % ((int(cpuid,16) & 0xFFF0 ) >> 4))    
 | 
						|
 | 
						|
def parse_line_for_register(line):
 | 
						|
    _, register_val = line.split(":")
 | 
						|
    return register_val.strip()    
 | 
						|
 | 
						|
def main(crash_log, elfhelper):
 | 
						|
    mmfar_val = 0
 | 
						|
    bfar_val = 0
 | 
						|
    lines = iter(crash_log.read().splitlines())
 | 
						|
    
 | 
						|
    for eachline in lines:
 | 
						|
        if "++ MbedOS Fault Handler ++" in eachline:
 | 
						|
            break
 | 
						|
    else:
 | 
						|
        print("ERROR: Unable to find \"MbedOS Fault Handler\" header")
 | 
						|
        return
 | 
						|
    
 | 
						|
    for eachline in lines:
 | 
						|
        if "-- MbedOS Fault Handler --" in eachline:
 | 
						|
            break
 | 
						|
        
 | 
						|
        elif eachline.startswith("PC"):
 | 
						|
            pc_val = parse_line_for_register(eachline)
 | 
						|
            if elfhelper:
 | 
						|
                pc_name = elfhelper.function_name_for_addr(int(pc_val, 16))
 | 
						|
            else:
 | 
						|
                pc_name = "<unknown-symbol>"
 | 
						|
                        
 | 
						|
        elif eachline.startswith("LR"):
 | 
						|
            lr_val = parse_line_for_register(eachline)
 | 
						|
            if elfhelper:
 | 
						|
                lr_name = elfhelper.function_name_for_addr(int(lr_val, 16))
 | 
						|
            else:
 | 
						|
                lr_name = "<unknown-symbol>"
 | 
						|
            
 | 
						|
        elif eachline.startswith("SP"):
 | 
						|
            sp_val = parse_line_for_register(eachline)
 | 
						|
                        
 | 
						|
        elif eachline.startswith("HFSR"):
 | 
						|
            hfsr_val = parse_line_for_register(eachline)
 | 
						|
            
 | 
						|
        elif eachline.startswith("MMFSR"):
 | 
						|
            mmfsr_val = parse_line_for_register(eachline)
 | 
						|
            
 | 
						|
        elif eachline.startswith("BFSR"):
 | 
						|
            bfsr_val = parse_line_for_register(eachline)
 | 
						|
            
 | 
						|
        elif eachline.startswith("UFSR"):
 | 
						|
            ufsr_val = parse_line_for_register(eachline)
 | 
						|
            
 | 
						|
        elif eachline.startswith("CPUID"):
 | 
						|
            cpuid_val = parse_line_for_register(eachline)
 | 
						|
            
 | 
						|
        elif eachline.startswith("MMFAR"):
 | 
						|
            mmfar_val = parse_line_for_register(eachline)
 | 
						|
            
 | 
						|
        elif eachline.startswith("BFAR"):
 | 
						|
            bfar_val = parse_line_for_register(eachline)    
 | 
						|
    
 | 
						|
    print("\nCrash Info:")        
 | 
						|
    print("\tCrash location = %s [0x%s] (based on PC value)" % (pc_name.strip(), str(pc_val)))
 | 
						|
    print("\tCaller location = %s [0x%s] (based on LR value)" % (lr_name.strip(), str(lr_val)))        
 | 
						|
    print("\tStack Pointer at the time of crash = [%s]" % (str(sp_val)))
 | 
						|
    
 | 
						|
    print("\tTarget and Fault Info:")
 | 
						|
    print_CPUID_info(cpuid_val)
 | 
						|
    print_HFSR_info(hfsr_val)
 | 
						|
    print_MMFSR_info(mmfsr_val, mmfar_val)
 | 
						|
    print_BFSR_info(bfsr_val, bfar_val)
 | 
						|
    print_UFSR_info(ufsr_val)
 | 
						|
        
 | 
						|
                            
 | 
						|
if __name__ == '__main__':
 | 
						|
    import argparse
 | 
						|
    
 | 
						|
    parser = argparse.ArgumentParser(description='Analyse mbed-os crash log. This tool requires arm-gcc binary utilities to be available in current path as it uses \'nm\' command')
 | 
						|
    # specify arguments
 | 
						|
    parser.add_argument(metavar='CRASH LOG', type=argparse.FileType('rb', 0),
 | 
						|
                        dest='crashlog',help='path to crash log file')      
 | 
						|
    parser.add_argument(metavar='ELF FILE', type=argparse.FileType('rb', 0),
 | 
						|
                        nargs='?',const=None,dest='elffile',help='path to elf file')             
 | 
						|
    parser.add_argument(metavar='MAP FILE', type=argparse.FileType('rb', 0),
 | 
						|
                        nargs='?',const=None,dest='mapfile',help='path to map file')                                    
 | 
						|
 | 
						|
    # get and validate arguments
 | 
						|
    args = parser.parse_args()
 | 
						|
 | 
						|
    # if both the ELF and MAP files are present, the addresses can be converted to symbol names
 | 
						|
    if args.elffile and args.mapfile:
 | 
						|
        elfhelper = ElfHelper(args.elffile, args.mapfile)
 | 
						|
    else:
 | 
						|
        print("ELF or MAP file missing, logging raw values.")
 | 
						|
        elfhelper = None
 | 
						|
    
 | 
						|
    # parse input and write to output
 | 
						|
    main(args.crashlog, elfhelper)
 | 
						|
    
 | 
						|
    #close all files
 | 
						|
    if args.elffile:
 | 
						|
        args.elffile.close()
 | 
						|
    if args.mapfile:
 | 
						|
        args.mapfile.close()
 | 
						|
    args.crashlog.close()
 | 
						|
 |