# performance_monitor **Repository Path**: pikailo/performance_monitor ## Basic Information - **Project Name**: performance_monitor - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-14 - **Last Updated**: 2026-06-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PerfMonitor 跨平台实时系统性能监控工具,提供 Web 界面。可选择运行中的进程,实时查看其 **CPU、内存、磁盘 I/O、GPU(利用率 / 显存)** 曲线;数据持久化到 SQLite,支持历史回放、导出与 `.log` 导入分析。 当前界面版本:**v1.3.1** ## 功能特性 ### 实时监控 - 可配置采样间隔(默认 1 秒),通过 **SSE** 推送至浏览器 - **CPU** 占用率(与 Windows **任务管理器 → 进程** 页一致:**每行 0~100%**、按逻辑核归一)、**内存 RSS**、**磁盘读/写速率**、**GPU % / 显存**(按进程,启动时自动探测后端) - 实时默认 **一行四列**:CPU | 内存 | 磁盘 | GPU;GPU 图可在 **利用率 / 显存** 间切换(单曲线) - 全部选中 PID 首次采到 **CPU + 内存** 后,再开始计时、写库与推流(界面显示「启动中 x/y」);磁盘速率可稍后补点,无 `io_counters` 的进程(如部分内核线程)按 0 处理 - 锁屏/休眠/采样间隔异常时自动重立差分基准,避免假 0 与误导性斜线 - 实时曲线最多保留约 **1 小时** 窗口数据;Y 轴按数据自适应(单进程 CPU 约 0~100%;合并时轴可高于 100%) - 图表支持 **行/列布局**、时间轴缩放(拖拽选区)、多图同步十字线 - 停止监控后 / 历史回放:曲线上标 **峰值、最低**;图顶条带按线显示 **均值、中位数**(内存 RSS/专用、磁盘读/写、GPU 利用率/显存各一行;CPU 单行) ### 进程列表 - 搜索(支持 `;` 分隔多关键词)、按 PID/名称/CPU/内存排序 - **CPU %** 与实时监控、任务管理器「进程」列一致:`psutil` 原始值按逻辑核数归一后 **0~100%** - 多选、全选当前列表;已选数量与重名进程摘要(如 `已选 15,chrome(6),node(3)`) - 列表通过 **SSE `list_tick`** 刷新,节奏对齐 **任务管理器(约 1 Hz)**:**CPU / 内存 / 磁盘** 约 **1 秒**;**GPU %** 独立线程约 **1 秒**(读 PDH 缓存,与 CPU enrich 解耦) - watch 超过上限时:**固定排行靠前的 PID**(不再每秒轮转),后端 **分片 enrich**(每 tick 最多 64 PID),约 2 秒扫完 120 个 - **实时监控进行中**:`watch` 置空,列表指标由 **`monitor_tick`(默认 1s)** 与曲线同步,不再走 `list_tick` - watch 集合变更时 **debounce PDH 全量重建**(默认 ≥8s),减轻 `list:tick_cpu` 被拖慢 - 按内存/专用工作集排序时,排行 API 使用短 TTL(约 1.5s)的机器级内存快照,减轻重复全量采样 ### 合并展示与选型策略 | 场景 | 行为 | |------|------| | 已选 ≤ 15 个不同进程名 | 自由勾选「合并同名」「汇总全部」或不合并 | | 已选 > 15 个不同进程名 | 须二选一:「合并同名」或「汇总全部」 | | 已选 > 50 个 PID | 强制「汇总全部」并锁定合并选项 | | 已选 > 100 个 PID | 可继续勾选,但 **「开始监控」禁用**(单次监控上限 100 PID) | - **合并同名**:同名进程 CPU/内存/磁盘按时间戳聚合 - **汇总全部**:所有选中 PID 汇总为一组曲线 - 监控运行中或 **停止后** 均可切换合并方式(基于内存中的 PID 日志回放,带加载遮罩,避免图表闪烁) 阈值可在 `static/js/ui_limits.js` 中统一修改。 ### 历史会话 - 从 SQLite 加载历史会话列表,点击回放 CPU/内存/磁盘/GPU 曲线 - 支持「合并同名 / 汇总全部」切换(指标缓存,无需重复请求) - **分档加载(精度优先)**:≤12h 且行数适中可一次 bulk 全量;12h~48h 默认最近 6h 原始点;>48h(含 7×24h)默认最近 2h + 分块拉取;缩放时间轴时按窗补载 raw - 可配置外部历史库路径、删除单条/全部会话 - **导出** 会话或实时数据为 `.log`;**导入** `.log` 离线查看 - 压测库:`python scripts/seed_history_db.py --hours 168 --pids 20` ### 其他 - 深色 Web UI(macOS 风格),Chart.js v4 内置,无需 CDN - 推荐 **waitress** 作为生产 WSGI 服务器;未安装时回退 Flask 开发服务器 - 基于 **psutil**,支持 Windows、Linux、macOS ## 环境要求 - Python **3.9+** - 依赖见 `requirements.txt`: ``` psutil>=5.8.0 Flask>=3.0.0 waitress>=3.0.0 pywin32>=306; platform_system == "Windows" # PDH 进程 GPU(推荐,替代 PowerShell) nvidia-ml-py>=12.0.0 # NVIDIA 按进程 GPU(可选) darwin-perf>=1.1.0 # macOS Apple Silicon 按进程 GPU(可选) pyamdgpuinfo>=2.1.0 # Linux AMD 按进程 GPU(可选) ``` GPU 后端在 `monitor/platform_gpu.py` 启动时自动探测: - **Windows**:`win_perf`(进程内 **PDH** 采样 `GPU Engine`,需 `pywin32`;与任务管理器同源;**Intel / AMD / NVIDIA 均可用**) - **GPU % 说明**:低负载时 PDH 可能长期为 **0%**,任务管理器仍可能显示约 **1%**(平滑/归因不同);无 `GPU Engine` 计数器的子进程在列表中显示 **`-`**(`gpu_util_tracked=false`) - **macOS**:`darwin-perf`(Apple Silicon per-PID) - **NVIDIA 独显**:`nvml`(`nvidia-ml-py`,需安装 NVIDIA 驱动) 未探测到任何后端时不影响 CPU/内存监控。Windows 上若曾安装过 `nvidia-ml-py` 但无 NVIDIA 显卡,会忽略 NVML 并走 `win_perf`。 Chart.js v4 已内置在 `static/js/chart.umd.min.js`,无需 npm。 ### Linux(Debian / Ubuntu) 系统自带的 Python 受 [PEP 668](https://peps.python.org/pep-0668/) 保护,**不要**对全局环境执行 `pip3 install`(会报 `externally-managed-environment`)。请使用项目内虚拟环境: - 首次需安装 venv 组件(版本号与系统 Python 一致,例如 3.12): ```bash sudo apt install python3.12-venv python3-full # 可选:与 gnome-system-monitor 同源读内存(推荐) sudo apt install gir1.2-gtop-2.0 python3-gi ``` - 若执行 `python3 -m venv` 时报 `ensurepip is not available`,说明尚未安装上述包。 - 未安装 `gir1.2-gtop-2.0` 时,Linux 内存回退为读取 `/proc//statm`(算法与 libgtop 相同);进程列表约 **1 秒** 刷新内存,显示 **1 位小数** MiB。 - 实时监控时,进程表 **内存/常驻** 随 SSE 与采集间隔同步更新(与系统监视器刷新节奏接近)。 ## 快速开始 ```bash # 克隆仓库 git clone git@gitee.com:software_test_team/performance_monitor.git cd performance_monitor ``` ### Windows / macOS ```bash # 安装依赖(建议在虚拟环境中执行,见下方 Linux 步骤) pip install -r requirements.txt # 启动(默认 http://localhost:5000,并尝试打开浏览器) python main.py ``` ### Linux ```bash # 1. 创建并激活虚拟环境(目录 .venv 已在 .gitignore 中忽略) python3 -m venv .venv source .venv/bin/activate # 2. 在虚拟环境中安装依赖 pip install -r requirements.txt # 3. 启动 python main.py ``` 每次新开终端重新进入项目目录后,需先执行 `source .venv/bin/activate` 再运行 `python main.py`。提示符出现 `(.venv)` 表示已激活。 退出虚拟环境:`deactivate`。 ### 命令行参数 | 参数 | 默认值 | 说明 | |------|--------|------| | `--port PORT` | 5000 | HTTP 服务端口 | | `--interval SECONDS` | 1.0 | 采样间隔(秒) | | `--log-dir DIR` | `logs` | SQLite 数据库目录 | | `--no-browser` | — | 启动时不自动打开浏览器 | | `--debug` | — | 开启性能诊断日志(stderr,约每 2 秒汇总) | ```bash python main.py --port 8080 --interval 2 --log-dir ./data --no-browser ``` ### 性能诊断(监控 + 图表 + 列表) 用于定位卡顿在 **后端采样**、**SSE** 还是 **前端 DOM/Chart.js**。 **后端**(终端 stderr,每约 2 秒一行 `perf summary`): ```bash # PowerShell $env:PERF_MONITOR_LOG="1" python main.py # 或 python main.py --debug ``` 主要阶段:`list:tick_cpu` / `list:tick_gpu`(未监控列表)、`monitor:loop`(采集+写库+推流)、`monitor:sse_payload`(包大小)。单次超过约 80ms 会额外打 `perf slow`。 **前端**(浏览器控制台,每约 2 秒 `[PerfMonitor][perf] summary`;每 10 秒 `state` 快照): ```javascript localStorage.setItem('perfMonitorPerf', '1'); // 或 perfMonitorDebug=1 location.reload(); ``` 主要阶段:`sse:dispatch:*`、`list:apply_batch` / `list:cells` / `list:render`、`monitor:tick` / `monitor:chart_pid`、`chart:raf`。可配合 DevTools **Performance** 录屏对照 Long Task。 关闭前端:`localStorage.removeItem('perfMonitorPerf');` 后刷新。 **任务管理器验收**(`--debug` 或 `PERF_MONITOR_LOG=1`)stderr 关键字: | 日志 | 含义 | |------|------| | `tm cpu_ok` | 整表 enrich 耗时(2s 全量模式) | | `tm cpu_shard_slow` | 分片 enrich ≥1s(仅 watch>512 时) | | `tm tick_overrun` | 单轮 CPU tick 超过 1.15× 间隔(全量 120~200 PID 时偶发) | | `tm tick_slow` / `perf slow list:tick_cpu` | enrich 仍过慢 | | `tm pdh_full_skipped` | PDH 全量重建已 debounce(正常) | | `tm watch_unchanged` | 重复 POST watch 已跳过 | 验收目标:默认 **每 2s 对整份 watch(≤200 PID)全量** 更新 CPU/内存;日志 `shard=False`;`tm watch_unchanged` 常见、`watch_set` 不应秒级 120↔48 抖动。 ## 使用说明 ### 实时监控 1. 浏览器打开 `http://localhost:5000` 2. 在 **进程列表** 中搜索、排序并勾选目标进程 3. 按需勾选 **合并同名** 或 **汇总全部**(大量进程时界面会提示合并策略) 4. 点击 **开始监控** — 待全部 PID 指标就绪后,计时器开始走字,曲线开始更新 5. 使用缩放按钮或拖拽图表时间区域调整视窗;可关闭单张图表或通过「+」补回 CPU/内存/磁盘 6. 点击 **停止监控** 结束会话,数据写入 SQLite;可继续切换合并方式查看已采集曲线 7. **导出** 可将当前实时缓冲导出为 `.log` ### 历史会话 1. 页面下方 **历史会话** 区域点击 **加载历史** 2. 点击某条会话 chip 回放;可用合并选项切换视图 3. **配置** 可指定其他 SQLite 路径(存于浏览器 `localStorage`) 4. **导入** `.log`、**导出** 选中会话、**清空历史** 删除库中记录 ## 项目结构 ``` main.py 命令行入口(waitress / Flask) config.py PerfConfig 默认配置 monitor/ collector.py 后台采样(MetricsCollector) data_buffer.py 内存缓冲 + SQLite 批量写入 process_manager.py 进程枚举、搜索、信息补全 web/ app.py Flask 路由与 REST API sse.py SSE 广播 static/ index.html 单页应用 css/style.css 深色主题 js/ app.js 监控生命周期、合并策略、SSE charts.js 实时 Chart.js、缩放、重建 history_store.js 历史分档拉数 / IndexedDB 缓存 history_data_worker.js 历史指标按 PID 分区(Worker) history.js 历史回放与导入 process_list.js 进程表与选型 UI ui_limits.js 监控/合并阈值(前端统一配置) chart.umd.min.js Chart.js v4 logs/ perf_monitor.db 默认数据库(首次运行后生成) ``` ## 数据流 ``` 用户勾选 PID → POST /api/monitor/start → MetricsCollector 守护线程按间隔 psutil 采样 → 全部 PID 首次 CPU+内存 样本 → 对齐 session 起点、metrics_ready=true → DataBuffer(内存)+ DataLogger(SQLite 批量写) → SSE /stream 推送到浏览器 → ChartManager 追加/合并曲线(切换合并时从 _pidLog 回放) 停止 → POST /api/monitor/stop → 刷写 DB、关闭会话 ``` ## API 参考 | 方法 | 路由 | 说明 | |------|------|------| | GET | `/` | Web 前端 | | GET | `/api/processes` | 进程列表(`sort` `asc` `search`) | | POST | `/api/processes/enrich` | 批量补全 PID 的 CPU/内存 | | GET | `/api/processes/` | 单进程详情 | | POST | `/api/monitor/start` | 开始监控,body: `{"pids": [...]}` | | POST | `/api/monitor/stop` | 停止监控 | | GET | `/api/monitor/status` | `running` `metrics_ready` `elapsed` `active_pids` 等 | | GET | `/api/data/` | 内存缓冲中的指标序列 | | GET | `/api/config` | 当前库路径、采样间隔 | | GET | `/api/sessions` | 历史会话列表(可选 `?db=` 自定义库) | | DELETE | `/api/sessions` | 删除全部会话(可选 `?db=`) | | DELETE | `/api/sessions/` | 删除单条会话 | | GET | `/api/sessions//metrics/meta` | 会话档位、行数、是否允许 bulk、首屏时间窗 | | GET | `/api/sessions//metrics` | 指标:无参 bulk(仅 S 档小会话);`from`/`to`/`after`/`limit`/`pids` 分窗与分块 | | GET | `/api/sessions//metrics?pid=` | 单 PID 全量(兼容) | | GET | `/api/sessions//export` | 导出会话为 `.log` | | GET | `/api/data/export` | 导出当前实时缓冲 | | POST | `/api/import` | 上传 `.log` 解析 | | GET | `/stream` | SSE 实时指标流 | ## SQLite 结构 默认路径:`logs/perf_monitor.db`(`--log-dir` 可改)。 | 表 | 主要字段 | |----|----------| | `sessions` | `id`, `start_time`, `end_time`, `monitor_interval`, … | | `monitored_processes` | `session_id`, `pid`, `process_name`, `username`, `executable_path` | | `metrics` | `session_id`, `pid`, `timestamp`, `cpu_percent`, `memory_rss`, `memory_vms`, `memory_percent`, `disk_read_bps`, `disk_write_bps` | 索引:`(session_id, pid)`、`(timestamp)`、`(session_id, pid, timestamp)`。 ### 历史回放档位 | 档位 | 时长 | 默认加载 | 精度 | |------|------|----------|------| | S | ≤12h | 允许时一次 bulk 全会话 raw | DB 原始行 | | M | 12h~48h | 最近 6h raw,缩放补载 | 当前窗内 raw | | L | >48h | 最近 2h raw + chunk;不合并时最多绘 12 个 PID | 当前窗内 raw | 阈值见 `/api/config` 中 `history_*` 字段;大会话无参 `GET .../metrics` 返回 `400 use_range`。 ## 配置 `config.py` 中的 `PerfConfig`(部分可由命令行覆盖): | 字段 | 默认 | 说明 | |------|------|------| | `default_interval` | 1.0 | 采集器初始间隔(秒);CLI `--interval` 可改 | | `monitor_interval` | 1.0 | **实时监控**固定采集/SSE 间隔(秒);与 `list_tick` 独立 | | `max_data_points` | 1800 | 每 PID 内存缓冲上限(约 30 分钟 @1s) | | `db_batch_size` | 50 | SQLite 批量写入行数 | | `db_flush_interval` | 10.0 | 最大刷盘间隔(秒) | | `default_port` | 5000 | HTTP 端口 | | `log_dir` | `logs` | 数据目录 | 前端监控/合并阈值见 `static/js/ui_limits.js`(与后端独立)。 ## 跨平台说明 进程与 IO 通过 **psutil** 采集;**内存主列**按平台自动对齐本地系统监视器(见 `monitor/platform_memory.py`)。 | 平台 | 进程表主列「内存」 | `memory_private` 字段(DB/曲线副线) | 副列 | |------|-------------------|--------------------------------------|------| | **Linux** | `resident − share`(libgtop 或 statm,≈ 系统监视器 **Memory**) | 同上 | **Resident Memory**(完整常驻) | | **Windows** | 完整 RSS | 专用工作集(任务管理器「内存」) | 专用工作集 | | **macOS** | 完整 RSS | USS | USS | | 能力 | API | 说明 | |------|-----|------| | 进程列表 | `process_iter()` | Win / Linux / macOS | | CPU | `cpu_times()` 差分 ÷ Δt ÷ `cpu_count()` ×100,单进程封顶 100% | 对齐任务管理器「进程」页刻度;合并组为各进程之和(不含 PID 0) | | 内存 | `memory_info()` / `platform_memory` | Linux 主显示为 rss−shared;`memory_rss` 始终为完整 RSS | | 磁盘 I/O | `io_counters()` | macOS 上部分进程可能无权限,指标为空 | ## 常见问题 **Q: Linux 上与系统监视器内存仍不一致?** A: 主列对应 **Memory**(`resident − share`),副列对应 **Resident Memory**。安装 `gir1.2-gtop-2.0` 后与系统监视器同源;未安装时用 statm,数值应接近。若仍差数 MB~数十 MB,多为 **刷新时刻不同**(本工具约 1 秒刷新进程列表)。合并多 PID 时相加仍可能重复计入共享库。 **Q: Linux 上 `pip install` 报 `externally-managed-environment`?** A: 系统 Python 不允许全局 pip 安装。按上文 **Linux** 小节:先 `sudo apt install python3.12-venv python3-full`,再 `python3 -m venv .venv` 与 `source .venv/bin/activate`,然后在虚拟环境里 `pip install -r requirements.txt`。不要使用 `--break-system-packages`。 **Q: 点击开始后长时间显示「启动中」?** A: 需等所有选中 PID 都采到 **CPU + 内存**(状态栏会显示 `启动中 x/y`)。通常约 1~2 秒;若某进程已退出、采样超时或无法读内存,可能一直不满足,请减少选择或去掉无权限进程。无磁盘 IO 的进程不会阻塞启动。 **Q: 合并切换时图表闪一下?** A: v1.3.1 起重建过程有遮罩并批量定轴;若仍异常,请刷新页面后重试。 **Q: SSE 断开?** A: 前端会自动指数退避重连;生产环境请安装 `waitress`,避免 Flask 开发服务器长连接不稳定。 **Q: 磁盘曲线一直为空(macOS)?** A: 系统对非本用户进程的 IO 计数可能受限,属平台限制。 ## 许可证 MIT