当前位置: 首页 > news >正文

脚本处理iOS的Crash日志

背景

当我们打包app时,可以选择生成对应的符号表,其保存 16 进制函数地址映射信息,通过给定的函数起始地址和偏移量,可以对应函数具体信息以供分析。

所以我们拿到测试给的闪退日志(.crash)时,需要找到打包时对应生成的符号表(.dSYM)作为钥匙解析。具体分为下面几个步骤

  1. dwarfdump --uuid 命令获取 .dSYMuuid

  2. 打开 .crash 文件,在特定位置找到 uuid

  3. 根据 arm 版本比对两者是否一致

  4. Xcode 目录下寻找 symbolicatecrash 工具

    不同版本文件路径不同,具体版本请谷歌。Xcode9路径是/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/

  5. 设置终端环境变量

    export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

  6. 使用 symbolicatecrash 工具解析日志 symbolicatecrash .crash .dsym > a.out

虽然过程不复杂,但是每次都需要手动执行一次检查与命令,过于繁琐,所以决定用脚本化提高效率。


步骤实现

输入Crash日志

#要求输入crash文件路径
inputFile 'Please Input Crash File' 'crash'
crashPath=$filePath
复制代码

由于需要输入两种不同后缀的文件路径,且都需要检查,因此统一定义一个方法。

