mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-02-09 02:09:17 +08:00
## 架构重构
- 全局变量消除,迁移至 Config/State 对象
- SMB 插件融合(smb/smb2/smbghost/smbinfo)
- 服务探测重构,实现 Nmap 风格 fallback 机制
- 输出系统重构,TXT 实时刷盘 + 双写机制
- i18n 框架升级至 go-i18n
## 性能优化
- 正则表达式预编译
- 内存优化 map[string]struct{}
- 并发指纹匹配
- SOCKS5 连接复用
- 滑动窗口调度 + 自适应线程池
## 新功能
- Web 管理界面
- 多格式 POC 适配(xray/afrog)
- 增强指纹库(3139条)
- Favicon hash 指纹识别
- 插件选择性编译(Build Tags)
- fscan-lab 靶场环境
- 默认端口扩展(62→133)
## 构建系统
- 添加 no_local tag 支持排除本地插件
- 多版本构建:fscan/fscan-nolocal/fscan-web
- CI 添加 snapshot 模式支持仅测试构建
## Bug 修复
- 修复 120+ 个问题,包括 RDP panic、批量扫描漏报、
JSON 输出格式、Redis 检测、Context 超时等
## 测试增强
- 单元测试覆盖率 74-100%
- 并发安全测试
- 集成测试(Web/端口/服务/SSH/ICMP)
227 lines
7.9 KiB
Python
227 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Go Benchmark 结果可视化脚本
|
|
生成柱状图展示各函数的性能指标
|
|
"""
|
|
|
|
import re
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
import sys
|
|
import os
|
|
|
|
# 设置中文字体
|
|
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'DejaVu Sans']
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
|
def parse_benchmark_results(filepath):
|
|
"""解析 Go benchmark 输出"""
|
|
results = []
|
|
|
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
for line in f:
|
|
# 匹配格式: BenchmarkXxx-24 123456 1.234 ns/op 123 B/op 12 allocs/op
|
|
match = re.match(
|
|
r'(Benchmark\w+)-\d+\s+(\d+)\s+([\d.]+)\s+(ns|µs|ms)/op(?:\s+([\d.]+)\s+B/op)?(?:\s+(\d+)\s+allocs/op)?',
|
|
line.strip()
|
|
)
|
|
if match:
|
|
name = match.group(1).replace('Benchmark', '')
|
|
ops = int(match.group(2))
|
|
time_val = float(match.group(3))
|
|
time_unit = match.group(4)
|
|
bytes_op = float(match.group(5)) if match.group(5) else 0
|
|
allocs_op = int(match.group(6)) if match.group(6) else 0
|
|
|
|
# 统一转换为 ns
|
|
if time_unit == 'µs':
|
|
time_ns = time_val * 1000
|
|
elif time_unit == 'ms':
|
|
time_ns = time_val * 1000000
|
|
else:
|
|
time_ns = time_val
|
|
|
|
results.append({
|
|
'name': name,
|
|
'ops': ops,
|
|
'time_ns': time_ns,
|
|
'time_val': time_val,
|
|
'time_unit': time_unit,
|
|
'bytes': bytes_op,
|
|
'allocs': allocs_op
|
|
})
|
|
|
|
return results
|
|
|
|
def format_time(ns):
|
|
"""格式化时间显示"""
|
|
if ns >= 1000000:
|
|
return f"{ns/1000000:.1f}ms"
|
|
elif ns >= 1000:
|
|
return f"{ns/1000:.1f}µs"
|
|
else:
|
|
return f"{ns:.1f}ns"
|
|
|
|
def format_bytes(b):
|
|
"""格式化内存显示"""
|
|
if b >= 1024*1024:
|
|
return f"{b/1024/1024:.1f}MB"
|
|
elif b >= 1024:
|
|
return f"{b/1024:.1f}KB"
|
|
else:
|
|
return f"{b:.0f}B"
|
|
|
|
def create_benchmark_charts(results, output_dir):
|
|
"""创建 benchmark 可视化图表"""
|
|
|
|
if not results:
|
|
print("没有找到 benchmark 结果")
|
|
return
|
|
|
|
# 按模块分组
|
|
core_funcs = [r for r in results if any(x in r['name'] for x in
|
|
['CheckSum', 'TCPDial', 'ResultCollector', 'FailedPort', 'Estimate', 'Calculate', 'BuildExclude', 'ArrayCount'])]
|
|
parser_funcs = [r for r in results if 'ParseIP' in r['name'] or 'ParsePort' in r['name']]
|
|
finger_funcs = [r for r in results if 'DecodePattern' in r['name']]
|
|
|
|
# 图1: 执行时间对比 (对数刻度)
|
|
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
|
|
fig.suptitle('Go Benchmark 性能分析', fontsize=14, fontweight='bold')
|
|
|
|
# 子图1: 所有函数执行时间
|
|
ax1 = axes[0, 0]
|
|
names = [r['name'][:20] for r in results]
|
|
times = [r['time_ns'] for r in results]
|
|
colors = ['#2ecc71' if t < 1000 else '#f39c12' if t < 100000 else '#e74c3c' for t in times]
|
|
|
|
bars = ax1.barh(names, times, color=colors)
|
|
ax1.set_xscale('log')
|
|
ax1.set_xlabel('执行时间 (ns, 对数刻度)')
|
|
ax1.set_title('各函数执行时间')
|
|
|
|
# 添加数值标签
|
|
for bar, t in zip(bars, times):
|
|
ax1.text(t * 1.5, bar.get_y() + bar.get_height()/2,
|
|
format_time(t), va='center', fontsize=8)
|
|
|
|
# 子图2: 内存分配
|
|
ax2 = axes[0, 1]
|
|
mem_results = [r for r in results if r['bytes'] > 0]
|
|
if mem_results:
|
|
names = [r['name'][:20] for r in mem_results]
|
|
mem = [r['bytes'] for r in mem_results]
|
|
colors = ['#3498db' if m < 1024 else '#9b59b6' if m < 100000 else '#e74c3c' for m in mem]
|
|
|
|
bars = ax2.barh(names, mem, color=colors)
|
|
ax2.set_xscale('log')
|
|
ax2.set_xlabel('内存分配 (B, 对数刻度)')
|
|
ax2.set_title('各函数内存分配')
|
|
|
|
for bar, m in zip(bars, mem):
|
|
ax2.text(m * 1.5, bar.get_y() + bar.get_height()/2,
|
|
format_bytes(m), va='center', fontsize=8)
|
|
|
|
# 子图3: 核心模块详细对比
|
|
ax3 = axes[1, 0]
|
|
if core_funcs:
|
|
names = [r['name'][:18] for r in core_funcs]
|
|
times = [r['time_ns'] for r in core_funcs]
|
|
|
|
x = np.arange(len(names))
|
|
bars = ax3.bar(x, times, color='#3498db')
|
|
ax3.set_xticks(x)
|
|
ax3.set_xticklabels(names, rotation=45, ha='right', fontsize=8)
|
|
ax3.set_ylabel('执行时间 (ns)')
|
|
ax3.set_title('核心模块 (core) 性能')
|
|
ax3.set_yscale('log')
|
|
|
|
for bar, t in zip(bars, times):
|
|
ax3.text(bar.get_x() + bar.get_width()/2, t * 1.2,
|
|
format_time(t), ha='center', fontsize=7)
|
|
|
|
# 子图4: 解析器模块详细对比
|
|
ax4 = axes[1, 1]
|
|
if parser_funcs:
|
|
names = [r['name'].replace('ParseIP', 'IP').replace('ParsePort', 'Port')[:15] for r in parser_funcs]
|
|
times = [r['time_ns'] for r in parser_funcs]
|
|
|
|
x = np.arange(len(names))
|
|
bars = ax4.bar(x, times, color='#e74c3c')
|
|
ax4.set_xticks(x)
|
|
ax4.set_xticklabels(names, rotation=45, ha='right', fontsize=8)
|
|
ax4.set_ylabel('执行时间 (ns)')
|
|
ax4.set_title('解析器模块 (parsers) 性能')
|
|
ax4.set_yscale('log')
|
|
|
|
for bar, t in zip(bars, times):
|
|
ax4.text(bar.get_x() + bar.get_width()/2, t * 1.2,
|
|
format_time(t), ha='center', fontsize=7)
|
|
|
|
plt.tight_layout()
|
|
|
|
output_path = os.path.join(output_dir, 'benchmark_chart.png')
|
|
plt.savefig(output_path, dpi=150, bbox_inches='tight')
|
|
print(f"图表已保存: {output_path}")
|
|
plt.close()
|
|
|
|
# 图2: 性能热力图 - 时间 vs 内存
|
|
fig2, ax = plt.subplots(figsize=(10, 6))
|
|
|
|
# 筛选有内存分配的结果
|
|
valid_results = [r for r in results if r['bytes'] > 0]
|
|
if valid_results:
|
|
times = [r['time_ns'] for r in valid_results]
|
|
mems = [r['bytes'] for r in valid_results]
|
|
names = [r['name'][:15] for r in valid_results]
|
|
|
|
scatter = ax.scatter(times, mems, s=100, c=range(len(valid_results)),
|
|
cmap='viridis', alpha=0.7, edgecolors='black')
|
|
|
|
ax.set_xscale('log')
|
|
ax.set_yscale('log')
|
|
ax.set_xlabel('执行时间 (ns)')
|
|
ax.set_ylabel('内存分配 (B)')
|
|
ax.set_title('性能-内存权衡分析')
|
|
|
|
# 添加标签
|
|
for i, (t, m, n) in enumerate(zip(times, mems, names)):
|
|
ax.annotate(n, (t, m), textcoords="offset points",
|
|
xytext=(5, 5), fontsize=7)
|
|
|
|
# 添加参考线
|
|
ax.axhline(y=1024, color='orange', linestyle='--', alpha=0.5, label='1KB')
|
|
ax.axhline(y=1024*1024, color='red', linestyle='--', alpha=0.5, label='1MB')
|
|
ax.axvline(x=1000, color='green', linestyle='--', alpha=0.5, label='1µs')
|
|
ax.axvline(x=1000000, color='purple', linestyle='--', alpha=0.5, label='1ms')
|
|
ax.legend(loc='upper left', fontsize=8)
|
|
|
|
output_path2 = os.path.join(output_dir, 'benchmark_tradeoff.png')
|
|
plt.savefig(output_path2, dpi=150, bbox_inches='tight')
|
|
print(f"图表已保存: {output_path2}")
|
|
plt.close()
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
# 默认路径
|
|
input_file = "results/benchmarks/benchmark_results.txt"
|
|
output_dir = "results/benchmarks"
|
|
else:
|
|
input_file = sys.argv[1]
|
|
output_dir = os.path.dirname(input_file) or "."
|
|
|
|
if not os.path.exists(input_file):
|
|
print(f"文件不存在: {input_file}")
|
|
sys.exit(1)
|
|
|
|
print(f"解析 benchmark 结果: {input_file}")
|
|
results = parse_benchmark_results(input_file)
|
|
print(f"找到 {len(results)} 个 benchmark 结果")
|
|
|
|
for r in results:
|
|
print(f" - {r['name']}: {format_time(r['time_ns'])}, {format_bytes(r['bytes'])}")
|
|
|
|
create_benchmark_charts(results, output_dir)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|