How to generate crash backtrace just from your serial crash log, without coredump file.

If your crash log have dump maps, user stacks, and you have symbols too,
You can try to get a backtrace via below python scripts.

[2-11624.8825] PC, LR MEMINFO
[2-11624.8825] --------------------------------------------------------------------------------------
[2-11624.8825] PC:46cbfc92, LR:46cbfc59
[2-11624.8825] --------------------------------------------------------------------------------------
[2-11624.8825] PC meminfo (0x46cc005d to 0x46cc0092)
[2-11624.8825] 0040:                                                                               
[2-11624.8825] 0060: be07e9cd 801cf8d0 f04fb085 f1010901 ea4f061b e0015707 d03142b4 f85446a3
[2-11624.8825] 0080: f0055b04 2b010303 ea4fd1f6 f8d85a15 ea4f3a14
[2-11624.8825] --------------------------------------------------------------------------------------
[2-11624.8825] LR meminfo (0x46cbf459 to 0x46cc0059)
[2-11624.8825] f440:                                                                0205ea02
[2-11624.8825] f460: bf0442a2 f8832201 d009202c f84269da 6a191020 f1016a9a ea020101 621a0201
[2-11624.8825] f480: e9ddb002 b0024500 bf004770 8000f3af 4506e96d 5413ea4f e9cd460d ea4f8e04
......
[2-11624.8831] * dump maps on pid (2548 - efl_webprocess)
[2-11624.8831] -----------------------------------------------------------
[2-11624.8831] 00010000-00011000 r-xp 00000000 b3:12 55716      /usr/lib/chromium-efl/efl_webprocess
[2-11624.8831] 00020000-00021000 r--p 00000000 b3:12 55716      /usr/lib/chromium-efl/efl_webprocess
[2-11624.8831] 00021000-00022000 rw-p 00000000 00:00 0         
[2-11624.8831] 00031000-00036000 rw-p 00001000 b3:12 55716      /usr/lib/chromium-efl/efl_webprocess
[2-11624.8831] 00036000-0007c000 rw-p 00000000 00:00 0          [heap]
[2-11624.8831] 0007c000-008ae000 rw-p 00000000 00:00 0          [heap]

[2-11624.8917] -----------------------------------------------------------
[2-11624.8917] * dump user stack
[2-11624.8917] -----------------------------------------------------------
[2-11624.8917] pid(2548) stack vma (0xbe729000 ~ 0xbe74c000)
[2-11624.8917] User Stack: (0xbe749e20 to 0xbe745f10)
[2-11624.8917] -----------------------------------------------------------




Save below python scripts as btfs(chmod a+x btfs)
Usage: btfs [option] symbols serial
 backtrace from serial log
 The options are :
  -d use debug symbols
 Examples :
  btfs symbols dump_systemstate_20161220110158.log
  btfs -d symbols dump_systemstate_20161220110158.log



All related libs and bins that have debug info should placed in symbols folder like:
symbols$ ls
lib  usr

#!/usr/bin/env python
#Author errong.leng@gmail.com

import commands
import argparse
import glob
import os
import shlex
import subprocess
import sys
import time
import re

STATE_NONE = 0
STATE_PC = 1
STATE_LR = 2
STATE_DUMP_MAPS = 3
STATE_DUMP_MAPS_BEGIN = 4
STATE_DUMP_MAPS_END = 5
STATE_USERSTACK = 6
STATE_USERSTACK_BEGIN = 7
STATE_USERSTACK_END = 8

def hexstr2int(s):
    r = 0
    i = 0
    if (len(s) >= 2 and s[0] == '0' and (s[1] == 'x' or s[1] == 'X')):
        i = 2
    while i < len(s):
        if (s[i] == '\0' or s[i] == '\n' or s[i] == '\r'):
            break
        if (s[i] >= 'a'):
            r = ord(s[i]) - ord('a') + 10 + r*16;
        elif (s[i] >= 'A'):
            r = ord(s[i]) - ord('A') + 10 + r*16;
        else:
            r = ord(s[i]) - ord('0') + r*16;
        i = i + 1
    return r

def ishexstr(s):
    i = 0
    while i < len(s):
        if (s[i] == '\0' or s[i] == '\n' or s[i] == '\r'):
            break
        if ((s[i] >= '0' and s[i] <= '9') or
           (s[i] >= 'a' and s[i] <= 'f') or
           (s[i] >= 'A' and s[i] <= 'F') or
           s[i] == 'x' or
           s[i] == 'X'):
           i = i + 1
        else:
            return 0
    return 1

