# !/usr/bin/python
# -*- coding: utf-8 -*-
"""
@File : tools_perf_monitor.py
@Time : 2022/2/26 11:50
@Author : HuangJunYi
@Version : 1.0
@Desc : None
"""
import os
import re
import time
import psutil
# import win32api
from ctypes import windll
from datetime import datetime
from numpy.core import mean
from pyecharts.charts import Line, Tab, Bar
import pyecharts.options as opts
import sys
sys.path.append("D:\perftestframework")
from Bases import bases_params
from Bases.bases_handlers import PsutilUtils
def tools_checkPerfMonitorVersion(process_name):
"""
获取当前监控客户端版本号
:return:
"""
# file_ver_dict = win32api.GetFileVersionInfo(ConfigUtils().utils_get_config(bases_params.config_path, 'getXDVersion', 'xd_path'), os.sep)
# 主版本号 / 次版本号,备注截取于 Win32API 参考手册
# 对于所有平台,低位字包含 Windows 的版本号,该字的低位字节以十六进制表示法指定主版本号,高位字节以十六进制表示法指定次版本(版本号)
# FileVersionLS / FileVersionMS 指文件版本
# product_ver_ms = file_ver_dict['ProductVersionMS']
# product_ver_ls = file_ver_dict['ProductVersionLS']
# current_ver = '%d.%d.%d.%d' % (win32api.HIWORD(product_ver_ms), win32api.LOWORD(product_ver_ms), win32api.HIWORD(product_ver_ls), win32api.LOWORD(product_ver_ls))
current_ver = ""
if process_name == "tdxw.exe":
current_ver = "通达信进程"
elif process_name == "xiadan.exe":
current_ver = "下单还原进程"
elif process_name == "hexin.exe":
current_ver = "下单精简进程"
return current_ver
def tools_perfMonitor(process_name, cases_name, print_interval=1, time_wait=30):
"""
性能监控
:param process_name: 目标进程名称
:param cases_name: 生成 log 的名称
:param print_interval: 打印监控数据间隔
:param time_wait: 设置了一个等待时间,期间内如果可以获取到目标进程,则继续监控,否则监控结束
:return:
"""
# 获取进程的标识符,默认False
process_flag = False
# 动态获取进程号,获取最长时间为:time_wait,期间内如果可以获取到目标进程,退出循环,代码继续往下执行
now_data = time.time()
while time.time() <= now_data + time_wait:
if PsutilUtils.utils_get_process_id(process_name):
# 获取进程号时,设置flag为True
process_flag = True
break
else:
time.sleep(0.5)
continue
# 若获取当前进程
if process_flag:
proc = psutil.Process(PsutilUtils.utils_get_process_id(process_name))
while True:
# 打印监控数据间隔
time.sleep(print_interval)
# 若获取进程,则开始监控,并写入数据
if PsutilUtils.utils_get_process_id(process_name) is not None:
# 工作设置内存
work_setup_memory = proc.memory_info().rss / 1024
# 提交大小内存
commit_size_memory = proc.memory_info().private / 1024
# 占用总内存百分比
perc_of_total_memory = round(float(proc.memory_percent()), 1)
# 进程句柄数
proc_handles = proc.num_handles()
# CPU 使用率
cpu_usage = proc.cpu_percent()
# GDI 对象
gdi_object = windll.user32.GetGuiResources(windll.kernel32.OpenProcess(5432, 0, PsutilUtils.utils_get_process_id(process_name)), 0)
windll.kernel32.CloseHandle(windll.kernel32.OpenProcess(5432, 0, PsutilUtils.utils_get_process_id(process_name)))
# IO 读
io_read = proc.io_counters().read_count
# IO 写
io_write = proc.io_counters().write_count
if os.path.exists(bases_params.perf_log_path + tools_checkPerfMonitorVersion(process_name)):
pass
else:
os.makedirs(bases_params.perf_log_path + tools_checkPerfMonitorVersion(process_name))
with open(bases_params.perf_log_path + tools_checkPerfMonitorVersion(process_name) + '/' + cases_name + '.log', 'a') as f:
f.write('时间: {}, 工作设置内存: {}, 提交大小内存: {}, 占用总内存百分比: {}, 进程句柄数: {}, CPU 使用率: {}, GDI 对象: {}, IO读: {}, IO写:{} \n'.format(datetime.now(), work_setup_memory, commit_size_memory, perc_of_total_memory, proc_handles, cpu_usage, gdi_object, io_read, io_write))
f.close()
# 进程再次消失时,退出监控
elif PsutilUtils.utils_get_process_id(process_name) is None:
break
def tools_perfDatasClean(ver, cases_name):
"""
性能数据清洗
:param ver: 版本号
:param cases_name: 生成 log 的名称
:return:
"""
# work_setup_memory: 工作设置内存
# commit_size_memory: 提交大小内存
# perc_of_total_memory: 占用总内存百分比
# proc_handles: 进程句柄数
# cpu_usage: CPU 使用率
# gdi_object: GDI 对象
work_setup_memory = []
commit_size_memory = []
perc_of_total_memory = []
proc_handles = []
cpu_usage = []
gdi_object = []
with open(bases_params.perf_log_path + ver + '\\' + cases_name + '.log', 'r') as f:
for line in f.readlines():
work_setup_memory.append(''.join(re.findall(r'工作设置内存: (.*), 提交大小内存', line)))
commit_size_memory.append(''.join(re.findall(r'提交大小内存: (.*), 占用总内存百分比', line)))
perc_of_total_memory.append(''.join(re.findall(r'占用总内存百分比: (.*), 进程句柄数', line)))
proc_handles.append(''.join(re.findall(r'进程句柄数: (.*), CPU 使用率', line)))
cpu_usage.append(''.join(re.findall(r'CPU 使用率: (.*), GDI 对象', line)))
gdi_object.append(''.join(re.findall(r'GDI 对象: (.*), IO读', line)))
datas = {"version": ver, "工作设置内存": work_setup_memory, "提交大小内存": commit_size_memory, "占用总内存百分比": perc_of_total_memory, "进程句柄数": proc_handles, "CPU 使用率": cpu_usage, "GDI 对象": gdi_object}
return datas
def tools_generatorChartsDatas(ver, cases_name):
"""
生成 charts 图数据
:param ver: 版本号(列表形式)
:param cases_name: 生成 log 的名称
:return:
"""
charts_datas = []
for ver in ver:
charts_datas.append(tools_perfDatasClean(ver, cases_name))
return charts_datas
def tools_generatorLineCharts(ver, cases_name, perf_types):
"""
生成性能变化趋势图
:param ver: 版本号(列表)
:param cases_name: log 名称
:param perf_types: 性能指标
:return:
"""
# 需要根据所取y轴数据取最大值来作x轴的数据
x = [i for i in range(1, max(len(datas[perf_types]) for datas in tools_generatorChartsDatas(ver, cases_name)))]
line = (
Line()
.add_xaxis(x)
.set_global_opts(
xaxis_opts=opts.AxisOpts(name='Times \n\n 时间间隔:1s'),
yaxis_opts=opts.AxisOpts(name='登录-单笔-批量-撤单-退出 \n\n Size')
)
)
for datas in tools_generatorChartsDatas(ver, cases_name):
_line = (
Line()
.add_xaxis(x)
.add_yaxis(datas['version'], datas[perf_types], is_symbol_show=False, is_smooth=True, is_connect_nones=True)
)
line.overlap(_line)
return line
def tools_generatorBarCharts(ver, cases_name, perf_types):
bar = (
Bar()
.add_xaxis(['MAX', 'MIN', 'AVG'])
)
for datas in tools_generatorChartsDatas(ver, cases_name):
params = []
for x in datas[perf_types]:
if x != '' and x != '0.0' and x != '0':
params.append(x)
MAX = max([float(x) for x in params])
MIN = min([float(x) for x in params])
AVG = round(mean([float(x) for x in params]), 2)
_bar = (
Bar()
.add_xaxis(['MAX', 'MIN', 'AVG'])
.add_yaxis(datas['version'], [MAX, MIN, AVG], gap='0%', bar_width='20%')
)
bar.overlap(_bar)
return bar
if __name__ == '__main__':
# process_name = "tdxw.exe"
# cases_name = "wt"
# process_name = "hexin.exe"
# cases_name = "wt"
# process_name = "xiadan.exe"
# cases_name = "wt"
# tools_perfMonitor(process_name, cases_name)
tab = Tab()
for types in ['工作设置内存', '提交大小内存', '占用总内存百分比', '进程句柄数', 'CPU 使用率', 'GDI 对象']:
# 生成 charts 图时,仅需要修改这里的参数即可
# 折线图
tab.add(tools_generatorLineCharts(['通达信进程', '下单还原进程', '下单精简进程'], 'wt', types), types)
# 柱形图
# tab.add(tools_generatorBarCharts(['通达信进程', '下单还原进程', '下单精简进程'], 'wt', types), types)
if os.path.exists(bases_params.perf_img_path):
tab.render(bases_params.perf_img_path + '\\' + 'TDXvsTHSzw.html')
else:
os.makedirs(bases_params.perf_img_path)
tab.render(bases_params.perf_img_path + '\\' + 'TDXvsTHSzw.html')