import json import argparse import os.path import platform import matplotlib # 设置后端以避免GUI相关问题 matplotlib.use('Agg') import matplotlib.pyplot as plt import numpy as np from datetime import datetime from matplotlib.font_manager import FontProperties from matplotlib.patches import Rectangle import matplotlib.patches as mpatches # 设置更现代化的matplotlib样式 try: plt.style.use('seaborn-v0_8-whitegrid') except: try: plt.style.use('seaborn-whitegrid') except: pass # 如果都没有,使用默认样式 # 定义常量 TOP_FUNCTION_COUNT = 15 # 显示的函数数量 def setup_chinese_font_support(): """根据操作系统环境设置中文字体支持""" system = platform.system().lower() if system == 'windows': # Windows系统字体设置 plt.rcParams['font.sans-serif'] = ['SimHei', 'FangSong', 'Arial Unicode MS'] plt.rcParams['axes.unicode_minus'] = False elif system == 'linux': # 检查是否在WSL环境中 try: with open('/proc/version', 'r') as f: version_info = f.read().lower() is_wsl = 'microsoft' in version_info or 'wsl' in version_info except: is_wsl = False if is_wsl: # WSL环境下的字体设置 # 尝试使用Windows字体目录中的字体 possible_fonts = [ '/mnt/c/Windows/Fonts/msyh.ttc', # 微软雅黑 '/mnt/c/Windows/Fonts/simsun.ttc', # 宋体 '/mnt/c/Windows/Fonts/simhei.ttf', # 黑体 ] # 查找可用的中文字体 available_fonts = [] for font_path in possible_fonts: if os.path.exists(font_path): available_fonts.append(font_path) # 如果找到了Windows字体,使用它 if available_fonts: plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimSun', 'SimHei'] else: # 使用系统默认字体 plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Bitstream Vera Sans', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'sans-serif'] else: # 普通Linux环境 plt.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei', 'DejaVu Sans', 'Bitstream Vera Sans', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'sans-serif'] plt.rcParams['axes.unicode_minus'] = False else: # 其他系统使用默认设置 plt.rcParams['axes.unicode_minus'] = False # 在导入后立即设置字体支持 setup_chinese_font_support() # 设置全局样式参数 plt.rcParams['figure.facecolor'] = 'white' plt.rcParams['axes.facecolor'] = '#fafafa' plt.rcParams['axes.edgecolor'] = '#e0e0e0' plt.rcParams['axes.linewidth'] = 0.8 plt.rcParams['grid.alpha'] = 0.5 plt.rcParams['grid.color'] = '#e0e0e0' def read_json(file_path): with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) return data def plot_cprofile_table(ax, cprofile_data): """绘制cProfile详细信息表格""" if not cprofile_data or 'points' not in cprofile_data: ax.text(0.5, 0.5, '无cProfile数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('函数运行详细信息', fontsize=14, fontweight='bold', pad=20) return # 只取前15个最耗时的函数 fun_num = TOP_FUNCTION_COUNT points = cprofile_data['points'][:fun_num] # 准备表格数据 table_data = [] for point in points: # 简化函数名显示 func_name = point['function'][1:-1] func_name = func_name.split(',')[-1].strip() func_name = func_name[1:-1] # 限制函数名长度,但保留足够空间显示完整信息 func_name_display = func_name[:35] + '...' if len(func_name) > 35 else func_name # 将数据点添加到表格 table_data.append([ func_name_display, point['call_count'], f"{point['total_time']:.6f}", f"{point['cumulative_time']:.6f}" ]) # 创建表格 columns = ['函数名称', '调用次数', '总时间(s)', '累计时间(s)'] table = ax.table(cellText=table_data, colLabels=columns, cellLoc='center', loc='center', bbox=[0, 0, 1, 1]) table.auto_set_font_size(False) table.set_fontsize(9) table.scale(1, 1.5) # 设置表格样式 for i in range(len(columns)): table[(0, i)].set_facecolor('#4a86e8') table[(0, i)].set_text_props(weight='bold', color='white') # 为数据行设置交替颜色 for i in range(1, len(table_data)+1): for j in range(len(columns)): if i % 2 == 0: table[(i, j)].set_facecolor('#f2f2f2') else: table[(i, j)].set_facecolor('white') # 根据函数数量动态调整标题距离 # 每个函数行大约需要8个单位的高度,加上基础padding pad_value = 8 + len(points) * 3 ax.set_title(f'函数运行详细信息 (Top {fun_num} 函数)', fontsize=14, fontweight='bold', pad=pad_value) # 坐标轴隐藏 ax.set_xticks([]) ax.set_yticks([]) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['bottom'].set_visible(False) ax.spines['left'].set_visible(False) def plot_cprofile_bar_chart(ax, cprofile_data): """绘制cProfile总时间柱状图""" if not cprofile_data or 'points' not in cprofile_data: ax.text(0.5, 0.5, '无cProfile数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('函数运行总时间', fontsize=14, fontweight='bold', pad=20) return # 只取前N个最耗时的函数 fun_num = TOP_FUNCTION_COUNT points = cprofile_data['points'][:fun_num] # 如果没有数据点,直接返回 if not points: ax.text(0.5, 0.5, '无cProfile数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('函数运行总时间', fontsize=14, fontweight='bold', pad=20) return # 提取函数名和总时间 functions = [] total_times = [] for point in points: # 简化函数名显示 func_name = point['function'][1:-1] func_name = func_name.split(',')[-1].strip() func_name = func_name[1:-1] # 限制函数名长度 func_name_display = func_name[:35] + '...' if len(func_name) > 35 else func_name functions.append(func_name_display) total_times.append(point['total_time']) # 创建水平柱状图,使用渐变色彩,更适合长函数名显示 y_pos = np.arange(len(functions)) colors = plt.cm.plasma(np.linspace(0.1, 0.9, len(functions))) # 使用plasma色彩映射 bars = ax.barh(y_pos, total_times, color=colors, height=0.8, edgecolor='white', linewidth=0.5) # 设置坐标轴 ax.set_yticks(y_pos) ax.set_yticklabels(functions, fontsize=9) ax.set_xlabel('总时间 (秒)', fontsize=12, fontweight='bold') ax.set_title(f'函数运行总时间 (Top {fun_num} 函数)', fontsize=14, fontweight='bold', pad=20) # 改善网格线样式 ax.grid(axis='x', alpha=0.4, linestyle='-', linewidth=0.5) ax.set_axisbelow(True) # 在柱状图上添加数值标签,优化显示效果 for i, (bar, time) in enumerate(zip(bars, total_times)): width = bar.get_width() ax.text(width + max(total_times)*0.01, bar.get_y() + bar.get_height()/2., f'{time:.6f}', ha='left', va='center', fontsize=8, fontweight='bold', color='#333333') # 添加颜色条以显示颜色映射含义 if total_times: # 确保有数据才添加颜色条 sm = plt.cm.ScalarMappable(cmap=plt.cm.plasma, norm=plt.Normalize(vmin=min(total_times), vmax=max(total_times))) sm.set_array([]) cbar = plt.colorbar(sm, ax=ax, shrink=0.8, aspect=20, pad=0.02) cbar.set_label('执行时间 (秒)', fontsize=10, fontweight='bold') # 添加边框 for spine in ax.spines.values(): spine.set_linewidth(0.8) spine.set_color('#cccccc') def plot_memory_data(ax, memory_data): """绘制内存使用数据""" if not memory_data or 'points' not in memory_data: ax.text(0.5, 0.5, '无内存数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('memory_profiler 内存使用情况', fontsize=14, fontweight='bold', pad=20) return timestamps = [point['timestamp'] for point in memory_data['points']] memory_values = [point['memory'] for point in memory_data['points']] # 检查是否有数据 if not timestamps or not memory_values: ax.text(0.5, 0.5, '无内存数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('memory_profiler 内存使用情况', fontsize=14, fontweight='bold', pad=20) return # 转换时间戳为相对时间(秒) start_time = timestamps[0] if timestamps else 0 relative_times = [t - start_time for t in timestamps] # 查找内存峰值及其位置 max_memory = max(memory_values) if memory_values else 0 max_index = memory_values.index(max_memory) if memory_values else 0 max_time = relative_times[max_index] if relative_times else 0 # 计算平均内存使用量 avg_memory = np.mean(memory_values) if memory_values else 0 # 绘制内存使用情况曲线,使用更平滑的线条 ax.plot(relative_times, memory_values, color='#2e7d32', linewidth=2.5, marker='o', markersize=5, markerfacecolor='#4caf50', markeredgecolor='white', markeredgewidth=1.5, markevery=slice(0, len(relative_times), max(1, len(relative_times)//20))) # 控制标记密度 # 在峰值点添加特殊标记 ax.scatter(max_time, max_memory, s=150, color='#d32f2f', marker='o', edgecolor='white', linewidth=2, zorder=5, label=f'峰值: {max_memory:.2f} MB') # 添加平均线 ax.axhline(y=avg_memory, color='#1976d2', linestyle='--', linewidth=1.5, label=f'平均: {avg_memory:.2f} MB', alpha=0.8) # 添加填充区域以增强视觉效果 ax.fill_between(relative_times, memory_values, alpha=0.4, color='#4caf50') # 添加统计信息文本框 textstr = f'峰值: {max_memory:.2f} MB\n平均: {avg_memory:.2f} MB' props = dict(boxstyle='round', facecolor='#e3f2fd', alpha=0.8) ax.text(0.03, 0.97, textstr, transform=ax.transAxes, fontsize=10, verticalalignment='top', bbox=props, fontweight='bold') ax.set_xlabel('时间 (秒)', fontsize=12, fontweight='bold') ax.set_ylabel('内存使用量 (MB)', fontsize=12, fontweight='bold') ax.set_title(f'进程内存使用情况 (峰值: {max_memory:.2f} MB at {max_time:.1f}s)', fontsize=14, fontweight='bold', pad=20) ax.grid(True, alpha=0.4) ax.legend(loc='upper right', frameon=True, fancybox=True, shadow=True, ncol=1) def plot_psutil_data(ax, psutil_data): """绘制psutil系统资源数据""" if not psutil_data or 'points' not in psutil_data: ax.text(0.5, 0.5, '无进程资源数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('进程资源使用情况', fontsize=14, fontweight='bold', pad=20) return points = psutil_data['points'] if not points: ax.text(0.5, 0.5, '无进程资源数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('进程资源使用情况', fontsize=14, fontweight='bold', pad=20) return # 提取数据并转换时间字符串为时间戳 timestamps = [] rss_values = [] vms_values = [] cpu_values = [] mem_pct_values = [] for point in points: # 解析时间字符串并转换为时间戳 try: dt = datetime.strptime(point['now_time'], '%Y-%m-%d %H:%M:%S') timestamps.append(dt.timestamp()) except ValueError: # 如果解析失败,使用索引作为后备 timestamps.append(len(timestamps)) # 转换时间戳为相对时间(秒) start_time = timestamps[0] if timestamps else 0 relative_times = [t - start_time for t in timestamps] for point in points: # 解析RSS内存值 rss_str = point['rss'].replace('MB', '') try: rss_values.append(float(rss_str)) except ValueError: rss_values.append(0) # 解析VMS内存值 vms_str = point['vms'].replace('MB', '') try: vms_values.append(float(vms_str)) except ValueError: vms_values.append(0) # 解析CPU百分比 cpu_str = point['cpu_pct'].replace('%', '') try: cpu_values.append(float(cpu_str)) except ValueError: cpu_values.append(0) # 解析内存占用率 mem_pct_str = point['mem_pct'].replace('%', '') try: mem_pct_values.append(float(mem_pct_str)) except ValueError: mem_pct_values.append(0) # 检查是否有有效数据 if not any([rss_values, vms_values, cpu_values, mem_pct_values]): ax.text(0.5, 0.5, '无有效进程资源数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('进程资源使用情况', fontsize=14, fontweight='bold', pad=20) return # 计算统计数据 avg_cpu = np.mean(cpu_values) if cpu_values else 0 max_cpu = max(cpu_values) if cpu_values else 0 avg_rss = np.mean(rss_values) if rss_values else 0 max_rss = max(rss_values) if rss_values else 0 # 绘制双轴图 ax2 = ax.twinx() lines = [] # 存储线条引用 # 物理内存使用量(蓝色) if rss_values: line1, = ax.plot(relative_times, rss_values, marker='o', markersize=5, linewidth=2.5, color='#1976d2', label='物理内存(RSS)', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) lines.append(line1) # 添加平均线 ax.axhline(y=avg_rss, color='#1976d2', linestyle=':', linewidth=1.5, alpha=0.7, label=f'平均RSS: {avg_rss:.1f} MB') # 虚拟内存使用量(绿色) if vms_values: line3, = ax.plot(relative_times, vms_values, marker='^', markersize=6, linewidth=2.5, color='#388e3c', label='虚拟内存(VMS)', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) lines.append(line3) ax.set_xlabel('时间 (秒)', fontsize=12, fontweight='bold') ax.set_ylabel('内存使用量 (MB)', fontsize=12, fontweight='bold', color='#1976d2') ax.tick_params(axis='y', labelcolor='#1976d2') line2 = None line4 = None # CPU使用率(红色)和内存占用率(紫色) if cpu_values: line2, = ax2.plot(relative_times, cpu_values, marker='s', markersize=5, linewidth=2.5, color='#d32f2f', label='CPU使用率', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) lines.append(line2) # 添加CPU平均线和峰值标记 ax2.axhline(y=avg_cpu, color='#d32f2f', linestyle=':', linewidth=1.5, alpha=0.7, label=f'平均CPU: {avg_cpu:.1f}%') # 寻找CPU峰值点并标记 if cpu_values: max_cpu_idx = cpu_values.index(max_cpu) max_cpu_time = relative_times[max_cpu_idx] ax2.scatter(max_cpu_time, max_cpu, s=120, color='#d32f2f', marker='*', edgecolor='white', linewidth=1, zorder=5) if mem_pct_values: line4, = ax2.plot(relative_times, mem_pct_values, marker='d', markersize=6, linewidth=2.5, color='#7b1fa2', label='内存占用率', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) lines.append(line4) ax2.set_ylabel('百分比 (%)', fontsize=12, fontweight='bold', color='#d32f2f') ax2.tick_params(axis='y', labelcolor='#d32f2f') # 添加统计信息文本框 textstr = f'CPU峰值: {max_cpu:.1f}%\n内存峰值: {max_rss:.1f} MB' props = dict(boxstyle='round', facecolor='#ffebee', alpha=0.8) # 将统计信息放在左上角 ax.text(0.03, 0.97, textstr, transform=ax.transAxes, fontsize=10, verticalalignment='top', bbox=props, fontweight='bold') # 合并图例,放置在右侧避免与统计信息重叠 if lines: labels = [l.get_label() for l in lines] # 将图例放在左上角,但在统计信息文本框的下方 ax.legend(lines, labels, loc='upper left', frameon=True, fancybox=True, shadow=True, ncol=1, bbox_to_anchor=(0.02, 0.85)) # 调整bbox_to_anchor来控制具体位置 ax.set_title('进程资源使用情况', fontsize=14, fontweight='bold', pad=20) ax.grid(True, alpha=0.4) def plot_network_data(ax, psutil_data): """绘制网络使用数据""" if not psutil_data or 'points' not in psutil_data: ax.text(0.5, 0.5, '无网络数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('系统网络使用情况', fontsize=14, fontweight='bold', pad=20) return points = psutil_data['points'] if not points: ax.text(0.5, 0.5, '无网络数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('系统网络使用情况', fontsize=14, fontweight='bold', pad=20) return # 提取数据并转换时间字符串为时间戳 from datetime import datetime timestamps = [] net_recv_values = [] net_send_values = [] for point in points: # 解析时间字符串并转换为时间戳 try: dt = datetime.strptime(point['now_time'], '%Y-%m-%d %H:%M:%S') timestamps.append(dt.timestamp()) except ValueError: # 如果解析失败,使用索引作为后备 timestamps.append(len(timestamps)) # 转换为相对时间(秒) start_time = timestamps[0] if timestamps else 0 relative_times = [t - start_time for t in timestamps] for point in points: # 解析网络接收值 net_recv_str = point['sys_net_recv'].replace('MB/s', '').replace('MB', '').replace(' ', '') try: net_recv_values.append(float(net_recv_str)) except ValueError: net_recv_values.append(0) # 解析网络发送值 net_send_str = point['sys_net_send'].replace('MB/s', '').replace('MB', '').replace(' ', '') try: net_send_values.append(float(net_send_str)) except ValueError: net_send_values.append(0) # 检查是否有有效数据 if not net_recv_values and not net_send_values: ax.text(0.5, 0.5, '无有效网络数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('系统网络使用情况', fontsize=14, fontweight='bold', pad=20) return # 计算统计数据 avg_recv = np.mean(net_recv_values) if net_recv_values else 0 avg_send = np.mean(net_send_values) if net_send_values else 0 max_recv = max(net_recv_values) if net_recv_values else 0 max_send = max(net_send_values) if net_send_values else 0 # 绘制网络使用情况,使用面积图增强视觉效果 if net_recv_values: ax.plot(relative_times, net_recv_values, marker='o', markersize=5, linewidth=2.5, color='#1976d2', label='网络接收', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) ax.fill_between(relative_times, net_recv_values, alpha=0.3, color='#1976d2') # 添加平均线 ax.axhline(y=avg_recv, color='#1976d2', linestyle=':', linewidth=1.5, alpha=0.7, label=f'平均接收: {avg_recv:.2f} MB/s') if net_send_values: ax.plot(relative_times, net_send_values, marker='s', markersize=5, linewidth=2.5, color='#ff9800', label='网络发送', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) ax.fill_between(relative_times, net_send_values, alpha=0.3, color='#ff9800') # 添加平均线 ax.axhline(y=avg_send, color='#ff9800', linestyle=':', linewidth=1.5, alpha=0.7, label=f'平均发送: {avg_send:.2f} MB/s') # 添加统计信息文本框 textstr = f'接收峰值: {max_recv:.2f} MB/s\n发送峰值: {max_send:.2f} MB/s' props = dict(boxstyle='round', facecolor='#e3f2fd', alpha=0.8) ax.text(0.03, 0.97, textstr, transform=ax.transAxes, fontsize=10, verticalalignment='top', bbox=props, fontweight='bold') ax.set_xlabel('时间 (秒)', fontsize=12, fontweight='bold') ax.set_ylabel('速率 (MB/s)', fontsize=12, fontweight='bold') ax.set_title('系统网络使用情况', fontsize=14, fontweight='bold', pad=20) ax.grid(True, alpha=0.4) ax.legend(loc='upper right', frameon=True, fancybox=True, shadow=True, ncol=1) def plot_disk_io_data(ax, psutil_data): """绘制进程IO数据""" if not psutil_data or 'points' not in psutil_data: ax.text(0.5, 0.5, '无进程IO数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('进程IO情况', fontsize=14, fontweight='bold', pad=20) return points = psutil_data['points'] if not points: ax.text(0.5, 0.5, '无进程IO数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('进程IO情况', fontsize=14, fontweight='bold', pad=20) return # 提取数据并转换时间字符串为时间戳 timestamps = [] disk_read_values = [] disk_write_values = [] for point in points: # 解析时间字符串并转换为时间戳 try: dt = datetime.strptime(point['now_time'], '%Y-%m-%d %H:%M:%S') timestamps.append(dt.timestamp()) except ValueError: # 如果解析失败,使用索引作为后备 timestamps.append(len(timestamps)) # 转换为相对时间(秒) start_time = timestamps[0] if timestamps else 0 relative_times = [t - start_time for t in timestamps] for point in points: # 解析磁盘读取值 disk_read_str = point['disk_read'].replace('MB/s', '').replace('MB', '').replace(' ', '') try: disk_read_values.append(float(disk_read_str)) except ValueError: disk_read_values.append(0) # 解析磁盘写入值 disk_write_str = point['disk_write'].replace('MB/s', '').replace('MB', '').replace(' ', '') try: disk_write_values.append(float(disk_write_str)) except ValueError: disk_write_values.append(0) # 检查是否有有效数据 if not disk_read_values and not disk_write_values: ax.text(0.5, 0.5, '无有效IO数据', ha='center', va='center', transform=ax.transAxes, fontsize=12, color='#666666') ax.set_title('进程IO情况', fontsize=14, fontweight='bold', pad=20) return # 计算统计数据 avg_read = np.mean(disk_read_values) if disk_read_values else 0 avg_write = np.mean(disk_write_values) if disk_write_values else 0 max_read = max(disk_read_values) if disk_read_values else 0 max_write = max(disk_write_values) if disk_write_values else 0 # 绘制磁盘IO情况,使用不同样式的线条区分读写操作 if disk_read_values: ax.plot(relative_times, disk_read_values, marker='o', markersize=5, linewidth=2.5, color='#7b1fa2', label='磁盘读取', linestyle='-', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) ax.fill_between(relative_times, disk_read_values, alpha=0.3, color='#7b1fa2') # 添加平均线 ax.axhline(y=avg_read, color='#7b1fa2', linestyle=':', linewidth=1.5, alpha=0.7, label=f'平均读取: {avg_read:.2f} MB/s') if disk_write_values: ax.plot(relative_times, disk_write_values, marker='s', markersize=5, linewidth=2.5, color='#f57c00', label='磁盘写入', linestyle='--', markevery=slice(0, len(relative_times), max(1, len(relative_times)//15))) ax.fill_between(relative_times, disk_write_values, alpha=0.3, color='#f57c00') # 添加平均线 ax.axhline(y=avg_write, color='#f57c00', linestyle=':', linewidth=1.5, alpha=0.7, label=f'平均写入: {avg_write:.2f} MB/s') # 添加统计信息文本框 textstr = f'读取峰值: {max_read:.2f} MB/s\n写入峰值: {max_write:.2f} MB/s' props = dict(boxstyle='round', facecolor='#f3e5f5', alpha=0.8) ax.text(0.03, 0.97, textstr, transform=ax.transAxes, fontsize=10, verticalalignment='top', bbox=props, fontweight='bold') ax.set_xlabel('时间 (秒)', fontsize=12, fontweight='bold') ax.set_ylabel('速率 (MB/s)', fontsize=12, fontweight='bold') ax.set_title('进程IO情况', fontsize=14, fontweight='bold', pad=20) ax.grid(True, alpha=0.4) ax.legend(loc='upper right', frameon=True, fancybox=True, shadow=True, ncol=1) def main(): parser = argparse.ArgumentParser(description='绘制性能分析结果图') # parser.add_argument('--input','-i', required=True) # parser.add_argument('--output', '-o',required=True) parser.add_argument('--input','-i', default='./results/performance_analysis_report.json') parser.add_argument('--output', '-o', default='./results/performance_analysis_report.png') args = parser.parse_args() if not os.path.exists(args.input): raise ValueError(f'输入文件不存在:{args.input}') # 读取json数据 data = read_json(args.input) # 创建更大的图表 fig = plt.figure(figsize=(24, 16)) fig.suptitle('Python 性能分析报告', fontsize=24, fontweight='bold', y=0.95) # 设置图表背景色 fig.patch.set_facecolor('white') # 添加水印效果 fig.text(0.5, 0.5, 'Python Performance Report', fontsize=40, color='gray', ha='center', va='center', alpha=0.05, rotation=45) # 创建子图,增加每行的高度 # cProfile 表格 ax1 = plt.subplot2grid((10, 2), (0, 0), rowspan=3, colspan=1) # cProfile 柱状图 ax2 = plt.subplot2grid((10, 2), (0, 1), rowspan=3,colspan=1) # 内存使用情况 ax3 = plt.subplot2grid((10, 2), (3, 0), rowspan=4,colspan=1) # 系统资源监控 ax4 = plt.subplot2grid((10, 2), (3, 1), rowspan=4,colspan=1) # 网络使用情况 ax5 = plt.subplot2grid((10, 2), (7, 0), rowspan=3, colspan=1) # 磁盘读写情况 ax6 = plt.subplot2grid((10, 2), (7, 1), rowspan=3, colspan=1) # 绘制各个子图 plot_cprofile_table(ax1, data.get('analysis_results', {}).get('cprofile', {})) plot_cprofile_bar_chart(ax2, data.get('analysis_results', {}).get('cprofile', {})) plot_memory_data(ax3, data.get('analysis_results', {}).get('memory_profile', {})) plot_psutil_data(ax4, data.get('analysis_results', {}).get('psutil', {})) plot_network_data(ax5, data.get('analysis_results', {}).get('psutil', {})) plot_disk_io_data(ax6, data.get('analysis_results', {}).get('psutil', {})) # 调整布局 plt.tight_layout(rect=(0, 0.01, 1, 0.93)) # 减少底部边距以减少空白区域 # 添加页脚信息 fig.text(0.95, 0.005, f'生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}', fontsize=10, ha='right', va='bottom', color='#666666') fig.text(0.05, 0.005, 'Python性能分析工具', fontsize=10, ha='left', va='bottom', color='#666666') # 保存图像 plt.savefig(args.output, dpi=300, bbox_inches='tight', facecolor='white', edgecolor='none', orientation='landscape') print(f"性能分析图表已保存至: {args.output}") plt.close() # 关闭图形以释放内存 # plt.show() # 可选:显示图表 if __name__ == '__main__': main()