class AdressSeg:
    def __init__(self, begin, end):
        self.begin = begin
        self.end = end
    def begin(self):
        return self.begin
    def end(self):
        return self.end

class AdressSegList:
    def __init__(self):
        self.adlist = []
        self.begin = -1
    def append(self, begin, end):
        self.adlist.append(AdressSeg(begin, end))
        if (self.begin == -1):
            self.begin = begin
        self.end = end
    def libbegin(self):
        return self.begin
    def libend(self):
        return self.end

class Meminfo:
    def __init__(self, adressseg):
        self.addressseg = adressseg
        self.mem_list = []
    def begin(self):
        return self.addressseg.begin
    def end(self):
        return self.end

class AdressSegList:
    def __init__(self):
        self.adlist = []
        self.begin = -1
    def append(self, begin, end):
        self.adlist.append(AdressSeg(begin, end))
        if (self.begin == -1):
            self.begin = begin
        self.end = end
    def libbegin(self):
        return self.begin
    def libend(self):
        return self.end
  
class Meminfo:
    def __init__(self, adressseg):
        self.addressseg = adressseg
        self.mem_list = []
    def begin(self):
        return self.addressseg.begin
    def end(self):
        return self.addressseg.end
    def append_memory(self, memory):
        self.mem_list.append(memory)

class MapInfo:
    def __init__(self, address_seg, exe_lib_path):
        self.address_seg = address_seg;
        self.exe_lib_path = exe_lib_path

class MapList:
    def __init__(self):
        self.exe_libs = {}
        self.filecheck = {}
        self.btcount = 0
    def append_map(self, begin, end, libpath):
        if (self.exe_libs.has_key(libpath)):
            self.exe_libs[libpath].append(begin, end)
        else:
            adlist = AdressSegList()
            adlist.append(begin, end)
            self.exe_libs[libpath] = adlist
    def map_mem_info(self, meminfo, symbol_path, debug):
        j = 0
        while j < len(meminfo.mem_list):
            memory = meminfo.mem_list[j]
            j = j + 1
            self.map_memory(memory, symbol_path, debug)
    def map_memory_pc(self, memory, symbol_path, debug):
        ret = self.map_memory(memory, symbol_path, debug)
        if (ret == 0):
            print "#%-2d %s in ?? " % (self.btcount, memory)
            self.btcount = self.btcount + 1
    def map_memory(self, memory, symbol_path, debug):
        if (not ishexstr(memory)):
            return 0
        pc = hexstr2int(memory) - 1
        libs = self.exe_libs.keys()
        i = 0
        while i < len(libs):
            libpath = libs[i]
            i = i + 1
            real_libpath = symbol_path + libpath
            debug_libpath = symbol_path + "/usr/lib/debug" + libpath + ".debug"
            if (not (os.path.exists(real_libpath))):
                del self.exe_libs[libpath]
                continue
            if (not self.filecheck.has_key(libpath)):
                command_line = "file " + real_libpath
                (status, output) = commands.getstatusoutput(command_line)
                if (status != 0):
                    del self.exe_libs[libpath]
                    continue
                if (not (re.search(r'ELF 32-bit LSB  shared object', output) or
                    re.search(r'ELF 32-bit LSB  executable', output))):
                    del self.exe_libs[libpath]
                    continue
                self.filecheck[libpath] = 1
            adlist = self.exe_libs[libpath]
            libbegin = hexstr2int(adlist.libbegin())
            libend = hexstr2int(adlist.libend())
            if (pc < libbegin or pc > libend):
                continue
            address = pc - libbegin
            hexaddress = hex(address)
            option = " -afC "
            real_debug = 0
            if debug == 1:
                if (os.path.exists(debug_libpath)):
                    real_debug = 1
                    real_libpath = debug_libpath
                    option = " -afiC "
            command_line = "addr2line -e " + real_libpath + option + hexaddress
            (status, output) = commands.getstatusoutput(command_line)
            if (status == 0):
                funcs = output.split('\n')
                if (re.search(r'\$d', funcs[1], re.I) or
                    re.search(r'\$t', funcs[1], re.I)):
                    return 1
                if real_debug == 1:
                    print "#%-2d %s in %s" % (self.btcount, hex(pc), funcs[len(funcs)-2])
                    print " at " + funcs[len(funcs)-1]
                else:
                    print "#%-2d %s in %s" % (self.btcount, hex(pc), funcs[1])
                    print " at " + libpath
                self.btcount = self.btcount + 1
            return 1
        return 0

