随着项目的扩大,为了提高开发效率、编译速度,组件化(二进制化)是一种不可避免的趋势。大部分的公司都分享过相关文章,本文不再赘述。下面集中在二进制化带来的问题,以及相应的解决方案。
二进制在带来便利的同时,也带来一些新问题:
很多大厂都对此进行了研究,美团技术最近也做过一篇分享:美团 iOS 工程 zsource 命令背后的那些事儿
但是美中不足的是,文章中的解决方案存在以下问题:
为了解决以上问题,本文通过 lldb 提供的源码映射能力,实现了将任意的二进制文件映射到源码文件的通用方案。ps.如果读者了解 lldb + python ,阅读以下代码会更加简单。
在 ~/.lldbinit
(Xcode 启动时,会执行该脚本)位置创建文件,并添加代码(文件路径可以根据自身情况调整)
command script import /Users/kukudeaidian/LLDB_MapFile.py
创建 /Users/kukudeaidian/LLDB_MapFile.py
文件(和 lldbinit 中的路径保持一致即可),并添加下面的代码:
#encoding=utf-8
import lldb
import re
import os
# command 是用户输入的符号地址
def sun_map_address(debugger, command, result, internal_dict):
print(command)
# 获取 lldb 的命令交互环境,可以动态执行一些命令,比如 po obj
interpreter = lldb.debugger.GetCommandInterpreter()
# 创建一个对象,命令执行结果会通过该对象保存
returnObject = lldb.SBCommandReturnObject()
# 通过 image loopup 命令查找输入符号地址所在的编译模块信息
interpreter.HandleCommand('image lookup -v --address ' + command, returnObject)
# 获取返回结果
output = returnObject.GetOutput();
# 下面的代码设计思想是:
# 1、根据{地址}查找该地址所属的{源码编译路径}+{编译文件名}
# 2、通过{编译文件名}动态在{指定路径}查找相应的{源码路径}
# 3、将{源码编译路径}与{源码路径}映射
# 实际使用时,可以参考下面的方案。
# 1、根据{地址}查找该地址所属的{编译模块}。比如,SDWebImage
# 2、通过脚本动态下载{编译模块}的{源码仓库}
# 3、将{编译模块}与{源码仓库}映射
# 通过正则获取二进制编译时,源码的真正路径
filePath = re.match(r'(.|\n)*file = "(.*?)".*', output,re.M).group(2)
# 通过真正路径获取编译源文件的文件名
fileName = re.match(r'/.*/(.*)', filePath).group(1)
# 通过文件名在 ~/MMAViewabilitySDK_iOS 目录(可以是任意的地址或者通过 git clone 动态下载)下查找源文件
sourcePath = os.popen('mdfind -onlyin ~/MMAViewabilitySDK_iOS '+fileName).read().replace('\n','')
# 通过 settings set target.source-map 命令执行编译源码位置与当前源码位置的映射
interpreter.HandleCommand('settings set target.source-map ' + filePath + ' ' + sourcePath, returnObject)
# 添加一个 扩展命令。sun_map_address
# 在 lldb 输入 sun_map_address 0x10803839 时,会执行 lldb_MapFile.py 文件的 sun_map_address 方法
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add sun_map_address -f lldb_MapFile.sun_map_address')