iOS的out-of-memory日志如何翻译成堆栈信息
iOS的out-of-memory日志中的堆栈信息如何翻译成堆栈信息? 是否提供了如OOMDetector中的translate_oom.py工具。
可以提供,脚本还在整理,现在还有很多不通用的地方。
您好,麻烦问下,现在可以提供翻译的脚本了么?
嗯.. 我“失忆”了,小本本已经记下,这周我找相关同学要到脚本加上去,
同问下翻译脚本,或者告知怎么翻译?谢谢
@bart1989 @qinhaibo @gelinxiao 看了我们后台同学的翻译脚本,极其不举普适性。正在催他们搞一个通用的。时间不掌握在我手上,T_T.
参考这里内存监控的格式:
https://github.com/Tencent/matrix/wiki/Matrix-for-iOS-macOS-%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E
matrix/matrix-iOS/script 里也有卡顿和耗电堆栈解析脚本,结合看看。看看能不能先自己解析一下,或者提个PR,帮我们完善一下解析脚本!!希望谅解。
@chzhij5 请教个问题,内存监控只有在超出内存崩溃的情况下,才会记录并上报吗?我这边集成到项目中只有这种情况下才上报,内存监控配置我采用的是defaultConfig,是我配置的有问题吗?
@chzhij5 请教个问题,内存监控只有在超出内存崩溃的情况下,才会记录并上报吗?我这边集成到项目中只有这种情况下才上报,内存监控配置我采用的是defaultConfig,是我配置的有问题吗?
内存监控开启后会一直记录。
https://github.com/Tencent/matrix/blob/8e80e452d7100be9e67d4f6d9678aa4bcf88b73c/matrix/matrix-iOS/Matrix/WCMemoryStat/MemoryStatPlugin/WCMemoryStatPlugin.mm#L89 参考这里。现在是设定只有检测到爆内存才会上报。
@bart1989 @qinhaibo @gelinxiao 看了我们后台同学的翻译脚本,极其不举普适性。正在催他们搞一个通用的。时间不掌握在我手上,T_T.
参考这里内存监控的格式:
https://github.com/Tencent/matrix/wiki/Matrix-for-iOS-macOS-%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E
matrix/matrix-iOS/script 里也有卡顿和耗电堆栈解析脚本,结合看看。看看能不能先自己解析一下,或者提个PR,帮我们完善一下解析脚本!!希望谅解。
请问下当前是没有弄这个的计划吗
@chzhij5 请教个问题,内存监控只有在超出内存崩溃的情况下,才会记录并上报吗?我这边集成到项目中只有这种情况下才上报,内存监控配置我采用的是defaultConfig,是我配置的有问题吗?
请问下这块你们是怎么处理的
@bart1989 @qinhaibo @gelinxiao 看了我们后台同学的翻译脚本,极其不举普适性。正在催他们搞一个通用的。时间不掌握在我手上,T_T. 参考这里内存监控的格式: https://github.com/Tencent/matrix/wiki/Matrix-for-iOS-macOS-%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E matrix/matrix-iOS/script 里也有卡顿和耗电堆栈解析脚本,结合看看。看看能不能先自己解析一下,或者提个PR,帮我们完善一下解析脚本!!希望谅解。
请问下当前是没有弄这个的计划吗
同问
@chzhij5 请教个问题,内存监控只有在超出内存崩溃的情况下,才会记录并上报吗?我这边集成到项目中只有这种情况下才上报,内存监控配置我采用的是defaultConfig,是我配置的有问题吗?
请问 内存这块json数据 你解析出来了
您好,可以分享一下你们解析out-of-memory的日志的脚本吗,不通用也没事,给大家提供一下思路
您好,可以分享一下你们解析out-of-memory的日志的脚本吗
写了一个简单的解析脚本,结果如下

