Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,6 @@ jobs:
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: PCMforWindows
path: build/bin/**/*
path: |
build/bin/**/*
third-party-software.txt
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ if(UNIX) # APPLE, LINUX, FREE_BSD
message(STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")

install(DIRECTORY "perfmon" DESTINATION ${CMAKE_INSTALL_DATADIR}/pcm)
install(FILES "third-party-software.txt" DESTINATION ${CMAKE_INSTALL_DATADIR}/pcm)

endif(UNIX)

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ build_script:
- msbuild src\WinMSRDriver\MSR.vcxproj /m /t:Build /p:Configuration=Release /p:Platform=x64

after_build:
- cmd: 7z a pcm-all.zip %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.exe %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.dll %APPVEYOR_BUILD_FOLDER%\build\src\Release\*.lib %APPVEYOR_BUILD_FOLDER%\build\src\Release\*.exp %APPVEYOR_BUILD_FOLDER%\src\windows\PCM-Service.exe.config %APPVEYOR_BUILD_FOLDER%\src\WinMSRDriver\x64\Release\MSR\msr.*
- cmd: 7z a pcm-all.zip %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.exe %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.dll %APPVEYOR_BUILD_FOLDER%\build\src\Release\*.lib %APPVEYOR_BUILD_FOLDER%\build\src\Release\*.exp %APPVEYOR_BUILD_FOLDER%\src\windows\PCM-Service.exe.config %APPVEYOR_BUILD_FOLDER%\src\WinMSRDriver\x64\Release\MSR\msr.* %APPVEYOR_BUILD_FOLDER%\third-party-software.txt
artifacts:
- path: pcm-all.zip
name: pcm-all
Expand Down
6 changes: 3 additions & 3 deletions doc/WINDOWS_HOWTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ _For support of systems with more than _**_64_**_ logical cores you need to comp
alternatively you can perform `cmake -B build`, open *PCM.sln* form *build* folder in and build required project in Visual Studio.
.exe and .dll files will be located in *build\bin\Release* folder
3. As Administrator create PCM directory in Windows "Program Files" directory (e.g. `C:\Program Files (x86)\PCM\`)
4. As Administrator copy the msr.sys driver and pcm.exe into the PCM directory
4. As Administrator copy the msr.sys driver into `c:\windows\system32` and pcm.exe into the PCM directory
5. Run pcm.exe utility from the PCM directory as Administrator

For Windows 7+ and Windows Server 2008+ R2 the PCM utilities need to be run as Administrator:
Expand Down Expand Up @@ -51,7 +51,7 @@ If you do not want or cannot compile the msr.sys driver you might use a third-pa
Instructions:

1. Download the free RealTemp utility package from [http://www.techpowerup.com/realtemp/](http://www.techpowerup.com/realtemp/) or any other free utility that uses the open-source WinRing0 driver (like OpenHardwareMonitor [http://code.google.com/p/open-hardware-monitor/downloads/list](http://code.google.com/p/open-hardware-monitor/downloads/list)).
2. Copy WinRing0.dll, WinRing0.sys, WinRing0x64.dll, WinRing0x64.sys files from there into the PCM.exe binary location, into the PCM-Service.exe location and into c:\windows\system32
2. Copy WinRing0.dll, WinRing0.sys, WinRing0x64.dll, WinRing0x64.sys files from there into c:\windows\system32
3. Run the PCM.exe tool and/or go to step 6 (perfmon utility).

## Compile the Windows MSR driver
Expand Down Expand Up @@ -122,7 +122,7 @@ Starting from this release, **pcm-sensor-server** is now supported on Windows. T

### Running pcm-sensor-server on Windows

1. Create a directory for PCM in a protected location (e.g., `C:\Program Files\PCM\` or `C:\Program Files (x86)\PCM\`). Copy `msr.sys` and `pcm-sensor-server.exe` to this directory. **Important:** Do not place PCM binaries in user-writable directories (e.g., Downloads, Desktop, `C:\Users\Public\`) to prevent DLL planting attacks.
1. Create a directory for PCM in a protected location (e.g., `C:\Program Files\PCM\` or `C:\Program Files (x86)\PCM\`). Copy `pcm-sensor-server.exe` to this directory and `msr.sys` to `c:\windows\system32`. If using WinPmem for memory bandwidth statistics, also copy `winpmem_x64.sys` (or `winpmem_x86.sys`) to `c:\windows\system32`. **Important:** Do not place PCM binaries or drivers in user-writable directories (e.g., Downloads, Desktop, `C:\Users\Public\`) to prevent driver planting attacks.

2. Run as Administrator (required for MSR access):
```
Expand Down
2 changes: 1 addition & 1 deletion src/cpucounters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3279,7 +3279,7 @@ PCM::PCM() :
#endif
#ifdef _MSC_VER
// WARNING: This driver code (msr.sys) is only for testing purposes, not for production use
Driver drv(Driver::msrLocalPath());
Driver drv(Driver::msrSystemPath());
// drv.stop(); // restart driver (usually not needed)
if (!drv.start())
{
Expand Down
5 changes: 5 additions & 0 deletions src/cpucounters.h
Original file line number Diff line number Diff line change
Expand Up @@ -2074,6 +2074,11 @@ class PCM_API PCM
//! \return socket identifier
int32 getSocketId(uint32 core_id) const { return (int32)topology[core_id].socket_id; }

//! \brief Determines die of given processor ID within a socket
//! \param os_id processor identifier
//! \return die identifier
int32 getDieId(uint32 os_id) const { return (int32)topology[os_id].die_id; }

//! \brief Maps NUMA node ID to CPU socket ID
//! \param numa_node_id NUMA node identifier
//! \return socket identifier, or -1 if mapping is not available or numa_node_id is invalid
Expand Down
11 changes: 8 additions & 3 deletions src/mmio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ class PCMPmem : public WinPmem {
SYSTEM_INFO sys_info;
SecureZeroMemory(&sys_info, sizeof(sys_info));

GetCurrentDirectory(MAX_PATH - 10, driver_filename);
// Use System32 directory to avoid untrusted search path vulnerability
if (!GetSystemDirectory(driver_filename, MAX_PATH - 10))
{
std::wcerr << "Failed to get System32 directory path.\n";
return -1;
}

GetNativeSystemInfo(&sys_info);
switch (sys_info.wProcessorArchitecture)
Expand All @@ -47,15 +52,15 @@ class PCMPmem : public WinPmem {
_tcscat_s(driver_filename, MAX_PATH, TEXT("\\winpmem_x64.sys"));
if (GetFileAttributes(driver_filename) == INVALID_FILE_ATTRIBUTES)
{
std::cerr << "ERROR: winpmem_x64.sys not found in current directory. Download it from https://github.com/Velocidex/WinPmem/blob/f044f340dd05658d026b0f293cdfa92876159872/kernel/binaries/winpmem_x64.sys .\n";
std::cerr << "ERROR: winpmem_x64.sys not found in System32 directory. Download it from https://github.com/Velocidex/WinPmem/blob/f044f340dd05658d026b0f293cdfa92876159872/kernel/binaries/winpmem_x64.sys .\n";
std::cerr << "ERROR: Memory bandwidth statistics will not be available.\n";
}
break;
case PROCESSOR_ARCHITECTURE_INTEL:
_tcscat_s(driver_filename, MAX_PATH, TEXT("\\winpmem_x86.sys"));
if (GetFileAttributes(driver_filename) == INVALID_FILE_ATTRIBUTES)
{
std::cerr << "ERROR: winpmem_x86.sys not found in current directory. Download it from https://github.com/Velocidex/WinPmem/blob/f044f340dd05658d026b0f293cdfa92876159872/kernel/binaries/winpmem_x86.sys .\n";
std::cerr << "ERROR: winpmem_x86.sys not found in System32 directory. Download it from https://github.com/Velocidex/WinPmem/blob/f044f340dd05658d026b0f293cdfa92876159872/kernel/binaries/winpmem_x86.sys .\n";
std::cerr << "ERROR: Memory bandwidth statistics will not be available.\n";
}
break;
Expand Down
2 changes: 1 addition & 1 deletion src/pcm-memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,7 @@ int mainThrows(int argc, char * argv[])
}
else if (check_argument_equals(*argv, {"--installDriver"}))
{
Driver tmpDrvObject = Driver(Driver::msrLocalPath());
Driver tmpDrvObject = Driver(Driver::msrSystemPath());
if (!tmpDrvObject.start())
{
tcerr << "Can not access CPU counters\n";
Expand Down
2 changes: 1 addition & 1 deletion src/pcm-msr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ int mainThrows(int argc, char * argv[])
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

// WARNING: This driver code (msr.sys) is only for testing purposes, not for production use
Driver drv = Driver(Driver::msrLocalPath());
Driver drv = Driver(Driver::msrSystemPath());
// drv.stop(); // restart driver (usually not needed)
if (!drv.start())
{
Expand Down
4 changes: 2 additions & 2 deletions src/pcm-pcicfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ int mainThrows(int argc, char * argv[])
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

// WARNING: This driver code (msr.sys) is only for testing purposes, not for production use
Driver drv = Driver(Driver::msrLocalPath());
Driver drv = Driver(Driver::msrSystemPath());
// drv.stop(); // restart driver (usually not needed)
if (!drv.start())
{
Expand All @@ -137,7 +137,7 @@ int mainThrows(int argc, char * argv[])
}

// List all PCI devices
forAllDevices([&dec, &verbosity, &pciDB](const uint32 group, const uint32 bus, const uint32 device, const uint32 function, const uint32 device_id)
forAllDevices([&dec, &verbosity, &pciDB](const uint32 group, const uint32 bus, const uint32 device, const uint32 function, const uint32 /* device_id */)
{
if (PciHandleType::exists(group, bus, device, function) == false)
{
Expand Down
159 changes: 159 additions & 0 deletions src/pcm-pcie-collector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026, Intel Corporation
#pragma once

#include "pcm-pcie.h"

#include <condition_variable>
#include <mutex>
#include <thread>
#include <atomic>
#include <vector>
#include <string>
#include <cstdint>
#include <iostream>

class PCIeCollector {
public:
struct SocketBW {
uint64_t readBytes = 0;
uint64_t writeBytes = 0;
};

static PCIeCollector* getInstance() {
static PCIeCollector instance;
return instance.supported_ ? &instance : nullptr;
}

PCIeCollector(PCIeCollector const &) = delete;
PCIeCollector& operator=(PCIeCollector const &) = delete;
PCIeCollector(PCIeCollector &&) = delete;
PCIeCollector& operator=(PCIeCollector &&) = delete;

static constexpr uint32_t kDefaultIntervalMs = 2000;

void startBackground(uint32_t intervalMs = kDefaultIntervalMs) {
bool expected = false;
if (!bgRunning_.compare_exchange_strong(expected, true)) return;
bgThread_ = std::thread([this, intervalMs]() {
while (bgRunning_.load()) {
collect();
std::unique_lock<std::mutex> lk(mu_);
cv_.wait_for(lk, std::chrono::milliseconds(intervalMs),
[this] { return !bgRunning_.load(); });
}
});
}

void stop() {
bool expected = true;
if (!bgRunning_.compare_exchange_strong(expected, false)) return;
cv_.notify_one();
if (bgThread_.joinable()) bgThread_.join();
}

~PCIeCollector() { stop(); }

uint32_t socketCount() const { return socketCount_; }

SocketBW getSocket(uint32_t skt) const {
std::lock_guard<std::mutex> lk(mu_);
if (skt < snapshot_.size()) return snapshot_[skt];
return {};
}

SocketBW getAggregate() const {
std::lock_guard<std::mutex> lk(mu_);
return aggregate_;
}

std::vector<uint64_t> getRawValues(uint32_t skt) const {
std::lock_guard<std::mutex> lk(mu_);
if (skt < rawValues_.size()) return rawValues_[skt];
return {};
}

std::vector<uint64_t> getRawAggregate() const {
std::lock_guard<std::mutex> lk(mu_);
return rawAggValues_;
}

const std::vector<std::string>& eventNames() const { return eventNames_; }
uint32_t numEvents() const { return static_cast<uint32_t>(eventNames_.size()); }
bool isSupported() const { return supported_; }

private:
PCIeCollector() {
try {
PCM* pcm = PCM::getInstance();
static constexpr uint32_t kPmonMultiplier = 1000;
platform_.reset(IPlatform::getPlatform(pcm, false, true, false, kPmonMultiplier));
if (platform_) {
supported_ = true;
socketCount_ = pcm->getNumSockets();
const auto& names = platform_->getEventNames();
eventNames_.assign(names.begin(), names.end());
snapshot_.resize(socketCount_);
rawValues_.resize(socketCount_, std::vector<uint64_t>(eventNames_.size(), 0));
rawAggValues_.resize(eventNames_.size(), 0);
cumSnapshot_.resize(socketCount_);
cumRawValues_.resize(socketCount_, std::vector<uint64_t>(eventNames_.size(), 0));
}
} catch (const std::exception& e) {
std::cerr << "PCIeCollector: " << e.what() << " (PCIe metrics disabled)\n";
supported_ = false;
}
}

void collect() {
if (!platform_ || !bgRunning_.load()) return;
platform_->cleanup();
platform_->getEvents();

SocketBW aggDelta{0, 0};
const uint32_t nEvt = numEvents();

for (uint32_t s = 0; s < socketCount_; ++s) {
uint64_t dr = platform_->getReadBw(s, IPlatform::TOTAL);
uint64_t dw = platform_->getWriteBw(s, IPlatform::TOTAL);
cumSnapshot_[s].readBytes += dr;
cumSnapshot_[s].writeBytes += dw;
aggDelta.readBytes += dr;
aggDelta.writeBytes += dw;
for (uint32_t i = 0; i < nEvt; ++i)
cumRawValues_[s][i] += platform_->event(s, IPlatform::TOTAL, i);
}
cumAggregate_.readBytes += aggDelta.readBytes;
cumAggregate_.writeBytes += aggDelta.writeBytes;

std::vector<uint64_t> rawAgg(nEvt, 0);
for (uint32_t s = 0; s < socketCount_; ++s)
for (uint32_t i = 0; i < nEvt; ++i)
rawAgg[i] += cumRawValues_[s][i];

std::lock_guard<std::mutex> lk(mu_);
snapshot_ = cumSnapshot_;
aggregate_ = cumAggregate_;
rawValues_ = cumRawValues_;
rawAggValues_ = rawAgg;
}

std::unique_ptr<IPlatform> platform_;
bool supported_ = false;
uint32_t socketCount_ = 0;
std::vector<std::string> eventNames_;

std::vector<SocketBW> snapshot_;
SocketBW aggregate_;
std::vector<std::vector<uint64_t>> rawValues_;
std::vector<uint64_t> rawAggValues_;

std::vector<SocketBW> cumSnapshot_;
SocketBW cumAggregate_;
std::vector<std::vector<uint64_t>> cumRawValues_;

mutable std::mutex mu_;
std::condition_variable cv_;
std::thread bgThread_;
std::atomic<bool> bgRunning_{false};
};
30 changes: 1 addition & 29 deletions src/pcm-pcie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,35 +93,7 @@ void print_usage(const string & progname)
cout << "\n";
}

IPlatform *IPlatform::getPlatform(PCM *m, bool csv, bool print_bandwidth, bool print_additional_info, uint32 delay)
{
switch (m->getCPUFamilyModel()) {
case PCM::GNR:
case PCM::GNR_D:
case PCM::SRF:
return new BirchStreamPlatform(m, csv, print_bandwidth, print_additional_info, delay);
case PCM::GRR:
return new LoganvillePlatform(m, csv, print_bandwidth, print_additional_info, delay);
case PCM::SPR:
case PCM::EMR:
return new EagleStreamPlatform(m, csv, print_bandwidth, print_additional_info, delay);
case PCM::ICX:
case PCM::SNOWRIDGE:
return new WhitleyPlatform(m, csv, print_bandwidth, print_additional_info, delay);
case PCM::SKX:
return new PurleyPlatform(m, csv, print_bandwidth, print_additional_info, delay);
case PCM::BDX_DE:
case PCM::BDX:
case PCM::KNL:
case PCM::HASWELLX:
return new GrantleyPlatform(m, csv, print_bandwidth, print_additional_info, delay);
case PCM::IVYTOWN:
case PCM::JAKETOWN:
return new BromolowPlatform(m, csv, print_bandwidth, print_additional_info, delay);
default:
return NULL;
}
}
// getPlatform() is defined inline in pcm-pcie.h.

PCM_MAIN_NOTHROW;

Expand Down
Loading
Loading