如何使用Swift来实现一个命令行工具的方法

2020-05-15 16:00:18于海丽

使用如下:

let (ret1, output1) = runShellAndOutput("ls -l")
if let output11 = output1 {
 print(output11)
}

let (ret2, output2) = runShellWithArgsAndOutput("ls", "-l")
if let output22 = output2 {
 print(output2)
}

如何解析Matrix的OOM Log

Matrix的OOM Log格式如下,其实就是一个大JSON:

{
 "head": {
  "protocol_ver": 1,
  "phone": "iPhone10,1",
  "os_ver": "13.4",
  "launch_time": 1589361495000,
  "report_time": 1589362109100,
  "app_uuid": ""
 },
 "items": [
  {
   "tag": "iOS_MemStat",
   "info": "",
   "scene": "",
   "name": "Malloc 12.54 MiB",
   "size": 146313216,
   "count": 1,
   "stacks": [
    {
     "caller": "f07199ac8a903127b17f0a906ffb0237@84128",
     "size": 146313216,
     "count": 1,
     "frames": [
      {
       "uuid": "a0a7d67af0f3399a8f006f92716d8e6f",
       "offset": 67308
      },
      {
       "uuid": "a0a7d67af0f3399a8f006f92716d8e6f",
       "offset": 69836
      },
      {
       "uuid": "f07199ac8a903127b17f0a906ffb0237",
       "offset": 84128
      },
      {
       "uuid": "b80198f7beb93e79b25c7a27d68bb489",
       "offset": 14934312
      },
      {
       "uuid": "1a46239df2fc34b695bc9f38869f0c85",
       "offset": 1126304
      },
      {
       "uuid": "1a46239df2fc34b695bc9f38869f0c85",
       "offset": 123584
      },
      {
       "uuid": "1a46239df2fc34b695bc9f38869f0c85",
       "offset": 1135100
      }]
    }
   ]
  }
 ]
}

解析的思路其实非常简单,将JSON转为Model,然后根据所需,提取对应的信息即可。

uuid是mach-o的唯一标识,offset则是符号相对于mach-o基地址的偏移量。拿到dSYM文件,使用 atos 命令即可进行符号化。

guard let rawLogModel = MatrixOOMLogParser.parse() else { exit(-1) }
print("______ Start to process Matrix OOM Log ...")

let group = DispatchGroup()

var metaLog = ""

for item in bodyInfo.items {
 guard let stacks = item.stacks else { continue }
 
 group.enter()
 
 DispatchQueue.global().async {
  var log = "______ item ______ name: (item.name), size: (item.size), count: (item.count) n"
  metaLog += log
  
  for stack in stacks {
   let outputs = stack.frames.map({ (frame: MatrixOOMLogModelFrame) -> String in
    // let uuid = frame.uuid
    let offset = frame.offset
    let instructionAddress = loadAddress + offset
    let (_, output) = runShellAndOutput("xcrun atos -o (dwarf) -arch arm64 -l 0x1 (instructionAddress.hexValue)")
    return output ?? ""
   })
   
   log += outputs.joined()
   
   print(log)
  }
  
  group.leave()
 }
}

group.wait()

print("n(metaLog)n")

print("______ Finished processing Matrix OOM Log ...")

MatrixOOMLogParser.parse() 就是将JSON转为Model,这里用的就是Swift里边的Codable。

这里有一个需要注意的点,Mac CLI没有Bundle的概念,只有一个bin文件。所以对于原始的JSON文件,只能通过外部bundle的方式来添加。通过 New->Target 单独建立一个bundle。需要在 Xcode -> Build Phases -> Copy Files 中添加该bundle名,然后即可通过 Bundle(url: mockDataBundleURL) 来加载该bundle并获取其中的log文件了。