内容简介:在做性能监控的时候,如果能把监控的CPU和内存增长变化用图表展示出来会比较直观,花了点时间用Python实现了下,来看下怎么用Python绘制Android CPU和内存变化曲线,生成增长曲线图表的PNG图片。一开始想通过采集的CPU和内存数据,导出到Excel生成增长曲线图表。做了下调研,并没有比较好的实现方法。后面看了下用Python来绘制图表实现起来挺容易的,而且Python的学习成本低,语法之类的做过开发的稍微看下就知道怎么用,容易上手。具体实现的效果如下,CPU和内存采集的数据是独立进程的,内存分本文转载自:https://www.chenwenguan.com/python-draw-cpu-memory-chart/,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有。
在做性能监控的时候,如果能把监控的CPU和内存增长变化用图表展示出来会比较直观,花了点时间用 Python 实现了下,来看下怎么用Python绘制Android CPU和内存变化曲线,生成增长曲线图表的PNG图片。
一、实现效果
一开始想通过采集的CPU和内存数据,导出到Excel生成增长曲线图表。做了下调研,并没有比较好的实现方法。后面看了下用Python来绘制图表实现起来挺容易的,而且Python的学习成本低,语法之类的做过开发的稍微看下就知道怎么用,容易上手。
具体实现的效果如下,CPU和内存采集的数据是独立进程的,内存分三块数据,应用总内存,Native内存和Dalvik内存,如果存在内存泄漏,要么在Native,要么在Dalvik,从图表增长曲线上很容易看出来。
二、具体逻辑实现详解
1. CPU图表的Python实现
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import json
import sys
import time
import traceback
def startDump():
try:
cpuData = json.loads(sys.argv\[1\])imagePath = sys.argv\[2\]cpuRateArray = \[\]timeArray = \[\]for cpuItem in cpuData: cpuRateArray.append(float(cpuItem\["cpuRate"\])) timeArray.append((float(float(cpuItem\["time"\]) - float(cpuData\[0\]\["time"\]))/1000))plt.title("Monitor Cpu Rate")plt.figure(figsize=(10, 8))plt.tight\_layout()plt.plot(timeArray, cpuRateArray, c='red', label='Process CPU')plt.ylabel("CPURate (%)", fontsize=12)plt.xlabel("TimeRange:" + formatTime(float(cpuData\[0\]\["time"\])) + ' - ' + formatTime(float(cpuData\[len(cpuData) -1\]\["time"\])), fontsize=10)plt.legend()plt.savefig(imagePath)
except Exception:
print 'exeption occur:' + traceback.format\_exc()
def formatTime(timeMillis):
timeSeconds = float(timeMillis/1000)
timelocal = time.localtime(timeSeconds)
timeFormat = time.strftime("%Y-%m-%d %H:%M:%S", timelocal)
return timeFormat
if __name__ == '__main__':
startDump()
2. 内存图表的Python实现
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import json
import sys
import time
import traceback
def startDump():
try:
memoryData = json.loads(sys.argv\[1\])imagePath = sys.argv\[2\]totalPssArray = \[\]nativePssArray = \[\]dalvikPssArray = \[\]timeArray = \[\]for memoryItem in memoryData: totalPssArray.append(float(memoryItem\["totalPss"\])/1024) nativePssArray.append(float(memoryItem\["nativePss"\])/1024) dalvikPssArray.append(float(memoryItem\["dalvikPss"\])/1024) timeArray.append((float(float(memoryItem\["time"\]) - float(memoryData\[0\]\["time"\]))/1000))plt.title("Monitor Memory")plt.figure(figsize=(10, 8))plt.tight\_layout()plt.plot(timeArray, totalPssArray, c='red', label='Total Memory')plt.plot(timeArray, nativePssArray, c='yellow', label='Native Memory')plt.plot(timeArray, dalvikPssArray, c='blue', label='Dalvik Memory')plt.ylabel("Memory (MB)", fontsize=12)plt.xlabel("TimeRange:" + formatTime(float(memoryData\[0\]\["time"\])) + ' - ' + formatTime(float(memoryData\[len(memoryData) -1\]\["time"\])), fontsize=10)plt.legend()plt.savefig(imagePath)
except Exception:
print 'exeption occur:' + traceback.format\_exc()
def formatTime(timeMillis):
timeSeconds = float(timeMillis/1000)
timelocal = time.localtime(timeSeconds)
timeFormat = time.strftime("%Y-%m-%d %H:%M:%S", timelocal)
return timeFormat
if __name__ == '__main__':
startDump()
3. 实现说明
脚本传入的参数有两个,一个是监控的 JSON 数据字符串值sys.argv[1],一个是保存的图片文件完整路径sys.argv[2]。关于传入的 JSON参数字符串值需要加上单引号修饰 ,否则会导致解析异常,传入的JSON参数也 不能直接是JSON对象 ,必须转化成字符串,示例调用命令如下:
python dump_chart.py '<JSONString>' cpu_chart.png
1)采样CPU示例数据,time是设备的系统时间戳,CPU的占用率的计算可以查看前面写的: Android 性能监控之CPU监控
[
{
"time": "1589435564442.279053",
"cpuRate": "2.17"
},
{
"time": "1589435565655.333008",
"cpuRate": "3.26"
},
{
"time": "1589435566954.137939",
"cpuRate": "2.52"
},
...
]
2)采样内存示例数据,totalPss、nativePss和dalvikPss值都是从dumpsys meminfo输出的应用内存信息中截取出来的原始数据,对应“TOTAL”、“ Native Heap“、” Dalvik Heap“字段的Pss Total值。内存信息的监控获取参考:Android 性能监控之内存监控
[
{
"time": "1589636256923.429932",
"totalPss": 177804,
"nativePss": 27922,
"dalvikPss": 10212
},
{
"time": "1589636258236.298096",
"totalPss": 178021,
"nativePss": 27850,
"dalvikPss": 9990
},
{
"time": "1589636259525.219971",
"totalPss": 177899,
"nativePss": 27742,
"dalvikPss": 9990
},
...
]
三、实现过程中遇到的问题
1. load方法使用错误
json.load ()方法使用错误,应该替换成 json.loads ()。
exeption occur:Traceback (most recent call last):
File "*******", line 11, in startDump
memoryData = json.load(sys.argv\[1\])
File "/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 287, in load
return loads(fp.read(),
AttributeError: 'str' object has no attribute 'read'
2. JSON字符串对象入参问题
File "******", line 11, in startDump
memoryData = json.loads(sys.argv\[1\])
File "/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 339, in loads
return \_default\_decoder.decode(s)
File "/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py", line 364, in decode
obj, end = self.raw\_decode(s, idx=\_w(s, 0).end())
File "/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py", line 382, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
针对Python脚本调用,JSON字符串对象作为入参,传入的JSON字符串对象 需要加单引号处理,比如在 JavaScript中示例处理如下:
'\'' + JSON.stringify(cpuRateJSON) + '\''
3. Python需要显示声明参数的类型
在Python中需要指明参数的类型,解析获取到JSON对象中的值之后,Python并不会根据参数来判断是什么类型,需要指明要转化的对象参数类型,比如把系统时间戳转化成float值类型:float(memoryData[0][“time”])
Traceback (most recent call last):
File "*******", line 21, in startDump
timeArray.append(timeStamp(memoryItem\["time"\]))
File "*******", line 36, in timeStamp
timeStamp = float(timeNum/1000)
TypeError: unsupported operand type(s) for /: 'unicode' and 'int'
4. 编码导致的异常
SyntaxError: Non-ASCII character '\xe5' in file ******* on line 24, but no encoding declared; see http://python.org/dev/peps/pe... for details
如果运行之后报如下的异常,说明是编码出问题,在脚本开头加上编码类型声明:
!usr/bin/python
-*- coding: utf-8 -*-
5. 保存的文件格式限制
plt.savefig ( image_path ) 保存的文件格式只能是 eps , pdf , pgf , png , ps , raw , rgba , svg , svgz 这些,不支持jpg 图片的保存。