QtMonitor
使用Windows API实现程序性能监控
QtMonitor
使用Windows API实现程序性能监控
语言组成
- C++
- 100.0%
文件结构
查看完整文件清单(20 个文件)
QtMonitor.sln QtMonitor/QtMonitor.cpp QtMonitor/QtMonitor.h QtMonitor/QtMonitor.qrc QtMonitor/QtMonitor.ui QtMonitor/QtMonitor.vcxproj QtMonitor/QtMonitor.vcxproj.filters QtMonitor/cpuMonitor/cpuMonitor.cpp QtMonitor/cpuMonitor/cpuMonitor.h QtMonitor/ioMonitor/ioMonitor.cpp QtMonitor/ioMonitor/ioMonitor.h QtMonitor/io_test.txt QtMonitor/main.cpp QtMonitor/memoryMonitor/memoryMonitor.cpp QtMonitor/memoryMonitor/memoryMonitor.h QtMonitor/systemMonitor.cpp QtMonitor/systemMonitor.h QtMonitor/threadMonitor/threadMonitor.cpp QtMonitor/threadMonitor/threadMonitor.h README.md
关键源码
QtMonitor/cpuMonitor/cpuMonitor.cpp
#include "cpuMonitor.h"/** * @brief Initializes the CPU monitor with processor count and first snapshots. */cpuMonitor::cpuMonitor(){ m_timer.start(); SYSTEM_INFO info{}; GetSystemInfo(&info); m_coreCount = static_cast<int>(info.dwNumberOfProcessors); initProcessCpuSnapshot(); initSystemCpuSnapshot();}/** * @brief Calculates current-process CPU usage from GetProcessTimes deltas. * @param percentOut Receives the process CPU usage percentage. * @return true when a valid percentage is produced; false otherwise. */bool cpuMonitor::processCpuPercent(double& percentOut){ FILETIME createTime{}; FILETIME exitTime{}; FILETIME kernelTime{}; FILETIME userTime{}; if (!GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime)) { return false; } const qint64 wallNs = m_timer.nsecsElapsed(); const quint64 processCpuTime = fileTimeToUInt64(kernelTime) + fileTimeToUInt64(userTime); const quint64 deltaProcessTime = processCpuTime >= m_lastProcessCpuTime ? processCpuTime - m_lastProcessCpuTime : 0; const qint64 deltaWallNs = wallNs - m_lastProcessWallNs; m_lastProcessCpuTime = processCpuTime; m_lastProcessWallNs = wallNs; if (!m_processCpuValid || deltaWallNs <= 0 || m_coreCount <= 0) { m_processCpuValid = true; return false; } // GetProcessTimes is measured in 100 ns ticks. QElapsedTimer returns ns. const double deltaWall100Ns = static_cast<double>(deltaWallNs) / 100.0; percentOut = (static_cast<double>(deltaProcessTime) * 100.0) / (deltaWall100Ns * static_cast<double>(m_coreCount)); if (percentOut < 0.0) { percentOut = 0.0; } return true;}/** * @brief Calculates total system CPU usage from GetSystemTimes deltas. * @param percentOut Receives the system CPU usage percentage. * @return true when a valid percentage is produced; false otherwise. */bool cpuMonitor::systemCpuPercent(double& percentOut){ FILETIME idleTime{}; FILETIME kernelTime{}; FILETIME userTime{}; if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) { return false; } const quint64 currentIdle = fileTimeToUInt64(idleTime); const quint64 currentKernel = fileTimeToUInt64(kernelTime); const quint64 currentUser = fileTimeToUInt64(userTime); const quint64 idleDelta = currentIdle >= m_lastSystemIdle ? currentIdle - m_lastSystemIdle : 0; const quint64 kernelDelta = currentKernel >= m_lastSystemKernel ? currentKernel - m_lastSystemKernel : 0; const quint64 userDelta = currentUser >= m_lastSystemUser ? currentUser - m_lastSystemUser : 0; m_lastSystemIdle = currentIdle; m_lastSystemKernel = currentKernel; m_lastSystemUser = currentUser; if (!m_systemCpuValid) { m_systemCpuValid = true; return false; } const quint64 total = kernelDelta + userDelta; if (total == 0) { return false; } const quint64 busy = total > idleDelta ? total - idleDelta : 0; percentOut = (static_cast<double>(busy) * 100.0) / static_cast<double>(total); if (percentOut < 0.0) { percentOut = 0.0; } return true;}/** * @brief Combines the high and low FILETIME parts into one 64-bit value. * @param fileTime FILETIME value to convert. * @return Unsigned 100 ns tick count. */quint64 cpuMonitor::fileTimeToUInt64(const FILETIME& fileTime){ ULARGE_INTEGER value{}; value.LowPart = fileTime.dwLowDateTime; value.HighPart = fileTime.dwHighDateTime; return static_cast<quint64>(value.QuadPart);}/** * @brief Stores the current process CPU time and matching wall-clock time. */void cpuMonitor::initProcessCpuSnapshot(){ FILETIME createTime{}; FILETIME exitTime{}; FILETIME kernelTime{}; FILETIME userTime{}; if (!GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime)) { m_processCpuValid = false; return; } m_lastProcessCpuTime = fileTimeToUInt64(kernelTime) + fileTimeToUInt64(userTime); m_lastProcessWallNs = m_timer.nsecsElapsed(); m_processCpuValid = true;}/** * @brief Stores the current idle, kernel, and user system CPU times. */void cpuMonitor::initSystemCpuSnapshot(){ FILETIME idleTime{}; FILETIME kernelTime{}; FILETIME userTime{}; if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) { m_systemCpuValid = false; return; } m_lastSystemIdle = fileTimeToUInt64(idleTime); m_lastSystemKernel = fileTimeToUInt64(kernelTime); m_lastSystemUser = fileTimeToUInt64(userTime); m_systemCpuValid = true;}QtMonitor/memoryMonitor/memoryMonitor.cpp
#include "memoryMonitor.h"/** * @brief 获取当前进程内存计数器并复制到 memoryInfo。 * @param memoryOut 输出内存采样结果。 * @return true 表示采样成功;false 表示 GetProcessMemoryInfo 调用失败。 */bool memoryMonitor::sample(memoryInfo& memoryOut) const{ PROCESS_MEMORY_COUNTERS_EX counters{}; if (!GetProcessMemoryInfo(GetCurrentProcess(),reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&counters),sizeof(counters))) { return false; } memoryOut.m_miPrivateBytes = static_cast<quint64>(counters.PrivateUsage); memoryOut.m_miPagefileBytes = static_cast<quint64>(counters.PagefileUsage); memoryOut.m_miPageFaultCount = static_cast<quint32>(counters.PageFaultCount); memoryOut.m_miWorkingSetBytes = static_cast<quint64>(counters.WorkingSetSize); memoryOut.m_miPeakPagefileBytes = static_cast<quint64>(counters.PeakPagefileUsage); return true;}QtMonitor/ioMonitor/ioMonitor.cpp
#include "ioMonitor.h"/** * @brief 初始化 I/O 监控器并记录第一份进程 I/O 快照。 */ioMonitor::ioMonitor(){ m_timer.start(); initSnapshot();}/** * @brief 读取当前进程 I/O 计数器并计算两次采样间的 I/O 速率。 * @return I/O 采样结果;首次采样或 API 失败时返回无效结果。 */ioInfo ioMonitor::sampleRates(){ ioInfo infoOut; IO_COUNTERS io{}; if (!GetProcessIoCounters(GetCurrentProcess(), &io)) { return infoOut; } const qint64 nowNs = m_timer.nsecsElapsed(); const qint64 deltaNs = nowNs - m_lastTimeNs; if (!m_valid || deltaNs <= 0) { m_lastIo = io; m_lastTimeNs = nowNs; m_valid = true; return infoOut; } const double deltaSec = static_cast<double>(deltaNs) / 1'000'000'000.0; const quint64 currentOps = io.ReadOperationCount + io.WriteOperationCount + io.OtherOperationCount; const quint64 lastOps = m_lastIo.ReadOperationCount + m_lastIo.WriteOperationCount + m_lastIo.OtherOperationCount; const quint64 deltaOps = currentOps >= lastOps ? currentOps - lastOps : 0; const quint64 deltaReadBytes = io.ReadTransferCount >= m_lastIo.ReadTransferCount ? io.ReadTransferCount - m_lastIo.ReadTransferCount : 0; const quint64 deltaWriteBytes = io.WriteTransferCount >= m_lastIo.WriteTransferCount ? io.WriteTransferCount - m_lastIo.WriteTransferCount : 0; infoOut.m_iiState = true; infoOut.m_iiTotalOps = currentOps; infoOut.m_iiOpsPerSec = static_cast<double>(deltaOps) / deltaSec; infoOut.m_iiTotalReadBytes = static_cast<quint64>(io.ReadTransferCount); infoOut.m_iiTotalWriteBytes = static_cast<quint64>(io.WriteTransferCount); infoOut.m_iiReadBytesPerSec = static_cast<double>(deltaReadBytes) / deltaSec; infoOut.m_iiWriteBytesPerSec = static_cast<double>(deltaWriteBytes) / deltaSec; m_lastIo = io; m_lastTimeNs = nowNs; return infoOut;}/** * @brief 初始化当前进程 I/O 计数器快照。 */void ioMonitor::initSnapshot(){ IO_COUNTERS io{}; if (!GetProcessIoCounters(GetCurrentProcess(), &io)) { m_valid = false; return; } m_lastIo = io; m_lastTimeNs = m_timer.nsecsElapsed(); m_valid = true;}QtMonitor/threadMonitor/threadMonitor.cpp
#include "threadMonitor.h"/** * @brief 遍历系统线程快照并统计 th32OwnerProcessID 等于当前进程 ID 的线程。 * @return 当前进程线程数;创建快照失败时返回 -1。 */int threadMonitor::threadCount() const{ const DWORD processId = GetCurrentProcessId(); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (snapshot == INVALID_HANDLE_VALUE) { return -1; } THREADENTRY32 entry{}; entry.dwSize = sizeof(THREADENTRY32); int count = 0; if (Thread32First(snapshot, &entry)) { do { if (entry.th32OwnerProcessID == processId) { ++count; } } while (Thread32Next(snapshot, &entry)); } CloseHandle(snapshot); return count;}QtMonitor/systemMonitor.cpp
#include "systemMonitor.h"/** * @brief 构造监控汇总对象,四个子监控器随成员初始化完成。 * @param parent QObject 父对象。 */systemMonitor::systemMonitor(QObject* parent) : QObject(parent){}/** * @brief 执行一次采样并通过 sampleReady() 发出结果。 */void systemMonitor::requestSample(){ emit sampleReady(handle_smSampleLine());}/** * @brief 汇总 CPU、线程、内存和 I/O 信息,并格式化为界面展示文本。 * @return 已格式化的多行监控信息。 */QString systemMonitor::handle_smSampleLine(){ memoryInfo memory; double processCpu = 0.0; double systemCpu = 0.0; const ioInfo io = m_ioMonitor.sampleRates(); const int threads = m_threadMonitor.threadCount(); const bool hasProcessCpu = m_cpuMonitor.processCpuPercent(processCpu); const bool hasSystemCpu = m_cpuMonitor.systemCpuPercent(systemCpu); const bool hasMemory = m_memoryMonitor.sample(memory); const QString threadsText = threads >= 0 ? QString::number(threads) : QStringLiteral("N/A"); const QString processCpuText = hasProcessCpu ? QString::number(processCpu, 'f', 1) + QStringLiteral("%") : QStringLiteral("N/A"); const QString systemCpuText = hasSystemCpu ? QString::number(systemCpu, 'f', 1) + QStringLiteral("%") : QStringLiteral("N/A"); QString memoryText = QStringLiteral("N/A"); if (hasMemory) { const double workingSetMb = static_cast<double>(memory.m_miWorkingSetBytes) / (1024.0 * 1024.0); const double privateMb = static_cast<double>(memory.m_miPrivateBytes) / (1024.0 * 1024.0); const double pagefileMb = static_cast<double>(memory.m_miPagefileBytes) / (1024.0 * 1024.0); const double peakPagefileMb = static_cast<double>(memory.m_miPeakPagefileBytes) / (1024.0 * 1024.0); memoryText = QStringLiteral("虚拟(Pagefile) ") + QString::number(pagefileMb, 'f', 1) + QStringLiteral(" MB") + QStringLiteral(", WS ") + QString::number(workingSetMb, 'f', 1) + QStringLiteral(" MB") + QStringLiteral(", 页错误 ") + QString::number(memory.m_miPageFaultCount) + QStringLiteral(", 提交(Private) ") + QString::number(privateMb, 'f', 1) + QStringLiteral(" MB") + QStringLiteral(", 峰值Pagefile ") + QString::number(peakPagefileMb, 'f', 1) + QStringLiteral(" MB"); } QString ioText = QStringLiteral("N/A"); if (io.m_iiState) { const double readMbPerSec = io.m_iiReadBytesPerSec / (1024.0 * 1024.0); const double writeMbPerSec = io.m_iiWriteBytesPerSec / (1024.0 * 1024.0); const double totalReadMb = static_cast<double>(io.m_iiTotalReadBytes) / (1024.0 * 1024.0); const double totalWriteMb = static_cast<double>(io.m_iiTotalWriteBytes) / (1024.0 * 1024.0); ioText = QStringLiteral("频率 ") + QString::number(io.m_iiOpsPerSec, 'f', 1) + QStringLiteral(" ops/s") + QStringLiteral(", 读 ") + QString::number(readMbPerSec, 'f', 2) + QStringLiteral(" MB/s") + QStringLiteral(", 写 ") + QString::number(writeMbPerSec, 'f', 2) + QStringLiteral(" MB/s") + QStringLiteral(", 累计读 ") + QString::number(totalReadMb, 'f', 1) + QStringLiteral(" MB") + QStringLiteral(", 累计写 ") + QString::number(totalWriteMb, 'f', 1) + QStringLiteral(" MB"); } return QStringLiteral("线程数: ") + threadsText + QStringLiteral(" | 进程CPU: ") + processCpuText + QStringLiteral(" | 系统CPU: ") + systemCpuText + QStringLiteral("\n内存: ") + memoryText + QStringLiteral("\nI/O: ") + ioText;}QtMonitor/QtMonitor.cpp
#include "QtMonitor.h"#include <QDateTime>#include <QFile>/** * @brief 构造主窗口并初始化 UI、采样定时器和 I/O 模拟定时器。 * @param parent 父 QWidget。 */QtMonitor::QtMonitor(QWidget* parent) : QWidget(parent) , ui(new Ui::QtMonitorClass()){ ui->setupUi(this); ui->startBtn->setText(QStringLiteral("开始")); ui->stopBtn->setText(QStringLiteral("停止")); ui->monitorInfo->setReadOnly(true); timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &QtMonitor::onTimerTick); connect(ui->timeEdit, &QLineEdit::editingFinished, this, &QtMonitor::onTimeEditChanged); ui->timeEdit->setValidator(new QIntValidator(1, 24 * 60 * 60, ui->timeEdit)); ioTimer = new QTimer(this); connect(ioTimer, &QTimer::timeout, this, &QtMonitor::onSimulateIO);}/** * @brief 析构主窗口,确保后台线程先停止再释放界面对象。 */QtMonitor::~QtMonitor(){ stopMonitoring(); delete ui;}/** * @brief 响应“开始”按钮点击,启动监控流程。 */void QtMonitor::on_startBtn_clicked(){ startMonitoring();}/** * @brief 响应“停止”按钮点击,停止监控流程。 */void QtMonitor::on_stopBtn_clicked(){ stopMonitoring();}/** * @brief 采样定时器回调,向 worker 线程投递采样请求。 */void QtMonitor::onTimerTick(){ if (!monitoring || !backend) { return; } onTimeEditChanged(); emit sampleRequested();}/** * @brief 读取界面输入并在运行时动态调整采样定时器间隔。 */void QtMonitor::onTimeEditChanged(){ if (!timer) { return; } const int seconds = intervalSeconds(); const int ms = seconds * 1000; if (timer->interval() != ms) { timer->setInterval(ms); }}/** * @brief 从 timeEdit 读取采样间隔。 * @return 有效输入返回用户输入秒数;无效输入返回 60 秒。 */int QtMonitor::intervalSeconds() const{ bool ok = false; const int seconds = ui->timeEdit->text().trimmed().toInt(&ok); return (ok && seconds > 0) ? seconds : 60;}/** * @brief 将采样结果带时间戳追加到 QTextEdit。 * @param line worker 线程返回的格式化监控文本。 */void QtMonitor::appendLine(const QString& line){ const QString timestamp = QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd HH:mm:ss")); ui->monitorInfo->append(timestamp + QStringLiteral(" | ") + line);}/** * @brief 创建 worker 线程和 systemMonitor,并启动采样与 I/O 模拟。 */void QtMonitor::startMonitoring(){ stopMonitoring(); ui->monitorInfo->clear(); m_ioToggle = false; // 准备固定大小的测试文件,让后续读写操作能稳定产生 I/O 计数变化。 QFile file(QStringLiteral("io_test.txt")); if (file.exists()) { file.remove(); } if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { file.write(QByteArray(2 * 1024 * 1024, '0')); file.close(); } monitorThread = new QThread(this); backend = new systemMonitor(); backend->moveToThread(monitorThread); // 所有采样请求和结果都使用 queued connection,避免 UI 线程直接执行采样逻辑。 connect(monitorThread, &QThread::finished, backend, &QObject::deleteLater); connect(this, &QtMonitor::sampleRequested, backend, &systemMonitor::requestSample, Qt::QueuedConnection); connect(backend, &systemMonitor::sampleReady, this, &QtMonitor::appendLine, Qt::QueuedConnection); monitorThread->start(); monitoring = true; onTimeEditChanged(); timer->start(timer->interval() > 0 ? timer->interval() : intervalSeconds() * 1000); if (ioTimer) { ioTimer->start(588); } onTimerTick();}/** * @brief 停止监控流程并同步回收 worker 线程。 */void QtMonitor::stopMonitoring(){ if (timer) { timer->stop(); } if (ioTimer) { ioTimer->stop(); } monitoring = false; if (backend) { disconnect(this, &QtMonitor::sampleRequested, backend, &systemMonitor::requestSample); disconnect(backend, &systemMonitor::sampleReady, this, &QtMonitor::appendLine); } if (monitorThread) { monitorThread->quit(); monitorThread->wait(); delete monitorThread; monitorThread = nullptr; backend = nullptr; }}/** * @brief 交替执行 2 MB 文件读写,用于制造当前进程 I/O 活动。 */void QtMonitor::onSimulateIO(){ QFile file(QStringLiteral("io_test.txt")); m_ioToggle = !m_ioToggle; constexpr int dataSize = 2 * 1024 * 1024; if (m_ioToggle) { if (file.open(QIODevice::ReadWrite)) { file.seek(0); file.write(QByteArray(dataSize, '1')); file.close(); } } else { if (file.open(QIODevice::ReadOnly)) { file.seek(0); file.read(dataSize); file.close(); } }}