matrix
matrix copied to clipboard
贡献一个支持 symbolicatecrash 的 ks2apple.py 转换脚本
有以下的一些要点
- 添加固定的
Report Version
避免报错No crash report version in .json
-
System Info
之前添加必要解析的信息头 - Binary Images 主 image 添加 arch 字段(其他的 image 不能添加 arch 字段,因为 matrix 里面记录的其他 image arch 解析出来是 arm64,实际上应该是 arm64e, 会报如下错误
atos cannot load symbols for the file /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport/14.7.1 (18G82) arm64e/Symbols/System/Library/PrivateFrameworks/AudioSession.framework/AudioSession for architecture arm64.
, 所以只要主 image 添加 arch 字段就可以了)
接下来是自己改的脚本
#!/usr/bin/env python2.7
#-*- coding: utf-8 -*-
'''
@Author: mattzheng
@since: 2019-05-13 11:46:33
@lastTime: 2019-07-01 18:06:08
@LastAuthor: Do not edit
'''
from curses.ascii import isdigit
import json
import os
import sys
import cgi
import html
import optparse
import traceback
from datetime import datetime
# reload(sys)
# sys.setdefaultencoding('utf-8')
device_map = {
'iPod1,1' : 'iPod touch 1G',
'iPod2,1' : 'iPod touch 2G',
'iPod3,1' : 'iPod touch 3G',
'iPod4,1' : 'iPod touch 4G',
'iPod5,1' : 'iPod touch 5G',
'iPad2,5' : 'iPad mini 1G',
'iPad2,6' : 'iPad mini 1G',
'iPad2,7' : 'iPad mini 1G',
'iPad4,4' : 'iPad mini 2G',
'iPad4,5' : 'iPad mini 2G',
'iPad4,6' : 'iPad mini 2G',
'iPad4,7' : 'iPad mini 3',
'iPad4,8' : 'iPad mini 3',
'iPad4,9' : 'iPad mini 3',
'iPad5,1' : 'iPad mini 4',
'iPad5,2' : 'iPad mini 4',
'iPad1,1' : 'iPad 1G',
'iPad2,1' : 'iPad 2',
'iPad2,2' : 'iPad 2',
'iPad2,3' : 'iPad 2',
'iPad2,4' : 'iPad 2',
'iPad3,1' : 'iPad 3',
'iPad3,2' : 'iPad 3',
'iPad3,3' : 'iPad 3',
'iPad3,4' : 'iPad 4',
'iPad3,5' : 'iPad 4',
'iPad3,6' : 'iPad 4',
'iPad4,1' : 'iPad Air',
'iPad4,2' : 'iPad Air',
'iPad4,3' : 'iPad Air',
'iPad5,3' : 'iPad Air 2',
'iPad5,4' : 'iPad Air 2',
'iPad6,3' : 'iPad Pro 9.7-inch',
'iPad6,4' : 'iPad Pro 9.7-inch',
'iPad6,7' : 'iPad Pro 12.9-inch',
'iPad6,8' : 'iPad Pro 12.9-inch',
'iPad6,11' : 'iPad 5',
'iPad6,12' : 'iPad 5',
'iPad7,11' : 'iPad 6',
'iPad7,12' : 'iPad 6',
'iPad7,1' : 'iPad Pro 12.9-inch 2',
'iPad7,2' : 'iPad Pro 12.9-inch 2',
'iPad7,3' : 'iPad Pro 10.5-inch',
'iPhone1,1' : 'iPhone',
'iPhone1,2' : 'iPhone 3G',
'iPhone2,1' : 'iPhone 3GS',
'iPhone3,1' : 'iPhone 4',
'iPhone3,2' : 'iPhone 4',
'iPhone3,3' : 'iPhone 4',
'iPhone4,1' : 'iPhone 4s',
'iPhone5,1' : 'iPhone 5',
'iPhone5,2' : 'iPhone 5',
'iPhone5,3' : 'iPhone 5c',
'iPhone5,4' : 'iPhone 5c',
'iPhone6,1' : 'iPhone 5s',
'iPhone6,2' : 'iPhone 5s',
'iPhone7,1' : 'iPhone 6 Plus',
'iPhone7,2' : 'iPhone 6',
'iPhone8,1' : 'iPhone 6s',
'iPhone8,2' : 'iPhone 6s Plus',
'iPhone8,4' : 'iPhone SE',
'iPhone9,1' : 'iPhone 7',
'iPhone9,3' : 'iPhone 7',
'iPhone9,2' : 'iPhone 7 Plus',
'iPhone9,4' : 'iPhone 7 Plus',
'iPhone10,1' : 'iPhone 8',
'iPhone10,4' : 'iPhone 8',
'iPhone10,2' : 'iPhone 8 Plus',
'iPhone10,5' : 'iPhone 8 Plus',
'iPhone10,3' : 'iPhone X',
'iPhone10,6' : 'iPhone X',
'iPhone11,2' : 'iPhone XS',
'iPhone11,4' : 'iPhone XS Max',
'iPhone11,6' : 'iPhone XS Max',
'iPhone11,8' : 'iPhone XR',
'iPhone12,1' : 'iPhone 11',
'iPhone12,3' : 'iPhone 11 Pro',
'iPhone12,5' : 'iPhone 11 Pro Max',
'iPhone13,1' : 'iPhone 12 mini',
'iPhone13,2' : 'iPhone 12',
'iPhone13,3' : 'iPhone 12 Pro',
'iPhone13,4' : 'iPhone 12 Pro Max',
}
def dump_json(obj):
return json.dumps(obj, indent=4,
sort_keys=True, separators=(',',': '))
def get_system_info(report):
'''Get system information from report'''
return report.get('system', None)
def get_report_info(report):
'''Get report information from report'''
return report.get('report', None)
def get_crash_info(report):
'''Get crash information from report'''
return report.get('crash', None)
def get_cpu_type(arch):
return {'arm64': 'ARM-64',
'arm': 'ARM',
'x86': 'X86',
'x86_64': 'X86_64'}.get(arch, 'Unknow')
def get_cpu_arch(report):
system = get_system_info(report)
arch = system.get('cpu_arch', '')
return get_cpu_type(arch)
def get_system_info_by_key(report, key):
system = get_system_info(report)
return system.get(key, 'unknown')
def get_app_name(report):
return get_system_info_by_key(report, 'CFBundleExecutable')
def get_bundle_name(report):
return get_system_info_by_key(report, 'CFBundleName')
def get_app_version(report):
return get_system_info_by_key(report, 'CFBundleShortVersionString')
def get_app_uuid(report):
return get_system_info_by_key(report, 'app_uuid').lower()
def get_build_version(report):
return get_system_info_by_key(report, 'CFBundleVersion')
def get_bundle_id(report):
return get_system_info_by_key(report, 'CFBundleIdentifier')
def get_machine(report):
return get_system_info_by_key(report, 'machine')
def get_process_id(report):
return get_system_info_by_key(report, 'process_id')
def get_parent_process_id(report):
return get_system_info_by_key(report, 'parent_process_id')
def get_executable_path(report):
return get_system_info_by_key(report, 'CFBundleExecutablePath')
def get_build_type(report):
return get_system_info_by_key(report, 'build_type')
def get_build_type(report):
return get_system_info_by_key(report, 'build_type')
def get_report_id(report):
report_info = get_report_info(report)
return report_info.get('id', 'unknown')
def get_apple_os_version(report):
# "system_version": "14.7.1"
system_version = get_system_info_by_key(report, 'system_version')
# "os_version": "18G82"
os_version = get_system_info_by_key(report, 'os_version')
# "machine": "iPhone13,2"
machine = get_system_info_by_key(report, 'machine')
machine = ''.join([i for i in machine if not i.isdigit() and i != ','])
return '{0} OS {1} ({2})'.format(machine, system_version, os_version)
def get_binary_img_info(report):
# key: name, value: uuid
img_info = {}
images = report.get('binary_images', [])
for image in images:
name = os.path.basename(image["name"])
uuid = image['uuid'].lower().replace('-', '')
if name not in img_info:
img_info[str(name)] = str(uuid)
return img_info
def get_belong_img(report, addr):
images = report.get('binary_images', [])
for img in images:
if img['image_addr'] <= addr <= (img['image_addr'] + img['image_size']):
return img
return None
CPU_TYPE_ARM = 12
CPU_ARCH_ABI64 = 0x01000000
CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64
CPU_TYPE_X86 = 7
CPU_TYPE_X86_64 = CPU_TYPE_X86 | CPU_ARCH_ABI64
CPU_SUBTYPE_ARM_V6 = 6
CPU_SUBTYPE_ARM_V7 = 9
CPU_SUBTYPE_ARM_V7F = 10
CPU_SUBTYPE_ARM_V7S = 11
CPU_SUBTYPE_ARM_V7K = 12
CPU_SUBTYPE_ARM_V6M = 14
CPU_SUBTYPE_ARM_V7M = 15
CPU_SUBTYPE_ARM_V7EM = 16
CPU_SUBTYPE_ARM_V8 = 13
CPU_ARM_TYPES = {
CPU_SUBTYPE_ARM_V6: 'armv6',
CPU_SUBTYPE_ARM_V7: 'armv7',
CPU_SUBTYPE_ARM_V7F: 'armv7f',
CPU_SUBTYPE_ARM_V7S: 'armv7s',
CPU_SUBTYPE_ARM_V7K: 'armv7k',
CPU_SUBTYPE_ARM_V6M: 'armv6m',
CPU_SUBTYPE_ARM_V7M: 'armv7m',
CPU_SUBTYPE_ARM_V7EM: 'armv7em',
CPU_SUBTYPE_ARM_V8: 'armv8',
}
def get_detail_cpu_arch(major, minor):
if major == CPU_TYPE_ARM:
return CPU_ARM_TYPES.get(minor, 'arm')
elif major == CPU_TYPE_ARM64:
return 'arm64'
elif major == CPU_TYPE_X86:
return 'i386'
elif major == CPU_TYPE_X86_64:
return 'x86_64'
return 'unknown({0},{1})'.format(major, minor)
def get_last_exception(report):
report_info = get_report_info(report)
process = report_info.get('process', None)
if not process:
return None
return process.get('last_dealloced_nsexception', None)
def get_time(report):
'''Get crash time from report
@return time like "2015-12-28 17:48:03 +0800"
'''
info = get_report_info(report)
timestamp = info.get('timestamp', None)
if not timestamp:
return ''
if type(timestamp) == type(0):
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
else:
return timestamp
#from dateutil import parser, tz
#date = parser.parse(timestamp)
#return date.astimezone(tz.tzlocal())\
# .strftime('%Y-%m-%d %H:%M:%S.000 %z')
def get_crash_thread(report):
crash = get_crash_info(report)
if not crash:
return []
#print crash
threads = crash['threads']
for thread in threads:
crashed = thread.get('crashed', False)
if crashed:
return thread
return crash.get('crashed_thread', None)
def parse_user_info(report):
user_info = report.get("user", None)
if not user_info:
return []
result = ['\nUser Info: {']
if user_info and user_info.get(APP_NAME):
uin = user_info[APP_NAME].get("uin", None)
if uin:
result.append(show_color(' Uin: {0}'.format(uin)))
if user_info[APP_NAME].get("UsrName"):
result.append(' UsrName: {0}'.format(user_info[APP_NAME].get("UsrName")))
if user_info[APP_NAME].get("heavyUser", -1) != -1:
result.append(' heavyUser: {0}'.format(user_info[APP_NAME].get("heavyUser")))
if user_info[APP_NAME].get("heavyUserType", -1) != -1:
result.append(' heavyUserType: {0}'.format(user_info[APP_NAME].get("heavyUserType")))
if user_info[APP_NAME].get("heavyPoint", -1) != -1:
result.append(' heavyPoint: {0}'.format(user_info[APP_NAME].get("heavyPoint")))
if user_info[APP_NAME].get("DumpType", -1) != -1:
result.append(' dumpType: {0}'.format(user_info[APP_NAME].get("DumpType")))
if user_info[APP_NAME].get("blockTime"):
result.append(' blockTime: {0}'.format(user_info[APP_NAME].get("blockTime")))
if user_info[APP_NAME].get("LastScene"):
result.append(' LastScene: {0}'.format(user_info[APP_NAME].get("LastScene")))
if user_info[APP_NAME].get("SecondLastScene"):
result.append(' SecondLastScene: {0}'.format(user_info[APP_NAME].get("SecondLastScene")))
if user_info[APP_NAME].get("WeAppScene"):
result.append(' WeAppScene: {0}'.format(user_info[APP_NAME].get("WeAppScene")))
if user_info[APP_NAME].get("ExistWeAppCount"):
result.append(' ExistWeAppCount: {0}'.format(user_info[APP_NAME].get("ExistWeAppCount")))
if len(result) < 2:
result.append(json.dumps(user_info, indent=4))
result.append('}')
return result
def parse_crash_header(report):
'''
{
"app_name":"madao",
"timestamp":"2022-10-11 14:47:22.00 +0800",
"app_version":"1.5.1",
"slice_uuid":"c94bfabd-4f66-39c3-a07e-404fad856371",
"adam_id":0,
"build_version":"68",
"platform":2,
"bundleID":"com.abc.madao",
"share_with_app_devs":0,
"is_first_party":0,
"bug_type":"109",
"os_version":"iPhone OS 14.7.1 (18G82)",
"incident_id":"EF71B1C4-2FDC-477A-AB51-CCE8C9AA75F7",
"name":"madao"
}
Incident Identifier: EF71B1C4-2FDC-477A-AB51-CCE8C9AA75F7
CrashReporter Key: 1c17caa56daf4f3c06d736dadc5a4af1af50c3e8
Hardware Model: iPhone13,2
Process: madao [5573]
Path: /private/var/containers/Bundle/Application/54550E30-656A-4F36-AF7B-8C0CE69D9A18/madao.app/madao
Identifier: com.abc.madao
Version: 68 (1.5.1)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.abc.madao [415]
'''
info_dict = {
'app_name': get_app_name(report),
'timestamp': get_time(report),
'app_version': get_app_version(report),
'slice_uuid': get_app_uuid(report),
'build_version': get_build_version(report),
'bundleID': get_bundle_id(report),
'os_version': get_apple_os_version(report),
'incident_id': get_report_id(report),
'name': get_app_name(report)
}
headers = [json.dumps(info_dict)]
headers.append('Incident Identifier: {0}'.format(get_report_id(report)))
headers.append('Hardware Model: {0}'.format(get_machine(report)))
headers.append('Process: {0} [{1}]'.format(get_app_name(report), get_parent_process_id(report)))
headers.append('Path: {0}'.format(get_executable_path(report)))
headers.append('Identifier: {0}'.format(get_bundle_id(report)))
headers.append('Version: {0} ({1})'.format(get_build_version(report), get_app_version(report)))
headers.append('Parent Process: launchd [{0}]'.format(get_parent_process_id(report)))
headers.append('Coalition: {0} [415]'.format(get_bundle_id(report)))
return headers
def parse_system_info(report):
'''Parse system information from report.
@return header lines
'''
sys = get_system_info(report)
if not sys:
return []
info = get_report_info(report)
_s = lambda x: sys.get(x, '')
_i = lambda x: info.get(x, '')
headers = ['System Info: {']
device = _s('machine')
if device in device_map:
device = device_map[device]
headers.append(' Device: {0}'.format(device))
headers.append(' OS Version: {0} {1} ({2})'.format(_s('system_name'), _s('system_version'), _s('os_version')))
user_info = report.get("user", None)
if user_info and user_info.get('WeChat'):
jb_info = user_info.get('WeChat').get('Jailbreak', None)
if jb_info:
headers.append(' Jailbreak: {0}'.format(jb_info))
mem = sys.get("memory")
if mem:
fmt = lambda x: " Mem {0:6}: {1:4} M".format(x, int(mem[x])/1024/1024)
headers.append(fmt("usable"))
headers.append(show_color(fmt("free")))
headers.append(fmt("size"))
headers.append('}')
return headers
def show_color(str,color='blue'):
return str
# return '<em style="color:%s;">%s</em>'%(color,str)
def zombie_exception(report):
crash = get_crash_info(report)
error = crash['error']
mach = error['mach']
exc_name = mach.get('exception_name', '0')
code_name = mach.get('code_name', '0x00000000')
if exc_name != 'EXC_BAD_ACCESS' or code_name != 'KERN_INVALID_ADDRESS':
return False
last_exception = get_last_exception(report)
if not last_exception:
return False
last_addr = last_exception['address']
thread = get_crash_thread(report)
registers = thread['registers']['basic']
for reg, addr in registers:
if addr == last_addr:
return True
return False
def parse_error_info(report):
crash = get_crash_info(report)
if not crash:
return []
error = crash.get('error')
if not error:
print("waring: no error found in crash")
return []
mach = error['mach']
signal = error['signal']
exc_name = mach.get('exception_name', '0')
sig_name = signal.get('name', None)
if not sig_name:
sig_name = signal.get('signal', '')
code_name = mach.get('code_name', '0x00000000')
addr = error.get('address', '0')
crash_thread = 0
thread = get_crash_thread(report)
if thread:
crash_thread = thread['index']
result = ['']
result.append('Exception Type: {0} ({1})'.format(exc_name, sig_name))
result.append('Exception Codes: {0} at {1:016x}'.format(code_name, int(addr)))
result.append('Crashed Thread: {0}'.format(crash_thread))
result.append('OS Version: {0}'.format(get_apple_os_version(report)))
# 添加 Report Version, 随便填一个
result.append('Report Version: 104')
diagnosis = crash.get('diagnosis', None)
#print "fuck haha", diagnosis
if diagnosis:
result.append('\nCrashDoctor Diagnosis: {0}'.format(diagnosis))
#print result
return result
def parse_crash_reason(report):
result = ['']
reason_fmt = ("Application Specific Information"
"*** Terminating app due to uncaught exception '{0}', "
"reason: '{1}'")
fmt = lambda x, y: reason_fmt.format(x, y)
crash = get_crash_info(report)
if not crash:
return []
error = crash.get('error')
if not error:
print("warning: no error found in crash")
return []
crash_type = error['type']
user_exception = error.get('user_reported', None)
ns_exception = error.get('nsexception', None)
if ns_exception:
result.append(fmt(ns_exception['name'], error.get('reason', '')))
elif zombie_exception(report):
last_exception = get_last_exception(report)
if (last_exception):
result.append(fmt(last_exception['name'], last_exception['reason']))
elif user_exception:
result.append(fmt(user_exception['name'], error['reason']))
line = user_exception.get('line_of_code', None)
backtrace = user_exception.get('backtrace', [])
if line or backtrace:
result.append('Custom Backtrace:')
if line:
result.append('Line: {0}'.format(line))
for entry in backtrace:
result.append(entry)
elif crash_type == 'cpp_exception':
#cpp_exception = error['cppexception']
cpp_exception = error.get('cppexception', None)
if cpp_exception and cpp_exception.get('name'):
result.append(fmt(cpp_exception['name'], error['reason']))
elif crash_type == 'deadlock':
result.append('Application main thread deadlocked')
return result
def parse_backtrace(report, backtrace):
num = 0
result = []
for trace in backtrace.get('contents', []):
try:
pc = trace['instruction_addr']
except:
traceback.print_exc()
continue
img = get_belong_img(report, pc)
if not img:
print("error, no img found for pc: %s" % pc)
result.append('{0:<4}{1:31} 0x{2:016x}'.format(num, 'unknown', pc))
num += 1
continue
# uuid = img['uuid'].lower().replace('-', '')
obj_addr = img['image_addr']
offset = pc - obj_addr
obj_name = os.path.basename(img['name'])
symbol_name = trace.get('symbol_name', None)
# symbol_addr = trace.get('symbol_addr', 0)
preamble = '{0:<4}{1:31} 0x{2:016x}'.format(num, obj_name, pc)
unsymbolicated = '0x{0:04x} + {1}'.format(obj_addr, offset)
symbolicated = '(null)'
is_unsymbolicated = False
if symbol_name:
symbolicated = '{0}'.format(symbol_name)
#symbolicated = '{0} + {1}'.format(symbol_name, pc-symbol_addr)
else:
is_unsymbolicated = True
if symbol_name == '<redacted>':
is_unsymbolicated = True
if is_unsymbolicated:
result.append('{0} {1}'.format(preamble, unsymbolicated))
else:
result.append('{0} {1:30} ({2})'.format(preamble, unsymbolicated, symbolicated))
num += 1
return result
def parse_thread_info(thread, report):
result = []
crashed = thread.get('crashed', False)
index = thread.get('index', -1)
if index == -1:
return result
name = thread.get('name', None)
queue = thread.get('dispatch_queue', None)
if name:
result.append('Thread {0} name: {1}'.format(index, name))
elif queue:
result.append('Thread {0} name: Dispatch queue: {1}'\
.format(index, queue))
if crashed:
result.append('Thread {0} Crashed:'.format(index))
else:
result.append('Thread {0}:'.format(index))
if "backtrace" in thread:
backtrace = parse_backtrace(report, thread['backtrace'])
result += backtrace
return result
def parse_thread_list(report):
crash = get_crash_info(report)
if not crash:
return []
#print dump_json( crash)
threads = crash['threads']
result = []
for thread in threads:
result.append('')
result += parse_thread_info(thread, report)
return result
def get_register_order(cpu):
cpu = cpu.lower()
#print "cpu %s" % cpu
arm = [ 'x'+str(i) for i in range(30)] + ['fp','sp', 'lr', 'pc',
'cpsr']
x86 = ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp',
'esp', 'ss', 'eflags', 'eip', 'cs', 'ds', 'es',
'fs', 'gs']
x86_64 = ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp',
'rsp', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13',
'r14', 'r15', 'rip', 'rflags', 'cs', 'fs', 'gs']
if cpu.startswith('arm'):
return arm
elif cpu in ('x86', 'i386', 'i486', 'i686'):
return x86
elif cpu == 'x86_64':
return x86_64
else:
return arm
def parse_cpu_state(report):
result = ['']
crashed = get_crash_thread(report)
if not crashed:
return result
index = crashed['index']
system = get_system_info(report)
cpu = get_detail_cpu_arch(system['binary_cpu_type'],
system['binary_cpu_subtype'])
cpu = get_cpu_type(cpu)
result.append('Thread {0} crashed with {1} Thread State:'.format(index, cpu))
registers = crashed.get('registers', {}).get('basic', {})
order = get_register_order(cpu)
if not order:
return result
line = ''
for i, reg in enumerate(order):
if (i != 0) and (i % 4 == 0):
result.append(line[:-1])
line = ''
try:
line += '{0:>6}: 0x{1:016x} '.format(reg, registers.get(reg, 0))
except:
continue
if line:
result.append(line[:-1])
return result
def parse_binary_images(report):
result = ['\nBinary Images:']
system = get_system_info(report)
if not system:
return result
exe_path = system['CFBundleExecutablePath']
images = report.get('binary_images', [])
images = sorted(images, key=lambda k: k['image_addr'])
image_count = 0
for image in images:
try:
image_count = image_count + 1
cpu = image.get('cpu_type')
if not cpu:
cpu = image.get('cpuType')
cpu_subtype = image.get('cpu_subtype')
if not cpu:
cpu_subtype = image.get('cpuSubType')
# 解析 cpu
cpu = get_detail_cpu_arch(cpu, cpu_subtype)
#cpu_sub = image['cpu_subtype']
addr = image['image_addr']
size = image['image_size']
path = image['name']
name = os.path.basename(path)
uuid = image['uuid'].lower().replace('-', '')
is_base = '+' if path==exe_path else ' '
if False:#image_count <=10 and not is_image_wechat(name) and simple_symbol_find.uuid_find(uuid) == False:
#name = "<em style=\"color: red;\">%s</em>"%name
result.append('<em style="color: red;">{0:>#18x} - {1:>#18x} {2}{3} <{4}> {5}</em>'\
.format(addr, addr+size-1, is_base, name,
uuid, path))
else:
if name == get_bundle_name(report):
print('{0:>#18x} - {1:>#18x} {2}{3} {4} <{5}> {6}'.format(addr, addr+size-1, is_base, name, cpu, uuid, path))
result.append('{0:>#18x} - {1:>#18x} {2}{3} {4} <{5}> {6}'\
.format(addr, addr+size-1, is_base, name, cpu,
uuid, path))
else:
result.append('{0:>#18x} - {1:>#18x} {2}{3} <{4}> {5}'\
.format(addr, addr+size-1, is_base, name,
uuid, path))
except:
traceback.print_exc()
continue
return result
def parse_mem_info(report):
result = ['\nMemory: {']
system = get_system_info(report)
mem = system.get("memory")
fmt = lambda x: " {0:6}: {1:4} M,".format(x, int(mem[x])/1024/1024)
result.append(fmt("usable"))
result.append(fmt("free"))
result.append(fmt("size")[:-1])
result.append('}')
return result
def parse_app_info(report):
result = ['\nApplication Info:{']
system = get_system_info(report)
if not system:
return []
_s = lambda x: system.get(x, '')
info = get_report_info(report)
_i = lambda x: info.get(x, '')
app_stats = system.get('application_stats', None)
if not app_stats:
return []
try:
if app_stats.get('app_launch_time'):
app_stats['app_launch_time'] = datetime.fromtimestamp(app_stats.get('app_launch_time')).strftime("%Y-%m-%d %H:%M:%S")
app_stats['app_crash__time'] = get_time(report)
app_stats['Process'] = '{0} [{1}]'.format(_s('process_name'), _s('process_id'))
app_stats["Identifier"] = '{0}'.format(_i('id'))
app_stats['Version'] = '{0} ({1})'.format(_s('CFBundleShortVersionString'), _s('CFBundleVersion'))
except:
traceback.print_exc()
user_info = report.get("user", None)
if user_info and user_info.get(APP_NAME):
commit_id = user_info[APP_NAME].get("commit_id", None)
if commit_id:
app_stats['Commit ID'] = '{0}'.format(commit_id)
wcdb_commit_id = user_info[APP_NAME].get("wcdb_commit_id", None)
if wcdb_commit_id:
app_stats['WCDB_Commit ID'] = '{0}'.format(wcdb_commit_id)
app_stats['Code Type'] = '{0}'.format(get_cpu_arch(report))
reason = "".join(parse_crash_reason(report))
if reason:
asi = 'Application Specific Information'
app_stats[asi] = reason.replace(asi, '')
for item in parse_error_info(report):
if not item:
continue
items = item.split(':')
if len(items) == 2:
app_stats[items[0].strip()] = items[1].strip()
if app_stats:
#result.append('\nApplication Info:\n{0}'\
# .format(dump_json(app_stats).replace('"', '')))
for key in sorted(app_stats):
fmt = lambda x: " {0:36}: {1}".format(key, app_stats[key])
result.append(fmt(key))
# if key in ['Crashed Thread','Application Specific Information','Exception Type','Version','app_crash__time','app_launch_time']:
# result.append(show_color(fmt(key)))
# else:
# result.append(fmt(key))
result.append('}\n')
return result
def parse_extra_info(report):
result = ['', 'Extra Information:']
crash = get_crash_info(report)
if not crash:
return []
error = crash.get('error')
if not error:
return []
ns_exception = error.get('nsexception', None)
if ns_exception:
ref_obj = ns_exception.get('referenced_object', None)
if ref_obj:
result.append('Object referenced by NSException:')
result.append(dump_json(ref_obj))
crashed = get_crash_thread(report)
if crashed:
stack = crashed.get('stack', None)
if stack:
result.append('Stack Dump (0x{0:08x}-0x{1:08x}):'\
.format(stack['dump_start'], stack['dump_end']))
result.append('')
result.append(stack.get('contents', ''))
notable_addr = crashed.get('notable_addresses', None)
if notable_addr:
for _tmp_stack in notable_addr:
if notable_addr[_tmp_stack].get('address', None):
notable_addr[_tmp_stack]['address'] = hex(notable_addr[_tmp_stack]['address'])
result.append('Notable Addresses:\n{0}'\
.format(dump_json(notable_addr)))
last_exception = get_last_exception(report)
if last_exception:
addr = last_exception['address']
name = last_exception['name']
reason = last_exception['reason']
result.append('\nLast deallocated NSException (0x{0:016x}): {1}: {2}'\
.format(addr, name, reason))
ref_obj = last_exception.get('referenced_object', None)
if ref_obj:
result.append('Referenced object:\n{0}'\
.format(dump_json(ref_obj)))
info = get_report_info(report)
app = info['process_name']
backtrace = parse_backtrace(crashed['backtrace'], app)
result += backtrace
'''
app_stats = system.get('application_stats', None)
if app_stats:
result.append('\nApplication Stats:\n{0}'\
.format(dump_json(app_stats)))
'''
return result
def parse_log_info(report):
result = ['', 'MMLog:']
user_info = report.get("user", None)
if not user_info or not user_info.get(APP_NAME, None) or not user_info[APP_NAME].get("log"):
result.append("no log found...")
return result
log_list = user_info[APP_NAME]["log"]
result += log_list
return result
def parse_click_info(report):
result = ['', 'Click:']
user_info = report.get("user", None)
if not user_info or not user_info.get(APP_NAME, None) or not user_info[APP_NAME].get("click"):
result.append("no click found...")
return []
click_list = user_info[APP_NAME]["click"]
result += click_list
return result
def parse_other_info(report):
result = ['']
user_info = report.get("user", None)
if not user_info or not user_info.get(APP_NAME, None) or not user_info[APP_NAME].get("log"):
print("no log found...")
return result
log_list = user_info[APP_NAME]["log"][-2:]
result += log_list
return result
def ks_json_2_apple(report, fout):
global IMG_INFO_MAP, APP_NAME
IMG_INFO_MAP = get_binary_img_info(report)
APP_NAME = get_app_name(report)
headers = parse_crash_header(report)
for line in headers:
fout.write(line+'\n')
fout.write('\n')
headers = parse_system_info(report)
for line in headers:
fout.write(line+'\n')
try:
errors = parse_error_info(report)
except:
traceback.print_exc()
errors = []
for line in errors:
line = html.escape(line)
fout.write(line+'\n')
try:
reason = parse_crash_reason(report)
except:
traceback.print_exc()
reason = []
for line in reason:
line = html.escape(line)
fout.write(line+'\n')
try:
user_info = parse_user_info(report)
for line in user_info:
fout.write(line+'\n')
except:
traceback.print_exc()
'''
try:
mems = parse_mem_info(report)
for line in mems:
fout.write(line+'\n')
except:
traceback.print_exc()
'''
app_info = parse_app_info(report)
for line in app_info:
fout.write(line+'\n')
#fout.write('\n\n')
#other_info = parse_other_info(report)
#for line in other_info:
# fout.write(line+'\n')
click_info = parse_click_info(report)
for line in click_info:
line = html.escape(line)
fout.write(line+'\n')
threads = parse_thread_list(report)
for line in threads:
line = html.escape(line)
fout.write(line+'\n')
cpu_state = parse_cpu_state(report)
for line in cpu_state:
line = html.escape(line)
fout.write(line+'\n')
images = parse_binary_images(report)
for line in images:
#line = html.escape(line)
fout.write(line+'\n')
extra = parse_extra_info(report)
for line in extra:
line = html.escape(line)
fout.write(line+'\n')
log_info = parse_log_info(report)
for line in log_info:
line = html.escape(line)
fout.write(line+'\n')
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option("-i","--input_file", help="input ks json file")
parser.add_option("-o","--output_file", help="output file")
(options, args) = parser.parse_args()
if not (options.input_file and options.output_file):
parser.print_help()
sys.exit(1)
reports = json.load(open(options.input_file))
fout = open(options.output_file, 'w')
if type(reports) is list:
print("this intput file contains %d report" %(len(reports)))
for report in reports:
fout.write("*------------------------------- report split line----------------------------*\n")
ks_json_2_apple(report, fout)
else:
ks_json_2_apple(reports, fout)
fout.close()
添加固定的Report Version 避免报错 No crash report version in .json System Info 之前添加必要解析的信息头 Binary Images 主 image 添加 arch 字段(其他的 image 不能添加 arch 字段,因为 matrix 里面记录的其他 image arch 解析出来是 arm64,实际上应该是 arm64e, 会报如下错误 atos cannot load symbols for the file /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport/14.7.1 (18G82) arm64e/Symbols/System/Library/PrivateFrameworks/AudioSession.framework/AudioSession for architecture arm64., 所以只要主 image 添加 arch 字段就可以了)
请问上面这几个信息是要如何添加?是否有Demo提供看下,非常感谢 1.Report Version 是在 WCCrashBlockMonitorConfig 配置中设置 appVersion 吗 2. System Info 之前添加信息头都需要加哪些 3. Binary Images 主 image 添加 arch 字段
我现在是直接将 MatrixIssue 中的 issueData 转成字符串后,使用脚本 python3 ks2apple.py -i matrixCrash.json -o crash1.json
进行解析,报错如下:
添加固定的Report Version 避免报错 No crash report version in .json System Info 之前添加必要解析的信息头 Binary Images 主 image 添加 arch 字段(其他的 image 不能添加 arch 字段,因为 matrix 里面记录的其他 image arch 解析出来是 arm64,实际上应该是 arm64e, 会报如下错误 atos cannot load symbols for the file /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport/14.7.1 (18G82) arm64e/Symbols/System/Library/PrivateFrameworks/AudioSession.framework/AudioSession for architecture arm64., 所以只要主 image 添加 arch 字段就可以了)
请问上面这几个信息是要如何添加?是否有Demo提供看下,非常感谢 1.Report Version 是在 WCCrashBlockMonitorConfig 配置中设置 appVersion 吗 2. System Info 之前添加信息头都需要加哪些 3. Binary Images 主 image 添加 arch 字段
我现在是直接将 MatrixIssue 中的 issueData 转成字符串后,使用脚本
python3 ks2apple.py -i matrixCrash.json -o crash1.json
进行解析,报错如下:
1、信息添加直接看我改的脚本,已经添加进去了,跑完之后可以直接调用 symbolicatecrash 解析 2、no img found for pc 应该是日志本身的问题,看下0x1053d1da4 这个(4382858660 -> 0x1053d1da4)堆栈地址是不是显示 unknown
symbolicatecrash
@madaoCN 直接用 symbolicatecrash 也只能解析出来业务崩溃代码吧,涉及到系统代码,比如Foundation里的是不能符号化成功的吧
添加固定的Report Version 避免报错 No crash report version in .json System Info 之前添加必要解析的信息头 Binary Images 主 image 添加 arch 字段(其他的 image 不能添加 arch 字段,因为 matrix 里面记录的其他 image arch 解析出来是 arm64,实际上应该是 arm64e, 会报如下错误 atos cannot load symbols for the file /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport/14.7.1 (18G82) arm64e/Symbols/System/Library/PrivateFrameworks/AudioSession.framework/AudioSession for architecture arm64., 所以只要主 image 添加 arch 字段就可以了)
请问上面这几个信息是要如何添加?是否有Demo提供看下,非常感谢 1.Report Version 是在 WCCrashBlockMonitorConfig 配置中设置 appVersion 吗 2. System Info 之前添加信息头都需要加哪些 3. Binary Images 主 image 添加 arch 字段 我现在是直接将 MatrixIssue 中的 issueData 转成字符串后,使用脚本
python3 ks2apple.py -i matrixCrash.json -o crash1.json
进行解析,报错如下:1、信息添加直接看我改的脚本,已经添加进去了,跑完之后可以直接调用 symbolicatecrash 解析 2、no img found for pc 应该是日志本身的问题,看下0x1053d1da4 这个(4382858660 -> 0x1053d1da4)堆栈地址是不是显示 unknown
@madaoCN 刚刚看了下,确实是显示unknown,请问是什么原因导致的?日志是通过Matrix的onReportIssue: 方法中直接拿到的json matrixCrash.json.zip
而且脚本解析出来的日志底部会出现MMLog no log found
嗯 多谢
好赞,最开始使用 python2.7 版本运行,html.escape 方法报错,换了 python3 就解决了