class CoreDumpInfo:
    def __init__(self, io_state, debug):
        self.io_state = io_state
        self.debug = debug
        self.maps = MapList()
    def set_state(self, io_state):
        self.io_state = io_state
    def set_name(self, name):
        self.name = name
    def set_pc(self, pc):
        self.pc = pc
    def set_lr(self, lr):
        self.lr = lr
    def set_usr_stack_meminfo(self, meminfo):
        self.urs_meminfo = meminfo
    def mem_end(self):
        return self.urs_meminfo.end()
    def mem_begin(self):
        return self.urs_meminfo.begin()
    def set_mem_end(self):
        self.io_state = STATE_USERSTACK_END
    def append_memory(self, memory):
        self.urs_meminfo.append_memory(memory)
    def append_map(self, begin, end, libpath):
        i = len(libpath) - 1
        while i >= 0:
            if (libpath[i] == '\0' or
                libpath[i] == '\n' or
                libpath[i] == ' ' or
                libpath[i] == '\r'):
                i = i - 1
            else:
                break
        path = libpath[:i+1]
        self.maps.append_map(begin, end, path)
    def backtrace(self, symbol_path):
        print "bt full for " + self.name
        self.maps.map_memory_pc(self.pc, symbol_path, self.debug)
        self.maps.map_memory(self.lr, symbol_path, self.debug)
        self.maps.map_mem_info(self.urs_meminfo, symbol_path, self.debug)

def DoReadPC(line, core_dump_info):
    pcobj = re.search(r'PC is at (.*)', line)
    if (pcobj):
        core_dump_info.set_state(STATE_PC)
        core_dump_info.set_pc(pcobj.group(1))

def DoReadLR(line, core_dump_info):
    lrobj = re.search(r'LR is at (.*)', line)
    if (lrobj):
        core_dump_info.set_state(STATE_LR)
        core_dump_info.set_lr(lrobj.group(1))

def DoReadUserStackList(line, core_dump_info):
    if (re.search(r'----------------------------', line)):
        core_dump_info.set_mem_end()
        return
    mfobj = re.search(r' ([A-Fa-f0-9]{4}): (.*)', line)
    if (not mfobj):
        return
    memory_begin = mfobj.group(1)
    mems = mfobj.group(2).split(' ')
    end = core_dump_info.mem_end()
    b4 = core_dump_info.mem_begin()[0:4]
    e4 = end[0:4]
    if ((hexstr2int(b4 + memory_begin) + 32) >= hexstr2int(end) or
        (hexstr2int(e4 + memory_begin) + 32) >= hexstr2int(end)):
        core_dump_info.set_mem_end(flag)
    i = 0
    while i < len(mems):
        memory = mems[i][:8]
        i = i+1
        if (re.search(r'[A-Fa-f0-9]{8}', memory)):
            core_dump_info.append_memory(memory)

def DoReadDumpMaps(line, core_dump_info):
    dmobj = re.search(r'dump maps on pid \((.*)\)', line)
    if (dmobj):
        core_dump_info.set_name(dmobj.group(1))
        core_dump_info.set_state(STATE_DUMP_MAPS)

def DoReadMaps(line, core_dump_info):
    if (re.search(r'task stack info', line)):
        core_dump_info.set_state(STATE_DUMP_MAPS_END)
        return
    mapobj = re.search(r'([A-Fa-z0-9]{8})-([A-Fa-z0-9]{8}).*(\/usr\/.*)', line)
    if (not mapobj):
        return
    core_dump_info.append_map(mapobj.group(1), mapobj.group(2), mapobj.group(3))

def DoReadUserStack(line, core_dump_info):
    usobj = re.search(r'User Stack: \((.*) to (.*)\)', line)
    if (usobj):
        core_dump_info.set_state(STATE_USERSTACK_BEGIN)
        core_dump_info.set_usr_stack_meminfo(Meminfo(AdressSeg(usobj.group(1), usobj.group(2))))

def read_line(line, core_dump_info):
    io_state = core_dump_info.io_state
    if (io_state < STATE_PC):
        DoReadPC(line, core_dump_info)
    elif (io_state < STATE_LR):
        DoReadLR(line, core_dump_info)
    elif (io_state < STATE_DUMP_MAPS):
        DoReadDumpMaps(line, core_dump_info)
    elif (io_state == STATE_DUMP_MAPS):
        core_dump_info.io_state = STATE_DUMP_MAPS_BEGIN
    elif (io_state < STATE_DUMP_MAPS_END):
        DoReadMaps(line, core_dump_info)
    elif (io_state < STATE_USERSTACK):
        DoReadUserStack(line, core_dump_info)
    elif (io_state < STATE_USERSTACK_END):
        DoReadUserStackList(line, core_dump_info)