#定义全局变量
filePath=
#输入文件路径
inputFile() {
    readSuccess=false
    #首先清空变量值
    filePath=
    while [ $readSuccess = false ]; do 
        echo $1
        #读取到变量中
        read -a filePath
        if [[ ! -e $filePath || ${filePath##*.} != $2 ]]; then
            echo "Input file is not ."$2
        else
            readSuccess=true
        fi
    done
}
复制代码

.dSYM 是文件夹路径,所以这里简单的判断了路径是否存在,如果不存在就继续让用户输入。

Shell命令中判断分为[]与[[]],后者比前者更通用,可以使用 || 正则运算等。

判断中,-f表示检查是否存在该文件,-d表示检查是否存在文件夹,-e表示检查是否存在该路径

输入dSYM符号表

dsymSuccess=false
while [ $dsymSuccess = false ]; do
    #要求输入dSYM文件路径
    inputFile 'Please Input dSYM File' 'dSYM'
    dsymPath=$filePath
    #检查是否匹配
    checkUUID "$crashPath" "$dsymPath"
    match=$?
    if [ $match -eq 0 ]; then
        echo 'UUID not match!'
    else
        dsymSuccess=true
    fi
done
复制代码

循环获取匹配 UUIDdSYM ,这里使用了另一种方法获取方法返回值,具体之后章节会总结。

查找symbolicatecrash工具

Xcode 文件夹指定路径下查找工具,加快效率,如果没找到就停止运行。

# 查找symbolicatecrash解析工具,内置在Xcode的库文件中
toolPath=`find /Applications/Xcode.app/Contents/SharedFrameworks -name symbolicatecrash | head -n 1`
if [ ! -f $toolPath ]; then
    echo "Symbolicatecrash not exist!"
    exit 0
fi
复制代码

执行解析命令

#先设置环境变量
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
#指定解析结果路径
crashName=`basename $crashPath`
afterPath="$(dirname "$crashPath")"/"${crashName%%.*}""_after.crash"
#开始解析
$toolPath "$crashPath" "$dsymPath" > "$afterPath" 2> /dev/null 
复制代码

这里我将错误信息导流到 /dev/null,保证解析文件没有杂乱信息。


遇到的问题

怎么获取函数返回值?

之前没有处理过需要返回数值的方法,所以一开始有点懵,查询资料后最终采用了两种方式实现了效果,现在做一些总结。

全局变量记录

#定义全局变量
filePath=
inputFile() {
    #读取到变量中
    read -a filePath
}
inputFile
crashPath=$filePath
复制代码

通过 inputFile 方法来了解一下,首先定义一个全局变量为 filePath,在方法中重新赋值,方法结束后读取全局变量中的数据。

这种方法的好处是可以自定义返回参数类型和个数,缺点是容易和其他变量搞混。

Return返回值

类似与C语言中的用法,脚本也支持 retrun 0 返回结果并停止运行。

checkUUID() {
    grep "$arm64id" "$1"
    if [ $? -ne 0 ]; then
        return 1;
    fi
    return 0;
}
checkUUID "$crashPath" "$dsymPath"
match=$?
复制代码

获取结果的方式为 $?,其能够返回环境中最后一个指令结果,也就是之前执行的checkUUID的结果。

优点是简洁明了,符合编码习惯,缺点是返回值只能是 0-255 的数字,不能返回其他类型的数据。

获取打印值

还有一种方法其实平时一直在使用,只不过并不了解其运行方式。

crashName=`basename $crashPath`

print() {
    echo "Hello World"
}
text=$(print)
复制代码

运行系统预设的方法或者自定义方法,将执行命令用 $() 的方式使用,就可以获取该命令中所有打印的信息,赋值到变量就可以拿到需要的返回值。

优点是功能全效率高,使用字符串的方式可以传递定制化信息,缺点是不可预期返回结果,需要通过字符串查找等命令辅助。

循环输入合法路径

在我的设想中,需要用户输入匹配的 dSYM 文件路径,如果不匹配,则重新输入,直到合法。为了支持嵌套,需要定义局部变量控制循环,具体代码如下

dsymSuccess=false
while [ $dsymSuccess = false ]; do
    #要求输入dSYM文件路径
    inputFile 'Please Input dSYM File' 'dSYM'
    dsymPath=$filePath
    #检查是否匹配
    checkUUID "$crashPath" "$dsymPath"
    match=$?
    if [ $match -eq 0 ]; then
        echo 'UUID not match!'
    else
        dsymSuccess=true
    fi
done
复制代码

处理字符串

获取到 UUID 所有输出信息后,需要截取出对应平台的信息,处理还是不太熟悉,特地整理如下

#原始信息
UUID: 92E495AA-C2D4-3E9F-A759-A50AAEF446CD (armv7) /Volumes/.dSYM/Contents/Resources/DWARF/app
UUID: 536527A8-0243-34DB-AE08-F1F64ACA4351 (arm64) /Volumes/.dSYM/Contents/Resources/DWARF/app

#去除中间间隔-
uuid=${uuid//-/}

#从后往前找第一个匹配 \(arm64的,并且都删除
arm64id=${uuid% \(arm64*}
#处理后
UUID: 92E495AAC2D43E9FA759A50AAEF446CD (armv7) /Volumes/.dSYM/Contents/Resources/DWARF/app
UUID: 536527A8024334DBAE08F1F64ACA4351

#从前往后找最后一个UUID: ,并删除
arm64id=${arm64id##*UUID: }
#处理后 
536527A8024334DBAE08F1F64ACA4351
复制代码

总结

看似简单的脚本,也花了一天时间编写,总体还是不太熟练,仍需努力联系。

这次特地尝试了与上次不同的参数输入方法,使用提示输入的方式,果然遇到了新的问题。好在都查资料解决了,结果还算满意。

脚本我提交到了Github,欢迎大家指教共同进步!给个关注最好啦~

相关文章:

  • 大家都在用HTTP/2了,而你还没听说过?
  • ➹使用webpack配置多页面应用(MPA)
  • Linux服务器配置---ntp
  • 基于NetCore C#的在线代码生成器
  • CSS3 动画卡顿解决方案
  • ywy_c_asm题
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • CF960G Bandit Blues(第一类斯特林数)
  • 我的网站搭建 (第21天) 评论功能设计
  • PHP 5.6 已结束安全支持,你升级到 PHP 7 系列了吗?
  • 企业网管用linux搭建邮件服务器为公司降本增效
  • Android基础:常见布局
  • 活佛开示小册下载
  • 在eclipse里配置Android ndk环境 适用于windows mac 和linux[转]
  • 把SOA看清楚
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【翻译】babel对TC39装饰器草案的实现
  • 【面试系列】之二:关于js原型
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • FineReport中如何实现自动滚屏效果
  • HomeBrew常规使用教程
  • javascript数组去重/查找/插入/删除
  • Laravel核心解读--Facades
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • PAT A1017 优先队列
  • PHP的类修饰符与访问修饰符
  • python docx文档转html页面
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 翻译--Thinking in React
  • 和 || 运算
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 如何合理的规划jvm性能调优
  • 如何实现 font-size 的响应式
  • 算法-图和图算法
  • # 数论-逆元
  • #162 (Div. 2)
  • (02)Hive SQL编译成MapReduce任务的过程
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (Java)【深基9.例1】选举学生会
  • (LeetCode C++)盛最多水的容器
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (接口自动化)Python3操作MySQL数据库
  • (强烈推荐)移动端音视频从零到上手(下)
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • ***通过什么方式***网吧
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始