脚本如下
#coding=utf-8
import os;
import re;
import json;
from optparse import OptionParser
from util import *
class OOMAnalyzer(object):
def __init__(self, inputDir, logFileUrl, appDsymPath=None):
self.inputDir = inputDir
self.logFileUrl = logFileUrl
self.appDsymPath = appDsymPath
def run(self):
if self.logFileUrl.find("http:") >= 0 or self.logFileUrl.find("https:") >= 0:
# 下载日志文件
downloadFilePath = "%s/%s" % (self.inputDir, "OOM.json")
downloadCMD = "wget %s -O %s" % (self.logFileUrl, downloadFilePath)
os.system(downloadCMD)
logFilePath = "%s/%s" % (self.inputDir, "OOM.json")
else:
logFilePath = self.logFileUrl
# 转换trace文件为json文件
print "========转换log文件为json对象"
f = open(logFilePath, mode="r")
contentStr = f.read()
logJson = json.loads(contentStr)
f.close()
# 解析数据
self.parseLog(logJson)
# 保存数据
# outputPath = "%s/%s" % (self.inputDir, "Loads.xlsx")
# if os.path.exists(outputPath):
# os.remove(outputPath)
# self.savePreviewInfoToExcel(logJson, outputPath)
# self.saveLogToExcelWithGroup(logJson, groupedData, outputPath)
# self.saveLogToExcel(logJson, outputPath)
print "Done"
def parseLog(self, logJson):
# 解析获取所有的Offsets
uuid2Offsets = self.parseOffsets(logJson)
# Head
head = logJson["head"]
app_uuid = head["app_uuid"]
# 解析当前APP的符号
curAppOffsets = uuid2Offsets.get(app_uuid)
if curAppOffsets is None:
return
address2SymMap = self.symbolictedAddress(self.appDsymPath, curAppOffsets)
print "address2SymMap=%s" % (address2SymMap)
# 重新构造结果
self.recreateLog(logJson, app_uuid, address2SymMap)
# 保存结果
outputPath = "%s/%s" % (self.inputDir, "OOM-Symbolicated.json")
fo = open(outputPath, "w")
fo.write(json.dumps(logJson))
fo.close()
print "Done"
def parseOffsets(self, logJson):
# Items 处理
uuid2Offsets = {}
datas = logJson["items"]
for dataItemIdx in range(len(datas)):
dataItem = datas[dataItemIdx]
stacks = dataItem.get("stacks")
if stacks is None:
continue
for stackIdx in range(len(stacks)):
stack = stacks[stackIdx]
frames = stack["frames"]
for frameIdx in range(len(frames)):
frame = frames[frameIdx]
uuid = frame["uuid"]
offset = frame["offset"]
uuids = uuid2Offsets.get(uuid)
if uuids is None:
uuids = []
uuid2Offsets[uuid] = uuids
try:
uuids.index(offset)
except ValueError:
uuids.append(offset)
print "处理结果 uuid 数量 %s" % (len(uuid2Offsets.keys()))
return uuid2Offsets
def symbolictedAddress(self, dsymPath, addresses, loadAddress = "", isSlide = True):
allAddressStr = ""
for address in addresses:
if isSlide:
addressInt = int(address) + 0x100000000
allAddressStr += ("%s " % hex(addressInt))
else:
allAddressStr += ("%s " % address)
if isSlide:
cmd = "atos -o %s -l %s %s " % (dsymPath, "0x100000000", allAddressStr)
else:
cmd = "atos -o %s -l %s %s " % (dsymPath, loadAddress, allAddressStr)
# res = os.system()
a = os.popen(cmd)
res = a.read()
symbols = res.split("\n")
address2SymMap = {}
for i in range(len(addresses)):
address2SymMap[addresses[i]] = symbols[i]
return address2SymMap
def recreateLog(self, logJson, destUuid, address2SymMap):
# Items 处理
uuid2Offsets = {}
datas = logJson["items"]
for dataItemIdx in range(len(datas)):
dataItem = datas[dataItemIdx]
# Size
size = dataItem.get("size")
sizeStr = readableStrFromSize(size)
if sizeStr is not None:
dataItem["size"] = sizeStr
# Stacks
stacks = dataItem.get("stacks")
if stacks is None:
continue
for stackIdx in range(len(stacks)):
stack = stacks[stackIdx]
frames = stack["frames"]
symbolictedFrames = []
for frameIdx in range(len(frames)):
frame = frames[frameIdx]
uuid = frame["uuid"]
offset = frame["offset"]
if destUuid == uuid:
symbolictedFrame = address2SymMap.get(offset)
if symbolictedFrame is not None:
symbolictedFrames.append(symbolictedFrame)
stack["frames"] = symbolictedFrames
def main(argv):
parser = OptionParser('usage: %prog -d <directory_path> -r <远程服务器Host>')
parser.add_option("-d", "--dir", dest="dir", help="包含.appletrace的文件夹")
parser.add_option("-u", "--file-url", dest="fileUrl", help="日志文件路径")
(options, args) = parser.parse_args()
if options.dir is None:
parser.print_help();
return
if options.remoteHost is None:
parser.print_help();
return
processor = OOMAnalyzer(options.dir, options.fileUrl)
processor.run()
if __name__ == "__main__":
main()
测试代码:
def test_OOMAnalyzer(self):
inputDir = "/Users/aron/Downloads/trace/global_943_2"
logFileUrl = "%s/download?path=%%2FLibrary%%2FBBPerfData%%2FOOM.json" % ("http://10.1.17.250")
dsymPath = "/Users/aron/Library/Developer/Xcode/DerivedData/MatrixDemo-hddrwbozwcpgmucghwqbgeurgvbg/Build/Products/Debug-iphoneos/MatrixDemo.app/MatrixDemo"
processor = OOMAnalyzer(inputDir, logFileUrl, appDsymPath=dsymPath)
processor.run()
写了一个简单的解析脚本,结果如下
脚本如下
#coding=utf-8 import os; import re; import json; from optparse import OptionParser from util import * class OOMAnalyzer(object): def __init__(self, inputDir, logFileUrl, appDsymPath=None): self.inputDir = inputDir self.logFileUrl = logFileUrl self.appDsymPath = appDsymPath def run(self): if self.logFileUrl.find("http:") >= 0 or self.logFileUrl.find("https:") >= 0: # 下载日志文件 downloadFilePath = "%s/%s" % (self.inputDir, "OOM.json") downloadCMD = "wget %s -O %s" % (self.logFileUrl, downloadFilePath) os.system(downloadCMD) logFilePath = "%s/%s" % (self.inputDir, "OOM.json") else: logFilePath = self.logFileUrl # 转换trace文件为json文件 print "========转换log文件为json对象" f = open(logFilePath, mode="r") contentStr = f.read() logJson = json.loads(contentStr) f.close() # 解析数据 self.parseLog(logJson) # 保存数据 # outputPath = "%s/%s" % (self.inputDir, "Loads.xlsx") # if os.path.exists(outputPath): # os.remove(outputPath) # self.savePreviewInfoToExcel(logJson, outputPath) # self.saveLogToExcelWithGroup(logJson, groupedData, outputPath) # self.saveLogToExcel(logJson, outputPath) print "Done" def parseLog(self, logJson): # 解析获取所有的Offsets uuid2Offsets = self.parseOffsets(logJson) # Head head = logJson["head"] app_uuid = head["app_uuid"] # 解析当前APP的符号 curAppOffsets = uuid2Offsets.get(app_uuid) if curAppOffsets is None: return address2SymMap = self.symbolictedAddress(self.appDsymPath, curAppOffsets) print "address2SymMap=%s" % (address2SymMap) # 重新构造结果 self.recreateLog(logJson, app_uuid, address2SymMap) # 保存结果 outputPath = "%s/%s" % (self.inputDir, "OOM-Symbolicated.json") fo = open(outputPath, "w") fo.write(json.dumps(logJson)) fo.close() print "Done" def parseOffsets(self, logJson): # Items 处理 uuid2Offsets = {} datas = logJson["items"] for dataItemIdx in range(len(datas)): dataItem = datas[dataItemIdx] stacks = dataItem.get("stacks") if stacks is None: continue for stackIdx in range(len(stacks)): stack = stacks[stackIdx] frames = stack["frames"] for frameIdx in range(len(frames)): frame = frames[frameIdx] uuid = frame["uuid"] offset = frame["offset"] uuids = uuid2Offsets.get(uuid) if uuids is None: uuids = [] uuid2Offsets[uuid] = uuids try: uuids.index(offset) except ValueError: uuids.append(offset) print "处理结果 uuid 数量 %s" % (len(uuid2Offsets.keys())) return uuid2Offsets def symbolictedAddress(self, dsymPath, addresses, loadAddress = "", isSlide = True): allAddressStr = "" for address in addresses: if isSlide: addressInt = int(address) + 0x100000000 allAddressStr += ("%s " % hex(addressInt)) else: allAddressStr += ("%s " % address) if isSlide: cmd = "atos -o %s -l %s %s " % (dsymPath, "0x100000000", allAddressStr) else: cmd = "atos -o %s -l %s %s " % (dsymPath, loadAddress, allAddressStr) # res = os.system() a = os.popen(cmd) res = a.read() symbols = res.split("\n") address2SymMap = {} for i in range(len(addresses)): address2SymMap[addresses[i]] = symbols[i] return address2SymMap def recreateLog(self, logJson, destUuid, address2SymMap): # Items 处理 uuid2Offsets = {} datas = logJson["items"] for dataItemIdx in range(len(datas)): dataItem = datas[dataItemIdx] # Size size = dataItem.get("size") sizeStr = readableStrFromSize(size) if sizeStr is not None: dataItem["size"] = sizeStr # Stacks stacks = dataItem.get("stacks") if stacks is None: continue for stackIdx in range(len(stacks)): stack = stacks[stackIdx] frames = stack["frames"] symbolictedFrames = [] for frameIdx in range(len(frames)): frame = frames[frameIdx] uuid = frame["uuid"] offset = frame["offset"] if destUuid == uuid: symbolictedFrame = address2SymMap.get(offset) if symbolictedFrame is not None: symbolictedFrames.append(symbolictedFrame) stack["frames"] = symbolictedFrames def main(argv): parser = OptionParser('usage: %prog -d <directory_path> -r <远程服务器Host>') parser.add_option("-d", "--dir", dest="dir", help="包含.appletrace的文件夹") parser.add_option("-u", "--file-url", dest="fileUrl", help="日志文件路径") (options, args) = parser.parse_args() if options.dir is None: parser.print_help(); return if options.remoteHost is None: parser.print_help(); return processor = OOMAnalyzer(options.dir, options.fileUrl) processor.run() if __name__ == "__main__": main()测试代码:
def test_OOMAnalyzer(self): inputDir = "/Users/aron/Downloads/trace/global_943_2" logFileUrl = "%s/download?path=%%2FLibrary%%2FBBPerfData%%2FOOM.json" % ("http://10.1.17.250") dsymPath = "/Users/aron/Library/Developer/Xcode/DerivedData/MatrixDemo-hddrwbozwcpgmucghwqbgeurgvbg/Build/Products/Debug-iphoneos/MatrixDemo.app/MatrixDemo" processor = OOMAnalyzer(inputDir, logFileUrl, appDsymPath=dsymPath) processor.run()
readableStrFromSize 应该是你的工具里面的吧,可以分享出来吗?
@lqwang521 方法如下
def readableStrFromSize(size):
kb = size * 1.0 / 1024
mb = kb / 1024
gb = mb / 1024
if gb > 1:
return "%.2f GB" % (gb)
elif mb > 1:
return "%.2f MB" % (mb)
elif kb > 1:
return "%.2f kB" % (kb)
else:
return "%.2f B" % (size)
写了一个简单的解析脚本,结果如下
脚本如下
#coding=utf-8 import os; import re; import json; from optparse import OptionParser from util import * class OOMAnalyzer(object): def __init__(self, inputDir, logFileUrl, appDsymPath=None): self.inputDir = inputDir self.logFileUrl = logFileUrl self.appDsymPath = appDsymPath def run(self): if self.logFileUrl.find("http:") >= 0 or self.logFileUrl.find("https:") >= 0: # 下载日志文件 downloadFilePath = "%s/%s" % (self.inputDir, "OOM.json") downloadCMD = "wget %s -O %s" % (self.logFileUrl, downloadFilePath) os.system(downloadCMD) logFilePath = "%s/%s" % (self.inputDir, "OOM.json") else: logFilePath = self.logFileUrl # 转换trace文件为json文件 print "========转换log文件为json对象" f = open(logFilePath, mode="r") contentStr = f.read() logJson = json.loads(contentStr) f.close() # 解析数据 self.parseLog(logJson) # 保存数据 # outputPath = "%s/%s" % (self.inputDir, "Loads.xlsx") # if os.path.exists(outputPath): # os.remove(outputPath) # self.savePreviewInfoToExcel(logJson, outputPath) # self.saveLogToExcelWithGroup(logJson, groupedData, outputPath) # self.saveLogToExcel(logJson, outputPath) print "Done" def parseLog(self, logJson): # 解析获取所有的Offsets uuid2Offsets = self.parseOffsets(logJson) # Head head = logJson["head"] app_uuid = head["app_uuid"] # 解析当前APP的符号 curAppOffsets = uuid2Offsets.get(app_uuid) if curAppOffsets is None: return address2SymMap = self.symbolictedAddress(self.appDsymPath, curAppOffsets) print "address2SymMap=%s" % (address2SymMap) # 重新构造结果 self.recreateLog(logJson, app_uuid, address2SymMap) # 保存结果 outputPath = "%s/%s" % (self.inputDir, "OOM-Symbolicated.json") fo = open(outputPath, "w") fo.write(json.dumps(logJson)) fo.close() print "Done" def parseOffsets(self, logJson): # Items 处理 uuid2Offsets = {} datas = logJson["items"] for dataItemIdx in range(len(datas)): dataItem = datas[dataItemIdx] stacks = dataItem.get("stacks") if stacks is None: continue for stackIdx in range(len(stacks)): stack = stacks[stackIdx] frames = stack["frames"] for frameIdx in range(len(frames)): frame = frames[frameIdx] uuid = frame["uuid"] offset = frame["offset"] uuids = uuid2Offsets.get(uuid) if uuids is None: uuids = [] uuid2Offsets[uuid] = uuids try: uuids.index(offset) except ValueError: uuids.append(offset) print "处理结果 uuid 数量 %s" % (len(uuid2Offsets.keys())) return uuid2Offsets def symbolictedAddress(self, dsymPath, addresses, loadAddress = "", isSlide = True): allAddressStr = "" for address in addresses: if isSlide: addressInt = int(address) + 0x100000000 allAddressStr += ("%s " % hex(addressInt)) else: allAddressStr += ("%s " % address) if isSlide: cmd = "atos -o %s -l %s %s " % (dsymPath, "0x100000000", allAddressStr) else: cmd = "atos -o %s -l %s %s " % (dsymPath, loadAddress, allAddressStr) # res = os.system() a = os.popen(cmd) res = a.read() symbols = res.split("\n") address2SymMap = {} for i in range(len(addresses)): address2SymMap[addresses[i]] = symbols[i] return address2SymMap def recreateLog(self, logJson, destUuid, address2SymMap): # Items 处理 uuid2Offsets = {} datas = logJson["items"] for dataItemIdx in range(len(datas)): dataItem = datas[dataItemIdx] # Size size = dataItem.get("size") sizeStr = readableStrFromSize(size) if sizeStr is not None: dataItem["size"] = sizeStr # Stacks stacks = dataItem.get("stacks") if stacks is None: continue for stackIdx in range(len(stacks)): stack = stacks[stackIdx] frames = stack["frames"] symbolictedFrames = [] for frameIdx in range(len(frames)): frame = frames[frameIdx] uuid = frame["uuid"] offset = frame["offset"] if destUuid == uuid: symbolictedFrame = address2SymMap.get(offset) if symbolictedFrame is not None: symbolictedFrames.append(symbolictedFrame) stack["frames"] = symbolictedFrames def main(argv): parser = OptionParser('usage: %prog -d <directory_path> -r <远程服务器Host>') parser.add_option("-d", "--dir", dest="dir", help="包含.appletrace的文件夹") parser.add_option("-u", "--file-url", dest="fileUrl", help="日志文件路径") (options, args) = parser.parse_args() if options.dir is None: parser.print_help(); return if options.remoteHost is None: parser.print_help(); return processor = OOMAnalyzer(options.dir, options.fileUrl) processor.run() if __name__ == "__main__": main()测试代码:
def test_OOMAnalyzer(self): inputDir = "/Users/aron/Downloads/trace/global_943_2" logFileUrl = "%s/download?path=%%2FLibrary%%2FBBPerfData%%2FOOM.json" % ("http://10.1.17.250") dsymPath = "/Users/aron/Library/Developer/Xcode/DerivedData/MatrixDemo-hddrwbozwcpgmucghwqbgeurgvbg/Build/Products/Debug-iphoneos/MatrixDemo.app/MatrixDemo" processor = OOMAnalyzer(inputDir, logFileUrl, appDsymPath=dsymPath) processor.run()
我想请问下这个json文件是哪里产生的?