def print_help():
    print "Usage: btfs [option] symbols serial"
    print " backtrace from serial log "
    print " The options are : "
    print "  -d use debug symbols"
    print " Examples : "
    print "  btfs symbols dump_systemstate_20161220110158.log"
    print "  btfs -d symbols dump_systemstate_20161220110158.log"

def gen_coredump(logfile, symbols, use_debug, begin_line, line_count):
    lastline = int(line_count) - int(begin_line) + 6
    core_dump = "/tmp/coredump_" + str(time.time())
    command_line = "tail -n " + str(lastline) + " " + logfile + " > " + core_dump
    os.system(command_line)
    core_dump_info = CoreDumpInfo(STATE_NONE, use_debug)
    for line in open(core_dump):
        if (core_dump_info.io_state == STATE_USERSTACK_END):
            break;
        read_line(line, core_dump_info)
    if (core_dump_info.io_state >= STATE_USERSTACK):
        core_dump_info.backtrace(symbols)
    elif (core_dump_info.io_state >= STATE_DUMP_MAPS):
        print "User Stack not found"
    elif (core_dump_info.io_state >= STATE_LR):
        print "dump maps not found"
    elif (core_dump_info.io_state >= STATE_PC):
        print "LR is at not found"
    elif (core_dump_info.io_state >= STATE_NONE):
        print "PC is at not found"
    command_line = "rm -f " + core_dump
    os.system(command_line)

def isdigital(s):
    l = len(s)
    i = 0
    while (i < l and s[i] != '\0' and s[i] != '\n' and s[i] != '\r'):
        if (s[i] >= '0' and s[i] <= '9'):
            i = i + 1
            continue
        else:
            return 0
    return 1

def main_function():
    if (len(sys.argv) < 3 or len(sys.argv) > 4):
        print_help()
        return
    i = 1
    use_debug = 0
    if len(sys.argv) >= 4:
        if (len(sys.argv[1]) < 2 or sys.argv[1][0] != '-' or sys.argv[1][1] != 'd'):
            print_help()
            return
        use_debug = 1
    symbol_path = sys.argv[len(sys.argv)-2]
    if (not os.path.exists(symbol_path)):
        symbol_path = os.path.abspath(os.curdir) + "/" + symbol_path
        if (not os.path.exists(symbol_path)):
            print symbol_path + " is not existed\n"
            return
    log_file = sys.argv[len(sys.argv)-1]
    if (not os.path.exists(log_file)):
        log_file = os.path.abspath(os.curdir) + "/" + log_file
        if (not os.path.exists(log_file)):
            print log_file + " is not existed\n"
            return
    command_line = "wc -l "+log_file+"|awk '{print $1}'"
    (status, line_count) = commands.getstatusoutput(command_line)
    if status:
        print command_line + " return " + status
        return
    print "log file total line count : %s" % line_count
    command_line = "cat " + log_file + "|grep -an 'PC is at'" + "|awk 'BEGIN{FS=\":\"} {print $1}'"
    (status, core_dump_begin_line) = commands.getstatusoutput(command_line)
    if status:
        print command_line + " return " + status
        return
    print "line number of which include 'PC is at' : \n%s" % core_dump_begin_line
    begin_lines = core_dump_begin_line.split('\n')
    if (len(begin_lines) <= 0):
        if (isdigital(begin_lines)):
            gen_coredump(log_file, symbol_path, use_debug, core_dump_begin_line, line_count)
    else:
        print "%d coredump founded" % len(begin_lines)
        index = 0
        for begin_line in begin_lines:
            index = index + 1
            if (isdigital(begin_line)):
                gen_coredump(log_file, symbol_path, use_debug, begin_line, line_count)
                if (index < len(begin_lines)):
                    print "\n"
                    print "\n"


if __name__ == '__main__':
    main_function()

Comments

Popular posts from this blog

How to fix error : no module named sendgrid when try to use sendgrid python lib in PHP.

react-native run-android : sun.security.provider.cert path.SunCertPathBuilderException : unable to find valid certification path to req uested target

react-native run-android : do not build/update modified code(App.js)