QtMonitor

使用Windows API实现程序性能监控

作者
WildPointer
更新
2026-07-12
类型
自有项目
仓库
0 stars / 0 forks(抓取快照)

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
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
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
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
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
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
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();        }    }}
WildPointer

专注系统编程、工程实践与底层技术,记录 C++、Qt、OpenCV 与 VTK 的学习和实践。