├── .gitignore ├── .travis.yml ├── AUTHORS.txt ├── LICENSE.md ├── Makefile ├── README.md ├── include ├── libpfc.h ├── libpfcmsr.h └── meson.build ├── kmod ├── Kbuild ├── Makefile ├── meson.build ├── mesonbuildkmod.sh └── pfckmod.c ├── meson.build └── src ├── libpfc.c ├── meson.build ├── pfcdemo.c ├── pfcdemoreftsc.cpp ├── pfctbhit.c ├── pfctbhitasm.S └── pfcutil.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | kmod.build 3 | *.o 4 | *.so 5 | *.ko 6 | pfcdemo 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: required 3 | 4 | addons: 5 | apt: 6 | sources: 7 | - deadsnakes 8 | packages: 9 | - linux-headers-4.4.0-101-generic 10 | - python3.6 11 | - python3.6-dev 12 | 13 | matrix: 14 | include: 15 | - env: BUILD=MAKE 16 | script: 17 | - make 18 | - env: BUILD=meson 19 | install: 20 | - mkdir build 21 | - export PATH="`pwd`/build:${PATH}" 22 | - wget https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-linux.zip && unzip -q ninja-linux.zip -d build 23 | - curl https://bootstrap.pypa.io/get-pip.py | sudo python3.6 24 | - sudo python3.6 -m pip install meson 25 | - meson --version 26 | script: 27 | - uname -r && cd build && meson .. -Dbuildtype=release --prefix=$HOME/.local 28 | - ninja 29 | - ninja install 30 | 31 | -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Olexa Bilaniuk 2 | Travis Downs 3 | Evan Nemerson 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Olexa Bilaniuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for libpfc userland and kernel components. 2 | # The preferred build method for libpfc is the combination of meson and ninja, and so this Makefile 3 | # may not always be up to date. It is provided for projects that prefer make-only builds. 4 | 5 | .PHONY : all clean 6 | 7 | vpath %.c src 8 | vpath %.h include 9 | 10 | ifeq ($(CC), pgcc) 11 | CFLAGS += -O0 -g -Iinclude -c99 12 | else 13 | CFLAGS += -Wall -Winvalid-pch -O0 -g -Iinclude -std=gnu99 14 | endif 15 | 16 | SHAREDLIB_FLAGS = -Wl,--no-undefined -Wl,--as-needed -shared -fPIC -Wl,-soname,libpfc.so '-Wl,-rpath,$$ORIGIN/' 17 | 18 | all : libpfc.so pfcdemo pfc.ko 19 | 20 | libpfc.o : libpfc.c libpfc.h libpfcmsr.h 21 | $(CC) $(CFLAGS) -fPIC -c $< -o libpfc.o 22 | 23 | libpfc.so : libpfc.o 24 | $(CC) $(SHAREDLIB_FLAGS) libpfc.o -o libpfc.so 25 | 26 | pfcdemo.o : pfcdemo.c libpfc.h 27 | $(CC) $(CFLAGS) -c $< -o pfcdemo.o 28 | 29 | pfcdemo : pfcdemo.o libpfc.so 30 | $(CC) '-Wl,-rpath,$$ORIGIN/' -L. pfcdemo.o -lpfc -lm -o pfcdemo 31 | 32 | pfc.ko : kmod/pfckmod.c kmod/Makefile 33 | rm -rf kmod.build 34 | cp -r kmod kmod.build 35 | cd kmod.build && $(MAKE) MAKEFLAGS= 36 | mv kmod.build/pfc.ko . 37 | rm -rf kmod.build 38 | 39 | clean : 40 | rm -f *.o libpfc.so pfcdemo pfc.ko 41 | rm -rf kmod.build 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/obilaniu/libpfc.svg?branch=master)](https://travis-ci.org/obilaniu/libpfc) 2 | 3 | # libpfc 4 | 5 | _A small library and kernel module for easy access to x86 performance monitor counters under Linux._ 6 | 7 | ## Intro 8 | 9 | `libpfc` is both a library and a Linux Loadable Kernel Module (LKM) that permits near-0-overhead use of the performance monitoring capabilities of modern x86 processors. 10 | 11 | ## Getting the code, building and installing 12 | 13 | `libpfc`'s source code can be found on [Github](https://github.com/obilaniu/libpfc). It uses the [Meson 0.41.0](http://mesonbuild.com/) build system, which itself requires [Ninja](https://ninja-build.org/), but itself has no dependencies. It can be build as follows. 14 | 15 | ```shell 16 | cd /path/to/some/folder 17 | git clone 'https://github.com/obilaniu/libpfc.git' libpfc 18 | mkdir libpfc/build 19 | cd libpfc/build 20 | meson.py .. -Dbuildtype=release --prefix=/path/to/prefixdir # Such as $HOME/.local 21 | ninja 22 | ninja install 23 | ``` 24 | 25 | Make sure a path to `libpfc.so` is present in `LD_LIBRARY_PATH`. 26 | 27 | ## Loading the kernel module `pfc.ko` 28 | 29 | The kernel module is installed in `/path/to/prefixdir/bin/pfc.ko`. To load it into the kernel, ensure that no other entity is using the `perf` functionality, including other kernel modules such as NMI watchdogs. As of Linux 4.4.5, this can be done, with root privileges, as follows: 30 | 31 | ```shell 32 | modprobe -ar iTCO_wdt iTCO_vendor_support 33 | echo 0 > /proc/sys/kernel/nmi_watchdog 34 | echo 2 > /sys/bus/event_source/devices/cpu/rdpmc 35 | insmod /path/to/prefixdir/bin/pfc.ko 36 | ``` 37 | 38 | In particular, in order to execute the commands with root privileges using `sudo`: 39 | 40 | ```shell 41 | modprobe -ar iTCO_wdt iTCO_vendor_support 42 | sudo sh -c 'echo 0 > /proc/sys/kernel/nmi_watchdog' 43 | sudo sh -c 'echo 2 > /sys/bus/event_source/devices/cpu/rdpmc' 44 | sudo insmod /path/to/prefixdir/bin/pfc.ko 45 | ``` 46 | 47 | It may be possible that `insmod /path/to/prefixdir/bin/pfc.ko` may fail with the error `Key was rejected by service` in case SecureBoot is enabled. To actually see if that's the case, running `mokutil --sb-state` will output the enable state of SecureBoot. 48 | 49 | ## Using `libpfc` in user-space 50 | 51 | ### Include 52 | 53 | `#include "libpfc.h"` 54 | 55 | This provides the API as well as two data types, `PFC_CNT` and `PFC_CFG`, which are both 64-bit 2's complement integers. 56 | 57 | ### Initialize the library 58 | 59 | `pfcInit()` 60 | 61 | Initializes the library. If the magic files exposed by `pfc.ko` (`/sys/module/pfc/config` and `/sys/module/pfc/counts`) cannot be opened, prints an error message. 62 | 63 | ### Pin thread to single core 64 | 65 | `pfcPinThread(coreNum)` 66 | 67 | Pins the thread to one selected core. This is important both because core migration interferes with performance statistics, and because `pfc.ko` does not virtualize or track counter values in any way. It is thus crucial that the process occupies a single core and that no other processor occupy it. 68 | 69 | ### Parse configurations 70 | 71 | `pfcParseCfg()` 72 | 73 | Takes a string describing a general-purpose counter event to track, and computes the 64-bit `PFC_CFG` value to be written to the MSR that would cause it to monitor said event. 74 | 75 | The fixed-function performance counters are enabled using configuration `2` and disabled with configuration `0`. No other configuration is allowed. 76 | 77 | ### Read/Write Configs & Counts 78 | 79 | ```c 80 | pfcRdCfgs(k, n, cfgs); 81 | pfcWrCfgs(k, n, cfgs); 82 | pfcRdCnts(k, n, cnts); 83 | pfcWrCnts(k, n, cnts); 84 | ``` 85 | 86 | Reads and writes hardware counter and configuration values for the `n` counters starting at counter `k`. On Haswell, counters 0, 1 and 2 are fixed-function counters, while counters 3, 4, 5 and 6 are general-purpose counters. 87 | 88 | ## Timing Code 89 | 90 | `libpfc.h` defines two assembler macros and one function for timing. 91 | 92 | - `PFCSTART(cnts)` reads the 7 hardware counters using `rdpmc` in a carefully-timed manner and _subtracts_ them out of the current value in `cnt[0..6]`. 93 | - `PFCEND(cnts)` reads the 7 hardware counters using `rdpmc` in a carefully-timed manner and _adds_ them into `cnt[0..6]`. 94 | - Since `PFCSTART/PFCEND` have non-negligible but identical cost and highly-predictable behaviour, the bias that they introduce in the recorded counter values can be computed and removed. 95 | 96 | `pfcRemoveBias(cnts, mul)` measures the cost of a pair `PFCSTART/PFCEND` with nothing in-between with the current counter configurations, and subtracts `mul` copies of that cost out of `cnts`. 97 | 98 | Therefore, to measure a snippet of code, one does as follows: 99 | 100 | ```c 101 | /* Initialization */ 102 | pfcInit(); 103 | pfcPinThread(3); /* Ideally, to any core other than 0 */ 104 | 105 | /* Compute configurations, possibly using `pfcParseConfig()` */ 106 | PFC_CFG cfgs[7] = {...}; 107 | PFC_CNT cnts[7] = {0,0,0,0,0,0,0}; 108 | 109 | /* Write configurations and starting values */ 110 | pfcWrCfgs(0, 7, cfgs); 111 | pfcWrCnts(0, 7, cnts); 112 | 113 | /**** HOT SECTION ****/ 114 | PFCSTART(cnts); 115 | /* Snippet to time */ 116 | PFCEND(cnts); 117 | /**** END HOT SECTION ****/ 118 | 119 | /* Bias compensation */ 120 | pfcRemoveBias(cnts, 1); 121 | 122 | /* Use values in cnts[0..6]. */ 123 | ``` 124 | 125 | An example of this process is in [`pfcdemo.c:71`](https://github.com/obilaniu/libpfc/blob/master/src/pfcdemo.c#L71). 126 | -------------------------------------------------------------------------------- /include/libpfc.h: -------------------------------------------------------------------------------- 1 | /* Include Guards */ 2 | #ifndef LIBPFC_H 3 | #define LIBPFC_H 4 | 5 | 6 | /* Includes */ 7 | #include 8 | #include "libpfcmsr.h" 9 | 10 | 11 | 12 | /* Data types */ 13 | typedef uint64_t PFC_CFG; 14 | typedef int64_t PFC_CNT; 15 | 16 | /** 17 | * The next three definitions are the positions of the three fixed counters within 18 | * the PFC_CNT array 19 | * See also "Table 18-2. Association of Fixed-Function Performance Counters with Architectural Performance Events" 20 | * in the Intel SDM Vol 3. 21 | */ 22 | 23 | #define PFC_FIXEDCNT_INSTRUCTIONS_RETIRED 0 /* INST_RETIRED.ANY */ 24 | #define PFC_FIXEDCNT_CPU_CLK_UNHALTED 1 /* CPU_CLK_UNHALTED.THREAD */ 25 | #define PFC_FIXEDCNT_CPU_CLK_REF_TSC 2 /* CPU_CLK_UNHALTED.REF_TSC */ 26 | 27 | /** 28 | * Error Codes 29 | * 30 | * Functions that return an int error may return one of the following codes, which can be turned 31 | * into a human-readable string using pfcErrorString(). 32 | */ 33 | 34 | #define PFC_ERR_OK (-0) /* OK */ 35 | #define PFC_ERR_OPENING_SYSFILE (-1) /* Couldn't open the /sys/modules/pfc files, kernel module probably not loaded */ 36 | #define PFC_ERR_PWRITE_FAILED (-2) /* A pwrite() call returned error (check errno?) */ 37 | #define PFC_ERR_PWRITE_TOO_FEW (-3) /* A pwrite() call wrote less than the expected number of bytes */ 38 | #define PFC_ERR_CPU_PIN_FAILED (-4) /* CPU pinning failed, probably in sched_setaffinity() */ 39 | #define PFC_ERR_CR4_PCE_NOT_SET (-5) /* Driver reported that cr4.pce wasn't set, or there was somehow an issue reading it */ 40 | #define PFC_ERR_AFFINITY_FAILED (-6) /* Setting CPU affinity failed (perhaps affinity is set externally excluding CPU 0?) */ 41 | #define PFC_ERR_READING_MASKS (-7) /* Didn't read the expected number of mask bytes from the sysfs */ 42 | 43 | 44 | /* Extern "C" Guard */ 45 | #ifdef __cplusplus 46 | extern "C"{ 47 | #endif 48 | 49 | /* Functions */ 50 | 51 | /** 52 | * Initialize and finalize library. 53 | * 54 | * Returns 0 on success and non-zero on failure. 55 | */ 56 | 57 | int pfcInit (void); 58 | void pfcFini (void); 59 | 60 | /** 61 | * Pins calling thread to given core, returns zero on success, non-zero otherwise. 62 | */ 63 | int pfcPinThread (int core); 64 | 65 | /** 66 | * Read and write the configurations and values of the n counters starting at 67 | * counter k. 68 | */ 69 | 70 | /* Writes n configuration values from cfg, starting at counter k. Returns 0 on success or an error code otherwise. */ 71 | int pfcWrCfgs (int k, int n, const PFC_CFG* cfg); 72 | int pfcRdCfgs (int k, int n, PFC_CFG* cfg); 73 | int pfcWrCnts (int k, int n, const PFC_CNT* cnt); 74 | int pfcRdCnts (int k, int n, PFC_CNT* cnt); 75 | int pfcRdMSR (uint64_t off, uint64_t* msr); 76 | 77 | /** 78 | * Translate argument to configuration. 79 | */ 80 | 81 | PFC_CFG pfcParseCfg (const char* s); 82 | 83 | /** 84 | * Dump out available events 85 | */ 86 | 87 | void pfcDumpEvts (void); 88 | 89 | 90 | /********************* 91 | ***** MACROS ***** 92 | *********************/ 93 | 94 | #define _pfc_asm_code_cnt_read_(op, rcx, off) \ 95 | "\n\tmov $"#rcx", %%rcx " \ 96 | "\n\trdpmc " \ 97 | "\n\tshl $32, %%rdx " \ 98 | "\n\tor %%rax, %%rdx " \ 99 | "\n\t"#op" %%rdx, "#off"(%0) " \ 100 | "\n\t" 101 | 102 | 103 | #define _pfc_asm_code_(op) \ 104 | "\n\tlfence " \ 105 | _pfc_asm_code_cnt_read_(op, 0x40000000, 0) \ 106 | _pfc_asm_code_cnt_read_(op, 0x40000001, 8) \ 107 | _pfc_asm_code_cnt_read_(op, 0x40000002, 16) \ 108 | _pfc_asm_code_cnt_read_(op, 0x00000000, 24) \ 109 | _pfc_asm_code_cnt_read_(op, 0x00000001, 32) \ 110 | _pfc_asm_code_cnt_read_(op, 0x00000002, 40) \ 111 | _pfc_asm_code_cnt_read_(op, 0x00000003, 48) \ 112 | "\n\tlfence " \ 113 | 114 | #define _pfc_macro_(b, op) \ 115 | asm volatile( \ 116 | _pfc_asm_code_(op) \ 117 | : /* Outputs */ \ 118 | : "r"((b)) /* Inputs */ \ 119 | : "memory", "rax", "rcx", "rdx" \ 120 | ) 121 | 122 | /** 123 | * The PFCSTART macro takes a single pointer to a 7-element array of 64-bit 124 | * integers and *subtracts* out of them the start counter values. 125 | */ 126 | 127 | #define PFCSTART(b) _pfc_macro_((b), sub) 128 | 129 | /** 130 | * The PFCEND macro is *exactly* the same as the PFCSTART macro, down to the 131 | * size and scheduling of every instruction, *except* that it *adds* the end 132 | * counter-values into the array. 133 | * 134 | * The end result is buffer[i] = (buffer[i] - start[i]) + end[i], which amounts 135 | * to the same thing as buffer[i] += (end[i] - start[i])! 136 | * 137 | * So, if the buffer is initialized beforehand to 0, then at the end it will 138 | * contain a biased count of the events currently selected on the PMCs. The 139 | * bias comes from the 37 instructions of PFCSTART and PFCEND; This bias is 140 | * roughly constant and can be estimated and removed by calling 141 | * pfcRemoveBias(b, 1) with no intervening change to the counters. 142 | */ 143 | 144 | #define PFCEND(b) _pfc_macro_((b), add) 145 | 146 | /** 147 | * Remove mul times from b the counter bias due to PFCSTART/PFCEND. 148 | */ 149 | 150 | void pfcRemoveBias (PFC_CNT* b, int64_t mul); 151 | 152 | /** 153 | * Return a string representation of a libpfc error code, such as the one 154 | * returned by pfcInit(). 155 | */ 156 | 157 | const char *pfcErrorString(int err); 158 | 159 | 160 | /* End Extern "C" Guard */ 161 | #ifdef __cplusplus 162 | } 163 | #endif 164 | 165 | #endif // LIBPFC_H 166 | 167 | -------------------------------------------------------------------------------- /include/libpfcmsr.h: -------------------------------------------------------------------------------- 1 | /* Include Guards */ 2 | #ifndef LIBPFCMSR_H 3 | #define LIBPFCMSR_H 4 | 5 | 6 | 7 | /** 8 | * MSRs we know of, sorted by offset. 9 | */ 10 | 11 | #ifndef MSR_PLATFORM_INFO 12 | #define MSR_PLATFORM_INFO 0x0CE 13 | #endif 14 | #ifndef MSR_IA32_PERFEVTSEL0 15 | #define MSR_IA32_PERFEVTSEL0 0x186 16 | #endif 17 | #ifndef MSR_IA32_PERF_STATUS 18 | #define MSR_IA32_PERF_STATUS 0x198 19 | #endif 20 | #ifndef MSR_IA32_PERF_CTL 21 | #define MSR_IA32_PERF_CTL 0x199 22 | #endif 23 | #ifndef MSR_IA32_CLOCK_MODULATION 24 | #define MSR_IA32_CLOCK_MODULATION 0x19A 25 | #endif 26 | #ifndef MSR_IA32_THERM_INTERRUPT 27 | #define MSR_IA32_THERM_INTERRUPT 0x19B 28 | #endif 29 | #ifndef MSR_IA32_THERM_STATUS 30 | #define MSR_IA32_THERM_STATUS 0x19C 31 | #endif 32 | #ifndef MSR_IA32_MISC_ENABLE 33 | #define MSR_IA32_MISC_ENABLE 0x1A0 34 | #endif 35 | #ifndef MSR_IA32_TEMPERATURE_TARGET 36 | #define MSR_IA32_TEMPERATURE_TARGET 0x1A2 37 | #endif 38 | #ifndef MSR_IA32_ENERGY_PERF_BIAS 39 | #define MSR_IA32_ENERGY_PERF_BIAS 0x1B0 40 | #endif 41 | #ifndef MSR_IA32_PACKAGE_THERM_STATUS 42 | #define MSR_IA32_PACKAGE_THERM_STATUS 0x1B1 43 | #endif 44 | #ifndef MSR_IA32_PACKAGE_THERM_INTERRUPT 45 | #define MSR_IA32_PACKAGE_THERM_INTERRUPT 0x1B2 46 | #endif 47 | #ifndef MSR_IA32_FIXED_CTR0 48 | #define MSR_IA32_FIXED_CTR0 0x309 49 | #endif 50 | #ifndef MSR_IA32_PERF_CAPABILITIES 51 | #define MSR_IA32_PERF_CAPABILITIES 0x345 52 | #endif 53 | #ifndef MSR_IA32_FIXED_CTR_CTRL 54 | #define MSR_IA32_FIXED_CTR_CTRL 0x38D 55 | #endif 56 | #ifndef MSR_IA32_PERF_GLOBAL_STATUS 57 | #define MSR_IA32_PERF_GLOBAL_STATUS 0x38E 58 | #endif 59 | #ifndef MSR_IA32_PERF_GLOBAL_CTRL 60 | #define MSR_IA32_PERF_GLOBAL_CTRL 0x38F 61 | #endif 62 | #ifndef MSR_IA32_PERF_GLOBAL_OVF_CTRL 63 | #define MSR_IA32_PERF_GLOBAL_OVF_CTRL 0x390 64 | #endif 65 | #ifndef MSR_PEBS_FRONTEND 66 | #define MSR_PEBS_FRONTEND 0x3F7 67 | #endif 68 | #ifndef MSR_IA32_A_PMC0 69 | #define MSR_IA32_A_PMC0 0x4C1 70 | #endif 71 | #ifndef MSR_CORE_PERF_LIMIT_REASONS 72 | #define MSR_CORE_PERF_LIMIT_REASONS 0x690 73 | #endif 74 | #ifndef MSR_IA32_PKG_HDC_CTL 75 | #define MSR_IA32_PKG_HDC_CTL 0xDB0 76 | #endif 77 | #ifndef MSR_IA32_PM_CTL1 78 | #define MSR_IA32_PM_CTL1 0xDB1 79 | #endif 80 | #ifndef MSR_IA32_THREAD_STALL 81 | #define MSR_IA32_THREAD_STALL 0xDB2 82 | #endif 83 | 84 | 85 | /* Notes */ 86 | 87 | /** 186+x IA32_PERFEVTSELx - Performance Event Selection, ArchPerfMon v3 88 | * 89 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 90 | * {................................################################} 91 | * | |||||||||| || | 92 | * Counter Mask -----------------------------------^^^^^^^^||||||||| || | 93 | * Invert Counter Mask ------------------------------------^|||||||| || | 94 | * Enable Counter ------------------------------------------^||||||| || | 95 | * AnyThread ------------------------------------------------^|||||| || | 96 | * APIC Interrupt Enable -------------------------------------^||||| || | 97 | * Pin Control ------------------------------------------------^|||| || | 98 | * Edge Detect -------------------------------------------------^||| || | 99 | * Operating System Mode ----------------------------------------^|| || | 100 | * User Mode -----------------------------------------------------^| || | 101 | * Unit Mask (UMASK) ----------------------------------------------^^^^^^^^| | 102 | * Event Select -----------------------------------------------------------^^^^^^^^ 103 | */ 104 | /** 309+x IA32_FIXED_CTRx - Fixed-Function Counter, ArchPerfMon v3 105 | * 106 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 107 | * {????????????????????????????????????????????????????????????????} 108 | * | | 109 | * Counter Value --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 110 | * 111 | * NB: Number of FF counters determined by CPUID.0x0A.EDX[ 4: 0] 112 | * NB: ???? FF counter bitwidth determined by CPUID.0x0A.EDX[12: 5] 113 | */ 114 | /** 345 IA32_PERF_CAPABILITIES - Performance Capabilities Enumeration 115 | * 116 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 117 | * {..................................................##############} 118 | * ||| |||| | 119 | * Full-Width Write -------------------------------------------------^|| |||| | 120 | * SMM Freeze --------------------------------------------------------^| |||| | 121 | * PEBS Record Format -------------------------------------------------^^^^||| | 122 | * PEBS Arch Regs ---------------------------------------------------------^|| | 123 | * PEBS Trap ---------------------------------------------------------------^| | 124 | * LBR Format ---------------------------------------------------------------^^^^^^ 125 | */ 126 | /** 38D IA32_FIXED_CTR_CTRL - Fixed Counter Controls, ArchPerfMon v3 127 | * 128 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 129 | * {....................................................############} 130 | * | || ||||| 131 | * IA32_FIXED_CTR2 controls ------------------------------------------^^^^| ||||| 132 | * IA32_FIXED_CTR1 controls ----------------------------------------------^^^^|||| 133 | * |||| 134 | * IA32_FIXED_CTR0 controls: |||| 135 | * IA32_FIXED_CTR0 PMI --------------------------------------------------------^||| 136 | * IA32_FIXED_CTR0 AnyThread ---------------------------------------------------^|| 137 | * IA32_FIXED_CTR0 enable (0:Disable 1:OS 2:User 3:All) -------------------------^^ 138 | */ 139 | /** 38E IA32_PERF_GLOBAL_STATUS - Global Overflow Status, ArchPerfMon v3 140 | * 141 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 142 | * {###..........................###............................####} 143 | * ||| ||| |||| 144 | * CondChgd ----^|| ||| |||| 145 | * OvfDSBuffer --^| ||| |||| 146 | * OvfUncore -----^ ||| |||| 147 | * IA32_FIXED_CTR2 Overflow -----------------^|| |||| 148 | * IA32_FIXED_CTR1 Overflow ------------------^| |||| 149 | * IA32_FIXED_CTR0 Overflow -------------------^ |||| 150 | * IA32_PMC(N-1) Overflow ------------------------------------------------^||| 151 | * .... -------------------------------------------------^|| 152 | * IA32_PMC1 Overflow --------------------------------------------------^| 153 | * IA32_PMC0 Overflow ---------------------------------------------------^ 154 | */ 155 | /** 38F IA32_PERF_GLOBAL_CTRL - Global Enable Controls, ArchPerfMon v3 156 | * 157 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 158 | * {.............................###............................####} 159 | * ||| |||| 160 | * IA32_FIXED_CTR2 enable ----------------------^|| |||| 161 | * IA32_FIXED_CTR1 enable -----------------------^| |||| 162 | * IA32_FIXED_CTR0 enable ------------------------^ |||| 163 | * IA32_PMC(N-1) enable -----------------------------------------------------^||| 164 | * .... ------------------------------------------------------^|| 165 | * IA32_PMC1 enable -------------------------------------------------------^| 166 | * IA32_PMC0 enable --------------------------------------------------------^ 167 | */ 168 | /** 390 IA32_PERF_GLOBAL_OVF_CTRL - Global Overflow Control, ArchPerfMon v3 169 | * 170 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 171 | * {###..........................###............................####} 172 | * ||| ||| |||| 173 | * ClrCondChgd ----^|| ||| |||| 174 | * ClrOvfDSBuffer --^| ||| |||| 175 | * ClrOvfUncore -----^ ||| |||| 176 | * IA32_FIXED_CTR2 ClrOverflow -----------------^|| |||| 177 | * IA32_FIXED_CTR1 ClrOverflow ------------------^| |||| 178 | * IA32_FIXED_CTR0 ClrOverflow -------------------^ |||| 179 | * IA32_PMC(N-1) ClrOverflow ------------------------------------------------^||| 180 | * .... -------------------------------------------------^|| 181 | * IA32_PMC1 ClrOverflow --------------------------------------------------^| 182 | * IA32_PMC0 ClrOverflow ---------------------------------------------------^ 183 | */ 184 | /** 3F7 MSR_PEBS_FRONTEND - Front-End Precise Event Condition Select, ArchPerfMon v4 185 | * 186 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 187 | * {.........................................###############...#.###} 188 | * | || | | | | 189 | * IDQ Bubble Width Specifier -----------------------------^^^| | | | | 190 | * IDQ Bubble Length Specifier --------------------------------^^^^^^^^^^^^ | | | 191 | * Event Code Select High -----------------------------------------------^ | | 192 | * Event Code Select -------------------------------------------------^^^ 193 | */ 194 | /** 4C1+x IA32_A_PMCx - General-Purpose Counter, ArchPerfMon v3 195 | * 196 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 197 | * {????????????????????????????????????????????????????????????????} 198 | * | | 199 | * Counter Value --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 200 | * 201 | * NB: Number of GP counters determined by CPUID.0x0A.EAX[15: 8] 202 | * NB: ???? GP counter bitwidth determined by CPUID.0x0A.EAX[23:16] 203 | */ 204 | /** DB2 IA32_THREAD_STALL - HDC Forced-Idle Cycle Counter 205 | * 206 | * Available if CPUID.06H:EAX[bit 13] = 1 207 | * 208 | * /63/60 /56 /48 /40 /32 /24 /16 /08 /00 209 | * {????????????????????????????????????????????????????????????????} 210 | * | | 211 | * Counter Value --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 212 | * 213 | * Accumulate stalled cycles on this logical processor due to HDC forced idling. 214 | */ 215 | 216 | 217 | #endif /* End Include Guards */ 218 | 219 | -------------------------------------------------------------------------------- /include/meson.build: -------------------------------------------------------------------------------- 1 | # The folder include/ represents our public interface/API. 2 | 3 | libpfcIncs = include_directories('.') 4 | install_headers('libpfc.h', 'libpfcmsr.h') 5 | -------------------------------------------------------------------------------- /kmod/Kbuild: -------------------------------------------------------------------------------- 1 | obj-m += pfc.o 2 | pfc-y += pfckmod.o 3 | ccflags-y += -I$(src)/../include 4 | -------------------------------------------------------------------------------- /kmod/Makefile: -------------------------------------------------------------------------------- 1 | # This variable specifies the kernel name/version string that we'll try to 2 | # build against: normally you never want to set this since just want to pick 3 | # up the current kernel version using uname -r. It's here to allow you to 4 | # "cross compile" against a different kernel version which is useful on 5 | # Travis-CI, for example, where the available linux-header versions don't 6 | # match the actual kernel version 7 | ifndef PFC_KMOD_KERNEL_NAME 8 | PFC_KMOD_KERNEL_NAME = $(shell uname -r) 9 | endif 10 | 11 | all: 12 | make -C /lib/modules/$(PFC_KMOD_KERNEL_NAME)/build M=$(PWD) modules 13 | 14 | clean: 15 | make -C /lib/modules/$(PFC_KMOD_KERNEL_NAME)/build M=$(PWD) clean 16 | -------------------------------------------------------------------------------- /kmod/meson.build: -------------------------------------------------------------------------------- 1 | # This folder contains the sources to the kernel module. It is self-contained. 2 | 3 | kmodIncs = include_directories('../include') 4 | kmodSrcs = files('pfckmod.c', 'Makefile', 'mesonbuildkmod.sh') 5 | 6 | 7 | kmod = custom_target('kmod', 8 | output : ['pfc.ko'], 9 | input : kmodSrcs, 10 | command : [meson.source_root()+'/kmod/mesonbuildkmod.sh', meson.source_root(), meson.build_root()], 11 | install : true, 12 | install_dir : 'bin', 13 | build_by_default : true 14 | ) 15 | -------------------------------------------------------------------------------- /kmod/mesonbuildkmod.sh: -------------------------------------------------------------------------------- 1 | mkdir -p "$2/kmod/kmod.dir" 2 | cp -Rf "$1/kmod/"* "$2/kmod/kmod.dir" 3 | rm -f "$2/kmod/kmod.dir/meson.build" "$2/kmod/kmod.dir/mesonbuild.sh" 4 | cd "$2/kmod/kmod.dir" 5 | sed -i -e "s|\$(src)|$1/kmod|" "$2/kmod/kmod.dir/Kbuild" 6 | make 7 | mv "pfc.ko" "$2/kmod" 8 | -------------------------------------------------------------------------------- /kmod/pfckmod.c: -------------------------------------------------------------------------------- 1 | /* Includes */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "libpfcmsr.h" 10 | 11 | 12 | /* Defines */ 13 | 14 | /** 15 | * Maximum number of PMCs (fixed-function and general-purpose combined) 16 | */ 17 | 18 | #define MAXPMC 25 19 | 20 | /** 21 | * Conditional logging. 22 | */ 23 | 24 | #define PRINTV(...) if (verbose) { printk(KERN_INFO __VA_ARGS__); } 25 | 26 | 27 | /* Data Structure Typedefs */ 28 | struct CPUID_LEAF; 29 | typedef struct CPUID_LEAF CPUID_LEAF; 30 | 31 | 32 | /* Data Structure Definitions */ 33 | struct CPUID_LEAF{ 34 | uint32_t a, b, c, d; 35 | }; 36 | 37 | 38 | /* Forward Declarations */ 39 | static ssize_t pfcCfgRd(struct file* f, 40 | struct kobject* kobj, 41 | struct bin_attribute* binattr, 42 | char* buf, 43 | loff_t off, 44 | size_t len); 45 | static ssize_t pfcCfgWr(struct file* f, 46 | struct kobject* kobj, 47 | struct bin_attribute* binattr, 48 | char* buf, 49 | loff_t off, 50 | size_t len); 51 | static ssize_t pfcMskRd(struct file* f, 52 | struct kobject* kobj, 53 | struct bin_attribute* binattr, 54 | char* buf, 55 | loff_t off, 56 | size_t len); 57 | static ssize_t pfcCntRd(struct file* f, 58 | struct kobject* kobj, 59 | struct bin_attribute* binattr, 60 | char* buf, 61 | loff_t off, 62 | size_t len); 63 | static ssize_t pfcCntWr(struct file* f, 64 | struct kobject* kobj, 65 | struct bin_attribute* binattr, 66 | char* buf, 67 | loff_t off, 68 | size_t len); 69 | static ssize_t pfcMsrRd (struct file* f, 70 | struct kobject* kobj, 71 | struct bin_attribute* binattr, 72 | char* buf, 73 | loff_t off, 74 | size_t len); 75 | static ssize_t pfcVerboseRd(struct kobject* kobj, 76 | struct kobj_attribute* attr, 77 | char* buf); 78 | static ssize_t pfcVerboseWr(struct kobject* kobj, 79 | struct kobj_attribute* attr, 80 | const char* buf, 81 | size_t count); 82 | static ssize_t pfcCR4PceRd(struct kobject* kobj, 83 | struct kobj_attribute* attr, 84 | char* buf); 85 | static int __init pfcInit(void); 86 | static void pfcExit(void); 87 | 88 | 89 | 90 | /* Global Variables & Constants */ 91 | static CPUID_LEAF leaf0 = {0,0,0,0}, /* Highest Value for Basic Processor Information and the Vendor Identification String */ 92 | leaf1 = {0,0,0,0}, /* Model, Family, Stepping Information */ 93 | leaf6 = {0,0,0,0}, /* Thermal and Power Management Features */ 94 | leafA = {0,0,0,0}, /* Architectural Performance Monitoring Features */ 95 | leaf80000000 = {0,0,0,0}, /* Highest Value for Extended Processor Information */ 96 | leaf80000001 = {0,0,0,0}, /* Extended Processor Signature & Feature Bits */ 97 | leaf80000002 = {0,0,0,0}, /* Processor Brand String 0 */ 98 | leaf80000003 = {0,0,0,0}, /* Processor Brand String 1 */ 99 | leaf80000004 = {0,0,0,0}; /* Processor Brand String 2 */ 100 | static unsigned family = 0; 101 | static unsigned model = 0; 102 | static unsigned stepping = 0; 103 | static unsigned exfamily = 0; 104 | static unsigned exmodel = 0; 105 | static unsigned dispFamily = 0; 106 | static unsigned dispModel = 0; 107 | static uint32_t maxLeaf = 0; 108 | static uint32_t maxExtendedLeaf = 0; 109 | static char procBrandString[49] = {0}; 110 | static int pmcArchVer = 0; 111 | static int pmcFf = 0; 112 | static int pmcGp = 0; 113 | static int pmcFfBitwidth = 0; 114 | static int pmcGpBitwidth = 0; 115 | static uint64_t pmcFfMask = 0; 116 | static uint64_t pmcGpMask = 0; 117 | static int pmcStartFf = 0; 118 | static int pmcEndFf = 0; 119 | static int pmcStartGp = 0; 120 | static int pmcEndGp = 0; 121 | static int fullWidthWrites = 0; 122 | static int verbose = 0; 123 | 124 | /** 125 | * The counters consist in the following MSRs on Core i7: 126 | * [0 ] IA32_FIXED_CTR0: Fixed-function - Retired Instructions 127 | * [1 ] IA32_FIXED_CTR1: Fixed-function - Unhalted Core CCs 128 | * [2 ] IA32_FIXED_CTR2: Fixed-function - Unhalted Reference CCs 129 | * [3+ ] IA32_A_PMCx: General-purpose - Configurable 130 | */ 131 | 132 | 133 | /* Attribute Hierarchy */ 134 | /* Binary attributes */ 135 | static const struct bin_attribute PFC_ATTR_config = { 136 | .attr = {.name="config", .mode=0660}, 137 | .size = MAXPMC*sizeof(uint64_t), 138 | .read = pfcCfgRd, 139 | .write = pfcCfgWr 140 | }; 141 | static const struct bin_attribute PFC_ATTR_masks = { 142 | .attr = {.name="masks", .mode=0440}, 143 | .size = MAXPMC*sizeof(uint64_t), 144 | .read = pfcMskRd, 145 | }; 146 | static const struct bin_attribute PFC_ATTR_counts = { 147 | .attr = {.name="counts", .mode=0660}, 148 | .size = MAXPMC*sizeof(uint64_t), 149 | .read = pfcCntRd, 150 | .write = pfcCntWr 151 | }; 152 | static const struct bin_attribute PFC_ATTR_msr = { 153 | .attr = {.name="msr", .mode=0660}, 154 | .size = 0, 155 | .read = pfcMsrRd 156 | }; 157 | static const struct bin_attribute* PFC_BIN_ATTR_GRP_LIST[] = { 158 | &PFC_ATTR_config, 159 | &PFC_ATTR_masks, 160 | &PFC_ATTR_counts, 161 | &PFC_ATTR_msr, 162 | NULL 163 | }; 164 | 165 | /* string attributes */ 166 | static struct kobj_attribute PFC_ATTR_verbose = { 167 | .attr = {.name="verbose", .mode=0660}, 168 | .show = pfcVerboseRd, 169 | .store = pfcVerboseWr 170 | 171 | }; 172 | 173 | static struct kobj_attribute PFC_ATTR_cr4pce = { 174 | .attr = {.name="cr4.pce", .mode=0440}, 175 | .show = pfcCR4PceRd 176 | 177 | }; 178 | 179 | static struct attribute* PFC_STR_ATTR_GRP_LIST[] = { 180 | &PFC_ATTR_verbose.attr, 181 | &PFC_ATTR_cr4pce.attr, 182 | NULL 183 | }; 184 | 185 | /* the attribute group, which points to all binary and string attributes */ 186 | static const struct attribute_group PFC_ATTR_GRP = { 187 | .name = NULL, 188 | .attrs = PFC_STR_ATTR_GRP_LIST, 189 | .bin_attrs = (struct bin_attribute **)PFC_BIN_ATTR_GRP_LIST 190 | }; 191 | 192 | 193 | 194 | 195 | /* Static Function Definitions */ 196 | 197 | /***************** UTILITIES *****************/ 198 | 199 | /** 200 | * @brief Ones Vector. 201 | * 202 | * Generate a 64-bit bitvector of ones with 203 | * 204 | * val[63 :n+k] = 0 205 | * val[n+k-1: k] = 1 206 | * val[ k-1: 0] = 0 207 | * 208 | * i.e. where the n bits starting at k counting from the LSB are all set, and 209 | * all other bits are 0. 210 | */ 211 | 212 | static uint64_t OV(int n, int k){ 213 | uint64_t v = n >= 64 ? ~0 : ((uint64_t)1 << n) - 1; 214 | return v << k; 215 | } 216 | 217 | /** 218 | * @brief Zeros Vector. 219 | * 220 | * Generate a 64-bit bitvector of zeros with 221 | * 222 | * val[63 :n+k] = 1 223 | * val[n+k-1: k] = 0 224 | * val[ k-1: 0] = 1 225 | * 226 | * i.e. where the n bits starting at k counting from the LSB are all clear, and 227 | * all other bits are 1. 228 | */ 229 | 230 | static uint64_t ZV(int n, int k){ 231 | return ~OV(n, k); 232 | } 233 | 234 | /** 235 | * @brief Bit Vector. 236 | * 237 | * Generate a 64-bit bitvector with 238 | * 239 | * val[63 :n+k] = 0 240 | * val[n+k-1: k] = v[n-1:0] 241 | * val[ k-1: 0] = 0 242 | * 243 | * i.e. where the n bits starting at k counting from the LSB are taken from 244 | * the LSBs of v, and all other bits are 0. 245 | */ 246 | 247 | static uint64_t BV(uint64_t v, int n, int k){ 248 | v &= OV(n, 0); 249 | return v << k; 250 | } 251 | 252 | /** 253 | * @brief Clear Vector. 254 | * 255 | * Generate a 64-bit bitvector with 256 | * 257 | * val = v 258 | * val[n+k-1: k] = 0 259 | * 260 | * i.e. where the n bits starting at k counting from the LSB are set to 0, 261 | * and all other bits are taken from v. 262 | */ 263 | 264 | static uint64_t CV(uint64_t v, int n, int k){ 265 | return v & ZV(n,k); 266 | } 267 | 268 | /** 269 | * @brief RDMSR wrapper. 270 | * 271 | * Returns the unmodified value of the given MSR. 272 | * 273 | * @returns The value read at the given MSR. 274 | * @note Does *not* check that addr is valid! 275 | */ 276 | 277 | static uint64_t pfcRDMSR(uint64_t addr){ 278 | return native_read_msr(addr); 279 | } 280 | 281 | /** 282 | * @brief WRMSR wrapper. 283 | * 284 | * Writes to the given MSR. If it is a known MSR, mask out reserved bits into 285 | * a temporary, logic-OR the reserved bits into the temporary and write back 286 | * this temporary. 287 | */ 288 | 289 | static void pfcWRMSR(uint64_t addr, uint64_t newVal){ 290 | uint64_t mask; 291 | 292 | /** 293 | * For writing to MSRs, it's required to retrieve the old value of 294 | * reserved bits and write them back. Things seem to blow up big-time 295 | * otherwise. 296 | * 297 | * Thus we retrieve a mask whose bits are set to 1 where the MSR's 298 | * corresponding bits are reserved. 299 | */ 300 | 301 | if( (addr >= MSR_IA32_A_PMC0 && 302 | addr < MSR_IA32_A_PMC0+pmcGp) || 303 | (addr >= MSR_IA32_PERFCTR0 && 304 | addr < MSR_IA32_PERFCTR0+pmcGp) ){ 305 | mask = ~pmcGpMask; 306 | }else if(addr == MSR_IA32_PERF_GLOBAL_CTRL ){ 307 | mask = ZV(pmcFf, 32) & ZV(pmcGp, 0); 308 | }else if(addr == MSR_IA32_PERF_GLOBAL_STATUS ){ 309 | return;/* RO MSR! */ 310 | }else if(addr == MSR_IA32_PERF_GLOBAL_OVF_CTRL ){ 311 | mask = ZV( 3, 61) & ZV(pmcFf, 32) & ZV(pmcGp, 0); 312 | }else if(addr == MSR_IA32_FIXED_CTR_CTRL ){ 313 | mask = ZV(4*pmcFf, 0); 314 | }else if(addr >= MSR_IA32_FIXED_CTR0 && 315 | addr < MSR_IA32_FIXED_CTR0 +pmcFf ){ 316 | mask = ~pmcFfMask; 317 | }else if(addr >= MSR_IA32_PERFEVTSEL0 && 318 | addr < MSR_IA32_PERFEVTSEL0+pmcGp ){ 319 | mask = 0xFFFFFFFF00000000; 320 | }else if(addr == MSR_IA32_THERM_STATUS){ 321 | mask = 0xFFFFFFFF0780F000; 322 | }else if(addr == MSR_IA32_PACKAGE_THERM_STATUS){ 323 | mask = 0xFFFFFFFFFF80F000; 324 | }else if(addr == MSR_IA32_TEMPERATURE_TARGET){ 325 | /** 326 | * On i7-4700MQ, 327 | * 328 | * - Bits 29-28 are undefined. 329 | * - Bits 15- 8 are, in fact, defined. 330 | */ 331 | mask = 0xFFFFFFFFF00000FF; 332 | }else if(addr == MSR_CORE_PERF_LIMIT_REASONS){ 333 | mask = 0xFFFFFFFF1A90FFFF; 334 | }else if(addr == MSR_IA32_ENERGY_PERF_BIAS){ 335 | mask = 0xFFFFFFFFFFFFFFF0; 336 | }else if(addr == MSR_IA32_PERF_CTL){ 337 | mask = 0xFFFFFFFFFFFF0000; 338 | }else if(addr == MSR_PEBS_FRONTEND){ 339 | mask = 0xFFFFFFFFFFC000E8; 340 | }else{ 341 | return;/* Unknown MSR! Taking no chances! */ 342 | } 343 | 344 | 345 | /** 346 | * Blend new and old & Writeback 347 | */ 348 | 349 | newVal = (~mask&newVal) | (mask&pfcRDMSR(addr)); 350 | native_write_msr(addr, 351 | (uint32_t)(newVal >> 0), 352 | (uint32_t)(newVal >> 32)); 353 | } 354 | 355 | /** 356 | * Clamp offset+len into a given counter range. 357 | * 358 | * Writes out the overlap's bounds through the output arguments. 359 | * 360 | * Returns whether or not [offset+len) overlaps [rangeStart, rangeEnd) 361 | */ 362 | 363 | static int pfcClampRange(int off, 364 | int len, 365 | int rangeStart, 366 | int rangeEnd, 367 | int* pmcStart, 368 | int* pmcEnd){ 369 | if(off+len <= rangeStart || /* If given range is fully before or */ 370 | off >= rangeEnd || /* fully after the target range, or */ 371 | len <= 0){ /* its length is <=0, then */ 372 | return 0; 373 | }else{ 374 | *pmcStart = off < rangeStart ? rangeStart : off; 375 | *pmcEnd = off+len > rangeEnd ? rangeEnd : off+len; 376 | return 1; 377 | } 378 | } 379 | 380 | /** 381 | * Check whether or not the given offset+length are sanely aligned. 382 | */ 383 | 384 | static int pfcIsAligned(loff_t off, size_t len, size_t mask){ 385 | return !((off|len) & mask); 386 | } 387 | 388 | /*************** END UTILITIES ***************/ 389 | 390 | 391 | /*************** COUNTER MANIPULATION ***************/ 392 | /* Ff */ 393 | void pfcFfCntWrEnb(int i, int v){ 394 | uint64_t en = pfcRDMSR(MSR_IA32_PERF_GLOBAL_CTRL); 395 | en |= (uint64_t)!!v << (32+i); 396 | pfcWRMSR(MSR_IA32_PERF_GLOBAL_CTRL, en); 397 | } 398 | void pfcFfCntWrCfg(int i, uint64_t c){ 399 | /** 400 | * We forbid the setting of the following bits in each 4-bit config group. 401 | * Bit 3: PMI Interrupt on counter overflow 402 | * for all counters. 403 | * 404 | * This corresponds to keeping only 0b0111. 405 | */ 406 | 407 | c &= 0x7; 408 | c = CV(pfcRDMSR(MSR_IA32_FIXED_CTR_CTRL), 4, 4*i) | BV(c, 4, 4*i); 409 | pfcWRMSR(MSR_IA32_FIXED_CTR_CTRL, c); 410 | } 411 | uint64_t pfcFfCntRdCfg(int i ){ 412 | uint64_t cfg = pfcRDMSR(MSR_IA32_FIXED_CTR_CTRL); 413 | cfg >>= 4*i; 414 | return cfg & 0xF; 415 | } 416 | void pfcFfCntWrVal(int i, uint64_t v){ 417 | pfcWRMSR(MSR_IA32_FIXED_CTR0+i, v); 418 | } 419 | uint64_t pfcFfCntRdVal(int i ){ 420 | return pfcRDMSR(MSR_IA32_FIXED_CTR0+i); 421 | } 422 | 423 | /* Gp */ 424 | void pfcGpCntWrEnb(int i, int v){ 425 | uint64_t en = pfcRDMSR(MSR_IA32_PERF_GLOBAL_CTRL); 426 | en |= (uint64_t)!!v << ( 0+i); 427 | pfcWRMSR(MSR_IA32_PERF_GLOBAL_CTRL, en); 428 | } 429 | void pfcGpCntWrCfg(int i, uint64_t c){ 430 | uint64_t evtNum, umask; 431 | 432 | /** 433 | * We forbid the setting of the following bits in each PERFEVTSELx MSR: 434 | * Bit 20: APIC Interrupt Enable on overflow bit 435 | * Bit 19: Pin Control bit 436 | * for all counters. 437 | */ 438 | 439 | c &= ~0x0000000000180000ULL; 440 | 441 | /** 442 | * For odd reasons, certain cache statistics can only be collected on 443 | * certain counters. 444 | */ 445 | 446 | evtNum = (c >> 0) & 0xFF; 447 | umask = (c >> 8) & 0xFF; 448 | 449 | if((evtNum == 0x48) || /* l1d_pend_miss */ 450 | (evtNum == 0xA3 && (umask == 0x08 || umask == 0x0C))){/* cycle_activity.l1d_pending */ 451 | if(i != 2){ 452 | c = 0;/* Disable. */ 453 | } 454 | } 455 | 456 | if(evtNum == 0xC0 && umask == 0x01){ 457 | if(i != 1){ 458 | c = 0;/* Disable. */ 459 | } 460 | } 461 | 462 | 463 | pfcWRMSR(MSR_IA32_PERFEVTSEL0+i, c); 464 | } 465 | uint64_t pfcGpCntRdCfg(int i ){ 466 | return pfcRDMSR(MSR_IA32_PERFEVTSEL0+i); 467 | } 468 | void pfcGpCntWrVal(int i, uint64_t v){ 469 | if (fullWidthWrites){ 470 | pfcWRMSR(MSR_IA32_A_PMC0+i, v); 471 | }else{ 472 | pfcWRMSR(MSR_IA32_PERFCTR0+i, v); 473 | } 474 | } 475 | uint64_t pfcGpCntRdVal(int i ){ 476 | return pfcRDMSR(MSR_IA32_PERFCTR0+i); 477 | } 478 | /*************** END COUNTER MANIPULATION ***************/ 479 | 480 | 481 | /**************** SYSFS ATTRIBUTES ****************/ 482 | 483 | /** 484 | * Read configuration. 485 | * 486 | * Returns the configuration of the selected counters, one 64-bit word per 487 | * counter, with the Ff counters first and the Gp counters last. 488 | * 489 | * For Ff counters, their 4-bit field from IA32_FIXED_CTR_CTRL is read. 490 | * For Gp counters, their respective IA32_PERFEVTSEL is read. 491 | * 492 | * @return Bytes of configuration data read 493 | */ 494 | 495 | static ssize_t pfcCfgRd (struct file* f, 496 | struct kobject* kobj, 497 | struct bin_attribute* binattr, 498 | char* buf, 499 | loff_t off, 500 | size_t len){ 501 | int pmcStart, pmcEnd, i, j; 502 | uint64_t* buf64 = (uint64_t*)buf; 503 | 504 | /* Check access is reasonable. */ 505 | if(!pfcIsAligned(off, len, 0x7) || off<0 || len<0){ 506 | return -1; 507 | } 508 | 509 | /* Read relevant MSRs */ 510 | j=0; 511 | if(pfcClampRange(off>>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){ 512 | pmcStart -= pmcStartFf; 513 | pmcEnd -= pmcStartFf; 514 | 515 | for(i=pmcStart;i>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){ 520 | pmcStart -= pmcStartGp; 521 | pmcEnd -= pmcStartGp; 522 | 523 | for(i=pmcStart;i>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){ 560 | pmcStart -= pmcStartFf; 561 | pmcEnd -= pmcStartFf; 562 | 563 | for(i=pmcStart;i>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){ 573 | pmcStart -= pmcStartGp; 574 | pmcEnd -= pmcStartGp; 575 | 576 | for(i=pmcStart;i>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){ 619 | pmcStart -= pmcStartFf; 620 | pmcEnd -= pmcStartFf; 621 | 622 | for(i=pmcStart;i>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){ 627 | pmcStart -= pmcStartGp; 628 | pmcEnd -= pmcStartGp; 629 | 630 | for(i=pmcStart;i>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){ 667 | pmcStart -= pmcStartFf; 668 | pmcEnd -= pmcStartFf; 669 | 670 | for(i=pmcStart;i>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){ 675 | pmcStart -= pmcStartGp; 676 | pmcEnd -= pmcStartGp; 677 | 678 | for(i=pmcStart;i>3, len>>3, pmcStartFf, pmcEndFf, &pmcStart, &pmcEnd)){ 715 | pmcStart -= pmcStartFf; 716 | pmcEnd -= pmcStartFf; 717 | 718 | for(i=pmcStart;i>3, len>>3, pmcStartGp, pmcEndGp, &pmcStart, &pmcEnd)){ 723 | pmcStart -= pmcStartGp; 724 | pmcEnd -= pmcStartGp; 725 | 726 | for(i=pmcStart;i> 8) & 0x0F; 859 | model = (leaf1.a >> 4) & 0x0F; 860 | stepping = (leaf1.a >> 0) & 0x0F; 861 | exmodel = (leaf1.a >> 16) & 0x0F; 862 | exfamily = (leaf1.a >> 20) & 0xFF; 863 | dispFamily = ( family != 0x0F ) ? family : (family+exfamily); 864 | dispModel = (family == 0x06 || family==0x0F) ? (exmodel<<4|model) : model; 865 | 866 | maxLeaf = leaf0.a; 867 | maxExtendedLeaf = leaf80000000.a; 868 | memcpy(&procBrandString[ 0], (const char*)&leaf80000002, 16); 869 | memcpy(&procBrandString[16], (const char*)&leaf80000003, 16); 870 | memcpy(&procBrandString[32], (const char*)&leaf80000004, 16); 871 | if(maxExtendedLeaf < 4){ 872 | strcpy(procBrandString, "(unknown)"); 873 | } 874 | 875 | 876 | /* And dump the CPUID info to the ring buffer for debug purposes. */ 877 | if(maxLeaf >= 1){ 878 | printk(KERN_INFO "pfc: Kernel Module loading on processor %s (Family %u (%X), Model %u (%03X), Stepping %u (%X))\n", 879 | procBrandString, dispFamily, dispFamily, dispModel, dispModel, stepping, stepping); 880 | }else{ 881 | printk(KERN_INFO "pfc: Kernel Module loading on processor %s\n", procBrandString); 882 | } 883 | printk(KERN_INFO "pfc: cpuid.0x0.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 884 | leaf0.a, leaf0.b, leaf0.c, leaf0.d); 885 | printk(KERN_INFO "pfc: cpuid.0x1.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 886 | leaf1.a, leaf1.b, leaf1.c, leaf1.d); 887 | printk(KERN_INFO "pfc: cpuid.0x6.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 888 | leaf6.a, leaf6.b, leaf6.c, leaf6.d); 889 | printk(KERN_INFO "pfc: cpuid.0xA.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 890 | leafA.a, leafA.b, leafA.c, leafA.d); 891 | printk(KERN_INFO "pfc: cpuid.0x80000000.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 892 | leaf80000000.a, leaf80000000.b, leaf80000000.c, leaf80000000.d); 893 | printk(KERN_INFO "pfc: cpuid.0x80000001.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 894 | leaf80000001.a, leaf80000001.b, leaf80000001.c, leaf80000001.d); 895 | printk(KERN_INFO "pfc: cpuid.0x80000002.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 896 | leaf80000002.a, leaf80000002.b, leaf80000002.c, leaf80000002.d); 897 | printk(KERN_INFO "pfc: cpuid.0x80000003.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 898 | leaf80000003.a, leaf80000003.b, leaf80000003.c, leaf80000003.d); 899 | printk(KERN_INFO "pfc: cpuid.0x80000004.0x0: EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x\n", 900 | leaf80000004.a, leaf80000004.b, leaf80000004.c, leaf80000004.d); 901 | 902 | 903 | /* Begin sanity checks. */ 904 | if(maxLeaf < 0xA){ 905 | printk(KERN_ERR "pfc: ERROR: Processor too old!\n"); 906 | return -1; 907 | } 908 | 909 | 910 | /* Check that CPU has performance monitoring. */ 911 | if(((leaf1.c>>15) & 1) == 0){ 912 | printk(KERN_ERR "pfc: ERROR: Processor does not have Perfmon and Debug Capability!\n"); 913 | return -1; 914 | } 915 | 916 | 917 | /** 918 | * Inform ourselves about PMC support. 919 | * 920 | * PMC information is gotten by CPUID.EAX = 0xA. 921 | * 922 | * PerfMon architecture version is in EAX[ 7: 0]. 923 | * #Gp PMCs is in EAX[15: 8] 924 | * Gp bitwidth is in EAX[23:16] 925 | * #Ff PMCs is in EDX[ 4: 0] if PMArchVer > 1. 926 | * Ff bitwidth is in EDX[12: 5] if PMArchVer > 1. 927 | */ 928 | 929 | pmcArchVer = (leafA.a >> 0) & 0xFF; 930 | if(pmcArchVer < 3 || pmcArchVer > 4){ 931 | printk(KERN_INFO "pfc: ERROR: Unsupported performance monitoring architecture version %d, only 3 or 4 supported!\n", pmcArchVer); 932 | return -1; 933 | } 934 | 935 | fullWidthWrites = (pfcRDMSR(MSR_IA32_PERF_CAPABILITIES) >> 13) & 1; 936 | 937 | pmcGp = (leafA.a >> 8) & 0xFF; 938 | pmcGpBitwidth = (leafA.a >> 16) & 0xFF; 939 | if (fullWidthWrites){ 940 | pmcGpMask = OV(pmcGpBitwidth,0); 941 | }else{ 942 | pmcGpMask = OV(32,0); 943 | } 944 | 945 | pmcFf = (leafA.d >> 0) & 0x1F; 946 | pmcFfBitwidth = (leafA.d >> 5) & 0xFF; 947 | pmcFfMask = OV(pmcFfBitwidth,0); 948 | 949 | 950 | /* Save bounds. */ 951 | pmcStartFf = 0; 952 | pmcEndFf = pmcFf; 953 | pmcStartGp = pmcFf; 954 | pmcEndGp = pmcFf+pmcGp; 955 | 956 | 957 | /* Dump out this data */ 958 | printk(KERN_INFO "pfc: PM Arch Version: %d\n", pmcArchVer); 959 | if(pmcFf + pmcGp > MAXPMC){ 960 | printk(KERN_INFO "pfc: More than %d PMCs found! Clamping to %d.\n", 961 | MAXPMC, MAXPMC); 962 | pmcGp = pmcGp>MAXPMC ? MAXPMC : pmcGp; 963 | pmcFf = pmcGp+pmcFf>MAXPMC ? MAXPMC-pmcGp : pmcFf; 964 | } 965 | printk(KERN_INFO "pfc: Fixed-function PMCs: %d\tMask %016llx (%d bits)\n", pmcFf, pmcFfMask, pmcFfBitwidth); 966 | printk(KERN_INFO "pfc: General-purpose PMCs: %d\tMask %016llx (%d bits)\n", pmcGp, pmcGpMask, pmcGpBitwidth); 967 | 968 | 969 | return 0; 970 | } 971 | 972 | /** 973 | * Initialize all counters. 974 | * 975 | * For each counter, therefore, 976 | * 1. Globally disable it 977 | * 2. Deconfigure it 978 | * 3. Zero it 979 | * 4. Clear its interrupt flags 980 | */ 981 | 982 | static void pfcInitCounters(void* unused){ 983 | int i; 984 | (void)unused; 985 | 986 | pfcWRMSR(MSR_IA32_PERF_GLOBAL_CTRL, 0); 987 | pfcWRMSR(MSR_IA32_FIXED_CTR_CTRL, 0); 988 | for(i=0;i /sys/bus/event_source/devices/cpu/rdpmc\n"); 1015 | printk(KERN_INFO "pfc: ERROR: as root. This enables ultra-low-overhead, userspace\n"); 1016 | printk(KERN_INFO "pfc: ERROR: access to the performance counters.\n"); 1017 | goto fail; 1018 | } 1019 | 1020 | on_each_cpu(pfcInitCounters, NULL, 1); 1021 | 1022 | /** 1023 | * The chmods that follow are necessary because sysfs_create_group() 1024 | * doesn't appreciate world-writeable files, but we WANT that for the 1025 | * convenience of our users. 1026 | */ 1027 | 1028 | ret = sysfs_create_group((struct kobject*)&THIS_MODULE->mkobj, 1029 | &PFC_ATTR_GRP); 1030 | ret |= sysfs_chmod_file ((struct kobject*) &THIS_MODULE->mkobj, 1031 | (struct attribute*)&PFC_ATTR_config, 0666); 1032 | ret |= sysfs_chmod_file ((struct kobject*) &THIS_MODULE->mkobj, 1033 | (struct attribute*)&PFC_ATTR_masks, 0444); 1034 | ret |= sysfs_chmod_file ((struct kobject*) &THIS_MODULE->mkobj, 1035 | (struct attribute*)&PFC_ATTR_counts, 0666); 1036 | ret |= sysfs_chmod_file ((struct kobject*) &THIS_MODULE->mkobj, 1037 | (struct attribute*)&PFC_ATTR_msr, 0444); 1038 | ret |= sysfs_chmod_file ((struct kobject*) &THIS_MODULE->mkobj, 1039 | (struct attribute*)&PFC_ATTR_verbose, 0666); 1040 | ret |= sysfs_chmod_file ((struct kobject*) &THIS_MODULE->mkobj, 1041 | (struct attribute*)&PFC_ATTR_cr4pce, 0444); 1042 | if(ret != 0){ 1043 | printk(KERN_INFO "pfc: ERROR: Failed to create sysfs attributes.\n"); 1044 | goto lateFail; 1045 | } 1046 | 1047 | 1048 | printk(KERN_INFO "pfc: Module pfc loaded successfully. Make sure to execute\n"); 1049 | printk(KERN_INFO "pfc: modprobe -ar iTCO_wdt iTCO_vendor_support\n"); 1050 | printk(KERN_INFO "pfc: echo 0 > /proc/sys/kernel/nmi_watchdog\n"); 1051 | printk(KERN_INFO "pfc: and blacklist iTCO_vendor_support and iTCO_wdt, since the CR4.PCE register\n"); 1052 | printk(KERN_INFO "pfc: initialization is periodically undone by an unknown agent.\n"); 1053 | return 0; 1054 | 1055 | 1056 | lateFail: 1057 | pfcExit(); 1058 | fail: 1059 | printk(KERN_INFO "pfc: ERROR: Failed to load module pfc.\n"); 1060 | return -1; 1061 | } 1062 | 1063 | /** 1064 | * Exit module. 1065 | */ 1066 | 1067 | static void pfcExit(void){ 1068 | printk(KERN_INFO "pfc: Module exiting...\n"); 1069 | 1070 | on_each_cpu(pfcInitCounters, NULL, 1); 1071 | sysfs_remove_group((struct kobject*)&THIS_MODULE->mkobj, 1072 | &PFC_ATTR_GRP); 1073 | 1074 | printk(KERN_INFO "pfc: Module exited.\n"); 1075 | } 1076 | 1077 | 1078 | 1079 | /* Module magic */ 1080 | module_init(pfcInit); 1081 | module_exit(pfcExit); 1082 | MODULE_LICENSE("Dual MIT/GPL"); 1083 | MODULE_AUTHOR(""); 1084 | MODULE_DESCRIPTION("Grants ***EXTREMELY UNSAFE*** access to the Intel Performance Monitor Counters (PMC)."); 1085 | 1086 | 1087 | 1088 | /* Notes */ 1089 | 1090 | /** 1091 | * Possibly required boot args: 1092 | * nmi_watchdog=0 modprobe.blacklist=iTCO_wdt,iTCO_vendor_support 1093 | * 1094 | * Otherwise, IA32_FIXED_CTR1 is monopolised by !@#$ NMI watchdog crapware. Even 1095 | * so, it seems that something gratuitously, though rarely, sets IA32_PMC0 to 1096 | * 0xFFFF every so often. 1097 | */ 1098 | 1099 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # The Performance Counter Library 2 | project('libpfc', ['c', 'cpp'], 3 | version: '0.0.0', 4 | meson_version: '>=0.41.0', 5 | license: 'MIT', 6 | default_options: ['c_std=gnu99', 'cpp_std=c++11']) 7 | 8 | subdir('include') 9 | subdir('kmod') 10 | subdir('src') 11 | -------------------------------------------------------------------------------- /src/libpfc.c: -------------------------------------------------------------------------------- 1 | /* Includes */ 2 | #define _GNU_SOURCE 3 | 4 | #include "libpfc.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | /* Data Structures */ 16 | struct UMASK; 17 | typedef struct UMASK UMASK; 18 | struct EVENT; 19 | typedef struct EVENT EVENT; 20 | 21 | struct UMASK{ 22 | uint64_t umaskVal; 23 | const char* name; 24 | }; 25 | struct EVENT{ 26 | uint64_t evtNum; 27 | const UMASK* umasks; 28 | const char* name; 29 | }; 30 | 31 | 32 | /* Global data */ 33 | static int cfgFd = -1; 34 | static int mskFd = -1; 35 | static int cntFd = -1; 36 | static int msrFd = -1; 37 | static int cr4Fd = -1; 38 | static uint64_t masks[7] = {0,0,0,0,0,0,0}; 39 | 40 | static const struct UMASK UMASK_LIST[] = { 41 | {0x02, "store_forward"}, /* 0 */ /* 0x03 */ 42 | {0x08, "no_sr"}, 43 | {0x00, NULL}, 44 | {0x01, "loads"}, /* 3 */ /* 0x05 */ 45 | {0x02, "stores"}, 46 | {0x00, NULL}, 47 | {0x01, "address_alias"}, /* 6 */ /* 0x07 */ 48 | {0x00, NULL}, 49 | {0x01, "miss_causes_a_walk"}, /* 8 */ /* 0x08 */ 50 | {0x02, "walk_completed_4k"}, 51 | {0x04, "walk_completed_2m_4m"}, 52 | {0x0e, "walk_completed"}, 53 | {0x10, "walk_duration"}, 54 | {0x20, "stlb_hit_4k"}, 55 | {0x40, "stlb_hit_2m"}, 56 | {0x60, "stlb_hit"}, 57 | {0x80, "pde_cache_miss"}, 58 | {0x00, NULL}, 59 | {0x03, "recovery_cycles"}, /* 18 */ /* 0x0D */ 60 | {0x00, NULL}, 61 | {0x01, "any"}, /* 20 */ /* 0x0E */ 62 | {0x10, "flags_merge"}, 63 | {0x20, "slow_lea"}, 64 | {0x40, "single_mul"}, 65 | {0x00, NULL}, 66 | {0x21, "demand_data_rd_miss"}, /* 25 */ /* 0x24 */ 67 | {0x41, "demand_data_rd_hit"}, 68 | {0xE1, "all_demand_data_rd"}, 69 | {0x42, "rfo_hit"}, 70 | {0x22, "rfo_miss"}, 71 | {0xE2, "all_rfo"}, 72 | {0x44, "code_rd_hit"}, 73 | {0x24, "code_rd_miss"}, 74 | {0x27, "all_demand_miss"}, 75 | {0xE7, "all_demand_references"}, 76 | {0xE4, "all_code_rd"}, 77 | {0x50, "l2_pf_hit"}, 78 | {0x30, "l2_pf_miss"}, 79 | {0xF8, "all_pf"}, 80 | {0x3F, "miss"}, 81 | {0xFF, "references"}, 82 | {0x00, NULL}, 83 | {0x50, "wb_hit"}, /* 42 */ /* 0x27 */ 84 | {0x00, NULL}, 85 | {0x4F, "reference"}, /* 44 */ /* 0x2E */ 86 | {0x41, "miss"}, 87 | {0x00, NULL}, 88 | {0x00, "core_clk"}, /* 47 */ /* 0x3C */ 89 | {0x01, "ref_xclk"}, 90 | {0x00, NULL}, 91 | {0x01, "pending"}, /* 50 */ /* 0x48 */ 92 | {0x00, NULL}, 93 | {0x01, "miss_causes_a_walk"}, /* 52 */ /* 0x49 */ 94 | {0x02, "walk_completed_4k"}, 95 | {0x04, "walk_completed_2m_4m"}, 96 | {0x0E, "walk_completed"}, 97 | {0x10, "walk_duration"}, 98 | {0x20, "stlb_hit_4k"}, 99 | {0x40, "stlb_hit_2m"}, 100 | {0x60, "stlb_hit"}, 101 | {0x80, "pde_cache_miss"}, 102 | {0x00, NULL}, 103 | {0x01, "sw_pf"}, /* 62 */ /* 0x4C */ 104 | {0x02, "hw_pf"}, 105 | {0x00, NULL}, 106 | {0x01, "replacement"}, /* 65 */ /* 0x51 */ 107 | {0x00, NULL}, 108 | {0x01, "abort_conflict"}, /* 67 */ /* 0x54 */ 109 | {0x02, "abort_capacity_write"}, 110 | {0x04, "abort_hle_store_to_elided_lock"}, 111 | {0x08, "abort_hle_elision_buffer_not_empty"}, 112 | {0x10, "abort_hle_elision_buffer_mismatch"}, 113 | {0x20, "abort_hle_elision_buffer_unsupported_alignment"}, 114 | {0x40, "hle_elision_buffer_full"}, 115 | {0x00, NULL}, 116 | {0x04, "int_not_eliminated"}, /* 75 */ /* 0x58 */ 117 | {0x08, "simd_not_eliminated"}, 118 | {0x01, "int_not_eliminated"}, 119 | {0x02, "simd_eliminated"}, 120 | {0x00, NULL}, 121 | {0x01, "ring0"}, /* 80 */ /* 0x5C */ 122 | {0x02, "ring123"}, 123 | {0x00, NULL}, 124 | {0x01, "misc1"}, /* 83 */ /* 0x5D */ 125 | {0x02, "misc2"}, 126 | {0x04, "misc3"}, 127 | {0x08, "misc4"}, 128 | {0x10, "misc5"}, 129 | {0x00, NULL}, 130 | {0x01, "empty_cycles"}, /* 89 */ /* 0x5E */ 131 | {0x00, NULL}, 132 | {0x01, "demand_data_rd"}, /* 91 */ /* 0x60 */ 133 | {0x02, "demand_code_rd"}, 134 | {0x04, "demand_rfo"}, 135 | {0x08, "all_data_rd"}, 136 | {0x00, NULL}, 137 | {0x01, "split_lock_uc_lock_duration"}, /* 96 */ /* 0x63 */ 138 | {0x02, "cache_lock_duration"}, 139 | {0x00, NULL}, 140 | {0x02, "empty"}, /* 99 */ /* 0x79 */ 141 | {0x04, "mite_uops"}, 142 | {0x08, "dsb_uops"}, 143 | {0x10, "ms_dsb_uops"}, 144 | {0x20, "ms_mite_uops"}, 145 | {0x30, "ms_uops"}, 146 | {0x18, "all_dsb_cycles_any_uops"}, 147 | {0x18, "all_dsb_cycles_4_uops"}, 148 | {0x24, "all_mite_cycles_any_uops"}, 149 | {0x24, "all_mite_cycles_4_uops"}, 150 | {0x3C, "mite_all_uops"}, 151 | {0x00, NULL}, 152 | {0x02, "misses"}, /* 111 */ /* 0x80 */ 153 | {0x00, NULL}, 154 | {0x01, "miss_causes_a_walk"}, /* 113 */ /* 0x85 */ 155 | {0x02, "walk_completed_4k"}, 156 | {0x04, "walk_completed_2m_4m"}, 157 | {0x0E, "walk_completed"}, 158 | {0x10, "walk_duration"}, 159 | {0x20, "stlb_hit_4k"}, 160 | {0x40, "stlb_hit_2m"}, 161 | {0x60, "stlb_hit"}, 162 | {0x00, NULL}, 163 | {0x01, "lcp"}, /* 122 */ /* 0x87 */ 164 | {0x04, "iq_full"}, 165 | {0x00, NULL}, 166 | {0x01, "cond"}, /* 125 */ /* 0x88 */ 167 | {0x02, "direct_jmp"}, 168 | {0x04, "indirect_jmp_non_call_ret"}, 169 | {0x08, "return_near"}, 170 | {0x10, "direct_near_call"}, 171 | {0x20, "indirect_near_call"}, 172 | {0x40, "nontaken"}, 173 | {0x80, "taken"}, 174 | {0xFF, "all_branches"}, 175 | {0x00, NULL}, 176 | {0x01, "cond"}, /* 135 */ /* 0x89 */ 177 | {0x04, "indirect_jmp_non_call_ret"}, 178 | {0x08, "return_near"}, 179 | {0x10, "direct_near_call"}, 180 | {0x20, "indirect_near_call"}, 181 | {0x40, "nontaken"}, 182 | {0x80, "taken"}, 183 | {0xFF, "all_branches"}, 184 | {0x00, NULL}, 185 | {0x01, "core"}, /* 144 */ /* 0x9C */ 186 | {0x00, NULL}, 187 | {0x01, "port_0"}, /* 146 */ /* 0xA1 */ 188 | {0x02, "port_1"}, 189 | {0x04, "port_2"}, 190 | {0x08, "port_3"}, 191 | {0x10, "port_4"}, 192 | {0x20, "port_5"}, 193 | {0x40, "port_6"}, 194 | {0x80, "port_7"}, 195 | {0x00, NULL}, 196 | {0x01, "any"}, /* 155 */ /* 0xA2 */ 197 | {0x04, "rs"}, 198 | {0x08, "sb"}, 199 | {0x10, "rob"}, 200 | {0x00, NULL}, 201 | {0x01, "cycles_l2_pending"}, /* 160 */ /* 0xA3 */ 202 | {0x02, "cycles_ldm_pending"}, 203 | {0x05, "stalls_l2_pending"}, 204 | {0x08, "cycles_l1d_pending"}, 205 | {0x0C, "stalls_l1d_pending"}, 206 | {0x00, NULL}, 207 | {0x01, "uops"}, /* 166 */ /* 0xA8 */ 208 | {0x00, NULL}, 209 | {0x01, "itlb_flush"}, /* 168 */ /* 0xAE */ 210 | {0x00, NULL}, 211 | {0x01, "demand_data_rd"}, /* 170 */ /* 0xB0 */ 212 | {0x02, "demand_core_rd"}, 213 | {0x04, "demand_rfo"}, 214 | {0x08, "all_data_rd"}, 215 | {0x00, NULL}, 216 | {0x02, "core"}, /* 175 */ /* 0xB1 */ 217 | {0x00, NULL}, 218 | {0x11, "dtlb_l1"}, /* 177 */ /* 0xBC */ 219 | {0x21, "itlb_l1"}, 220 | {0x12, "dtlb_l2"}, 221 | {0x22, "itlb_l2"}, 222 | {0x14, "dtlb_l3"}, 223 | {0x24, "itlb_l3"}, 224 | {0x18, "dtlb_memory"}, 225 | {0x28, "itlb_memory"}, 226 | {0x00, NULL}, 227 | {0x01, "dtlb_thread"}, /* 186 */ /* 0xBD */ 228 | {0x20, "stlb_any"}, 229 | {0x00, NULL}, 230 | {0x00, "any_p"}, /* 189 */ /* 0xC0 */ 231 | {0x01, "prec_dist"}, 232 | {0x00, NULL}, 233 | {0x08, "avx_to_sse"}, /* 192 */ /* 0xC1 */ 234 | {0x10, "sse_to_avx"}, 235 | {0x40, "any_wb_assist"}, 236 | {0x00, NULL}, 237 | {0x01, "all"}, /* 196 */ /* 0xC2 */ 238 | {0x02, "retire_slots"}, 239 | {0x00, NULL}, 240 | {0x02, "memory_ordering"}, /* 199 */ /* 0xC3 */ 241 | {0x04, "smc"}, 242 | {0x20, "maskmov"}, 243 | {0x00, NULL}, 244 | {0x00, "all_branches"}, /* 203 */ /* 0xC4 */ 245 | {0x01, "conditional"}, 246 | {0x02, "near_call"}, 247 | {0x04, "all_branches_pebs"}, 248 | {0x08, "near_return"}, 249 | {0x10, "not_taken"}, 250 | {0x20, "near_taken"}, 251 | {0x40, "far_branch"}, 252 | {0x00, NULL}, 253 | {0x00, "all_branches"}, /* 212 */ /* 0xC5 */ 254 | {0x01, "conditional"}, 255 | {0x04, "all_branches_pebs"}, 256 | {0x20, "near_taken"}, 257 | {0x00, NULL}, 258 | {0x01, "start"}, /* 217 */ /* 0xC8 */ 259 | {0x02, "commit"}, 260 | {0x04, "aborted"}, 261 | {0x08, "aborted_mem"}, 262 | {0x10, "aborted_timer"}, 263 | {0x20, "aborted_unfriendly"}, 264 | {0x40, "aborted_memtype"}, 265 | {0x80, "aborted_events"}, 266 | {0x00, NULL}, 267 | {0x01, "start"}, /* 226 */ /* 0xC9 */ 268 | {0x02, "commit"}, 269 | {0x04, "aborted"}, 270 | {0x08, "aborted_mem"}, 271 | {0x10, "aborted_timer"}, 272 | {0x20, "aborted_unfriendly"}, 273 | {0x40, "aborted_memtype"}, 274 | {0x80, "aborted_events"}, 275 | {0x00, NULL}, 276 | {0x02, "x87_output"}, /* 235 */ /* 0xCA */ 277 | {0x04, "x87_input"}, 278 | {0x08, "simd_output"}, 279 | {0x10, "simd_input"}, 280 | {0x1E, "any"}, 281 | {0x00, NULL}, 282 | {0x20, "lbr_inserts"}, /* 241 */ /* 0xCC */ 283 | {0x00, NULL}, 284 | {0x11, "stlb_miss_loads"}, /* 243 */ /* 0xD0 */ 285 | {0x12, "stlb_miss_stores"}, 286 | {0x21, "lock_loads"}, 287 | {0x41, "split_loads"}, 288 | {0x42, "split_stores"}, 289 | {0x81, "all_loads"}, 290 | {0x82, "all_stores"}, 291 | {0x00, NULL}, 292 | {0x01, "l1_hit"}, /* 251 */ /* 0xD1 */ 293 | {0x02, "l2_hit"}, 294 | {0x04, "l3_hit"}, 295 | {0x08, "l1_miss"}, 296 | {0x10, "l2_miss"}, 297 | {0x20, "l3_miss"}, 298 | {0x40, "hit_lfb"}, 299 | {0x00, NULL}, 300 | {0x01, "xsnp_miss"}, /* 259 */ /* 0xD2 */ 301 | {0x02, "xsnp_hit"}, 302 | {0x04, "xsnp_hitm"}, 303 | {0x08, "xsnp_none"}, 304 | {0x00, NULL}, 305 | {0x01, "local_dram"}, /* 264 */ /* 0xD3 */ 306 | {0x00, NULL}, 307 | {0x1F, "any"}, /* 266 */ /* 0xE6 */ 308 | {0x00, NULL}, 309 | {0x01, "demand_data_rd"}, /* 268 */ /* 0xF0 */ 310 | {0x02, "rfo"}, 311 | {0x04, "code_rd"}, 312 | {0x08, "all_pf"}, 313 | {0x10, "l1d_wb"}, 314 | {0x20, "l2_fill"}, 315 | {0x40, "l2_wb"}, 316 | {0x80, "all_requests"}, 317 | {0x00, NULL}, 318 | {0x01, "i"}, /* 277 */ /* 0xF1 */ 319 | {0x02, "s"}, 320 | {0x04, "e"}, 321 | {0x07, "all"}, 322 | {0x00, NULL}, 323 | {0x05, "demand_clean"}, /* 282 */ /* 0xF2 */ 324 | {0x06, "demand_dirty"}, 325 | {0x00, NULL} 326 | }; 327 | static const struct EVENT EVENT_LIST[256] = { 328 | {0x03, &UMASK_LIST[ 0], "ld_blocks"}, 329 | {0x05, &UMASK_LIST[ 3], "misalign_mem_ref"}, 330 | {0x07, &UMASK_LIST[ 6], "ld_blocks_partial"}, 331 | {0x08, &UMASK_LIST[ 8], "dtlb_load_misses"}, 332 | {0x0D, &UMASK_LIST[ 18], "int_misc"}, 333 | {0x0E, &UMASK_LIST[ 20], "uops_issued"}, 334 | {0x24, &UMASK_LIST[ 25], "l2_rqsts"}, 335 | {0x27, &UMASK_LIST[ 42], "l2_demand_rqsts"}, 336 | {0x2E, &UMASK_LIST[ 44], "llc"}, 337 | {0x3C, &UMASK_LIST[ 47], "cpu_clk_unhalted"}, 338 | {0x48, &UMASK_LIST[ 50], "l1d_pend_miss"}, 339 | {0x49, &UMASK_LIST[ 52], "dtlb_store_misses"}, 340 | {0x4C, &UMASK_LIST[ 62], "load_hit_pre"}, 341 | {0x51, &UMASK_LIST[ 65], "l1d"}, 342 | {0x54, &UMASK_LIST[ 67], "tx_mem"}, 343 | {0x58, &UMASK_LIST[ 75], "move_elimination"}, 344 | {0x5C, &UMASK_LIST[ 80], "cpl_cycles"}, 345 | {0x5D, &UMASK_LIST[ 83], "tx_exec"}, 346 | {0x5E, &UMASK_LIST[ 89], "rs_events"}, 347 | {0x60, &UMASK_LIST[ 91], "offcore_requests_outstanding"}, 348 | {0x63, &UMASK_LIST[ 96], "lock_cycles"}, 349 | {0x79, &UMASK_LIST[ 99], "idq"}, 350 | {0x80, &UMASK_LIST[ 111], "icache"}, 351 | {0x85, &UMASK_LIST[ 113], "itlb_misses"}, 352 | {0x87, &UMASK_LIST[ 122], "ild_stall"}, 353 | {0x88, &UMASK_LIST[ 125], "br_inst_exec"}, 354 | {0x89, &UMASK_LIST[ 135], "br_misp_exec"}, 355 | {0x9C, &UMASK_LIST[ 144], "idq_uops_not_delivered"}, 356 | {0xA1, &UMASK_LIST[ 146], "uops_executed_port"}, 357 | {0xA2, &UMASK_LIST[ 155], "resource_stalls"}, 358 | {0xA3, &UMASK_LIST[ 160], "cycle_activity"}, 359 | {0xA8, &UMASK_LIST[ 166], "lsd"}, 360 | {0xAE, &UMASK_LIST[ 168], "itlb"}, 361 | {0xB0, &UMASK_LIST[ 170], "offcore_requests"}, 362 | {0xB1, &UMASK_LIST[ 175], "uops_executed"}, 363 | /* Ghost [0xB7] and [0xBB] OFF_CORE_RESPONSE_[01] */ 364 | {0xBC, &UMASK_LIST[ 177], "page_walker_loads"}, 365 | {0xBD, &UMASK_LIST[ 186], "tlb_flush"}, 366 | {0xC0, &UMASK_LIST[ 189], "inst_retired"}, 367 | {0xC1, &UMASK_LIST[ 192], "other_assists"}, 368 | {0xC2, &UMASK_LIST[ 196], "uops_retired"}, 369 | {0xC3, &UMASK_LIST[ 199], "machine_clears"}, 370 | {0xC4, &UMASK_LIST[ 203], "br_inst_retired"}, 371 | {0xC5, &UMASK_LIST[ 212], "br_misp_retired"}, 372 | {0xC8, &UMASK_LIST[ 217], "hle_retired"}, 373 | {0xC9, &UMASK_LIST[ 226], "rtm_retired"}, 374 | {0xCA, &UMASK_LIST[ 235], "fp_assist"}, 375 | {0xCC, &UMASK_LIST[ 241], "rob_misc_events"}, 376 | /* Ghost [0xCD] MEM_TRANS_RETIRED */ 377 | {0xD0, &UMASK_LIST[ 243], "mem_uops_retired"}, 378 | {0xD1, &UMASK_LIST[ 251], "mem_load_uops_retired"}, 379 | {0xD2, &UMASK_LIST[ 259], "mem_load_uops_l3_hit_retired"}, 380 | {0xD3, &UMASK_LIST[ 264], "mem_load_uops_l3_miss_retired"}, 381 | {0xE6, &UMASK_LIST[ 266], "baclears"}, 382 | {0xF0, &UMASK_LIST[ 268], "l2_trans"}, 383 | {0xF1, &UMASK_LIST[ 277], "l2_lines_in"}, 384 | {0xF2, &UMASK_LIST[ 282], "l2_lines_out"}, 385 | {0x00, NULL , NULL} 386 | }; 387 | static const char* const PFC_ERROR_MESSAGES[] = { 388 | [-PFC_ERR_OK] = "Success", 389 | [-PFC_ERR_OPENING_SYSFILE] = "Error opening the /sys/module/pfc files. Is the kernel module loaded?", 390 | [-PFC_ERR_PWRITE_FAILED] = "Error writing to the driver configuration files (pwrite failed).", 391 | [-PFC_ERR_PWRITE_TOO_FEW] = "Write to driver config files wrote fewer bytes than expected.", 392 | [-PFC_ERR_CPU_PIN_FAILED] = "Pinning benchmark to single CPU failed.", 393 | [-PFC_ERR_CR4_PCE_NOT_SET] = "CR4.PCE not set. Try echo 2 > /sys/bus/event_source/devices/cpu/rdpmc.", 394 | [-PFC_ERR_AFFINITY_FAILED] = "Setting CPU affinity failed (perhaps affinity is set externally excluding CPU 0?)", 395 | [-PFC_ERR_READING_MASKS] = "Didn't read the expected number of mask bytes from the sysfs", 396 | }; 397 | 398 | /* Function Definitions */ 399 | 400 | int pfcInit (void){ 401 | /** 402 | * Open the magic files perfcount gives us access to 403 | */ 404 | 405 | cfgFd = open("/sys/module/pfc/config", O_RDWR | O_CLOEXEC); 406 | mskFd = open("/sys/module/pfc/masks", O_RDONLY | O_CLOEXEC); 407 | cntFd = open("/sys/module/pfc/counts", O_RDWR | O_CLOEXEC); 408 | msrFd = open("/sys/module/pfc/msr", O_RDONLY | O_CLOEXEC); 409 | cr4Fd = open("/sys/module/pfc/cr4.pce", O_RDONLY | O_CLOEXEC); 410 | 411 | /** 412 | * If failed to open, abort. 413 | */ 414 | 415 | if(cfgFd<0 || mskFd<0 || cntFd<0 || msrFd<0 || cr4Fd<0){ 416 | pfcFini(); 417 | return PFC_ERR_OPENING_SYSFILE; 418 | } 419 | 420 | /** 421 | * Check the CR4.PCE bit exposed by the kernel driver to check that we can issue rdpmc 422 | * calls from user space. If we fail to open the file, just keep going (e.g., old kernel 423 | * driver with new userspace, or secure permissions). 424 | */ 425 | 426 | if(cr4Fd != -1){ 427 | unsigned char buf[1]; 428 | int n = read(cr4Fd, buf, sizeof(buf)); 429 | close(cr4Fd); 430 | cr4Fd = -1; 431 | if(n != 1 || buf[0] != '1'){ 432 | return PFC_ERR_CR4_PCE_NOT_SET; 433 | } 434 | } 435 | 436 | /** 437 | * Read out mask information to learn bitwidths. 438 | */ 439 | 440 | if(pread(mskFd, masks, sizeof(masks), 0) != sizeof(masks)){ 441 | return PFC_ERR_READING_MASKS; 442 | } 443 | 444 | return 0; 445 | } 446 | 447 | void pfcFini (void){ 448 | close(cfgFd); 449 | cfgFd = -1; 450 | close(mskFd); 451 | mskFd = -1; 452 | close(cntFd); 453 | cntFd = -1; 454 | close(msrFd); 455 | msrFd = -1; 456 | close(cr4Fd); 457 | cr4Fd = -1; 458 | } 459 | 460 | int pfcPinThread (int core){ 461 | cpu_set_t cpuset; 462 | CPU_ZERO(&cpuset); 463 | CPU_SET(core, &cpuset); 464 | if (sched_setaffinity(0, sizeof(cpuset), &cpuset) == -1) { 465 | return PFC_ERR_AFFINITY_FAILED; 466 | } 467 | return 0; 468 | } 469 | 470 | int pfcWrCfgs (int k, int n, const PFC_CFG* cfg){ 471 | ssize_t wrSize = sizeof(*cfg)*n; 472 | ssize_t actual = pwrite(cfgFd, cfg, wrSize, k*sizeof(*cfg)); 473 | if (actual == -1) { 474 | return PFC_ERR_PWRITE_FAILED; 475 | } else if (actual < wrSize) { 476 | return PFC_ERR_PWRITE_TOO_FEW; 477 | } 478 | return 0; 479 | } 480 | int pfcRdCfgs (int k, int n, PFC_CFG* cfg){ 481 | return pread (cfgFd, cfg, sizeof(*cfg)*n, k*sizeof(*cfg)); 482 | } 483 | int pfcWrCnts (int k, int n, const PFC_CNT* cnt){ 484 | return pwrite(cntFd, cnt, sizeof(*cnt)*n, k*sizeof(*cnt)); 485 | } 486 | int pfcRdCnts (int k, int n, PFC_CNT* cnt){ 487 | return pread (cntFd, cnt, sizeof(*cnt)*n, k*sizeof(*cnt)); 488 | } 489 | int pfcRdMSR (uint64_t off, uint64_t* msr){ 490 | return pread (msrFd, msr, sizeof(*msr), off); 491 | } 492 | 493 | uint64_t pfcParseCfg (const char* s){ 494 | uint64_t edgeTriggered = 0, 495 | evtNum = 0, 496 | umaskVal = 0, 497 | user = 1, 498 | os = 0, 499 | anythread = 0, 500 | inv = 0, 501 | cmask = 0; 502 | int i = 0; 503 | int doneModeParsing = 0; 504 | const UMASK* umaskList = NULL; 505 | 506 | 507 | /** 508 | * Null configs result in disabled counter. 509 | */ 510 | 511 | if(!s || s[0] == '\0'){ 512 | return 0; 513 | } 514 | 515 | /** 516 | * Is it edge triggered? 517 | */ 518 | 519 | if(s[0] == '*'){ 520 | edgeTriggered = 1; 521 | s++; 522 | } 523 | 524 | /** 525 | * Find event number and umask list. 526 | */ 527 | 528 | while(EVENT_LIST[i].name){ 529 | int n = strlen(EVENT_LIST[i].name); 530 | if(strncasecmp(s, EVENT_LIST[i].name, n) == 0 && s[n] == '.'){ 531 | /* Found it. */ 532 | evtNum = EVENT_LIST[i].evtNum; 533 | umaskList = EVENT_LIST[i].umasks; 534 | s += n+1; 535 | break; 536 | } 537 | 538 | i++; 539 | } 540 | if(!EVENT_LIST[i].name){ 541 | /* We didn't find the event by name. Parse as integer. */ 542 | evtNum = strtoull(s, (char**)&s, 0); 543 | if(s[0] != '.' || evtNum > 0xFF){ 544 | return 0; 545 | } 546 | s++; 547 | 548 | i=0; 549 | while(EVENT_LIST[i].name){ 550 | if(EVENT_LIST[i].evtNum == evtNum){ 551 | /* Found it. */ 552 | umaskList = EVENT_LIST[i].umasks; 553 | break; 554 | } 555 | 556 | i++; 557 | } 558 | 559 | if(!umaskList){ 560 | /* Couldn't find event at all. */ 561 | return 0; 562 | } 563 | } 564 | 565 | /** 566 | * Find umask value. 567 | */ 568 | 569 | i=0; 570 | while(umaskList[i].name){ 571 | int n = strlen(umaskList[i].name); 572 | if(strncasecmp(s, umaskList[i].name, n) == 0 && 573 | (s[n] == '\0' || s[n] == '<' || (s[n] == '>' && s[n+1] == '=') || s[n] == ':')){ 574 | /* Found it. */ 575 | umaskVal = umaskList[i].umaskVal; 576 | s += n; 577 | break; 578 | } 579 | 580 | i++; 581 | } 582 | if(!umaskList[i].name){ 583 | umaskVal = strtoull(s, (char**)&s, 0); 584 | if(umaskVal > 0xFF || (s[0] != '\0' && s[0] != '<' && !(s[0] == '>' && s[1] == '=') && s[0] != ':')){ 585 | /* Parsing umask as an integer made no sense. */ 586 | return 0; 587 | } 588 | } 589 | 590 | /** 591 | * Parse comparison sign and cmask if available. 592 | */ 593 | 594 | if(s[0] == '>' && s[1] == '=' && isdigit(s[2])){ 595 | inv = 0; 596 | cmask = strtoull(s+2, (char**)&s, 0) & 0xFF; 597 | }else if(s[0] == '<' && isdigit(s[1])){ 598 | inv = 1; 599 | cmask = strtoull(s+1, (char**)&s, 0) & 0xFF; 600 | } 601 | 602 | /** 603 | * Parse mode bits if available. 604 | */ 605 | 606 | if(s[0] == ':'){ 607 | anythread = user = os = 0; 608 | while(!doneModeParsing){ 609 | switch(*++s){ 610 | case 'A': 611 | case 'a': anythread = 1;break; 612 | case 'U': 613 | case 'u': user = 1;break; 614 | case 'K': 615 | case 'k': os = 1;break; 616 | default : doneModeParsing = 1;break; 617 | } 618 | } 619 | } 620 | 621 | /** 622 | * At last, assemble the pieces. 623 | */ 624 | 625 | PFC_CFG cfg = (cmask << 24) | 626 | (inv << 23) | 627 | (1ULL << 22) | 628 | (anythread << 21) | 629 | (edgeTriggered << 18) | 630 | (os << 17) | 631 | (user << 16) | 632 | (umaskVal << 8) | 633 | (evtNum << 0); 634 | return cfg; 635 | } 636 | 637 | void pfcDumpEvts (void){ 638 | const EVENT* evt = EVENT_LIST; 639 | const UMASK* umask; 640 | 641 | printf("Available events:\n"); 642 | while(evt->evtNum){ 643 | printf("\t%s:\n", evt->name); 644 | 645 | umask = evt->umasks; 646 | while(umask->name){ 647 | printf("\t\t%s:\n", umask->name); 648 | umask++; 649 | } 650 | 651 | evt++; 652 | } 653 | } 654 | 655 | void pfcRemoveBias (PFC_CNT* b, int64_t mul){ 656 | PFC_CNT warmup[7] = {0,0,0,0,0,0,0}; 657 | int i; 658 | 659 | for(i=0;i<10;i++){ 660 | /** 661 | * This code sequence is the opposite of PFCSTART/PFCEND: It adds, then 662 | * subtracts. The net effect is a subtraction by an amount equal to the 663 | * bias. 664 | * 665 | * We execute this loop 10 times to ensure the loop and warmup buffer 666 | * are both "hot" and the branch predictor is settled, then break out 667 | * of the loop and apply the computed bias to the argument buffer. 668 | */ 669 | 670 | memset(warmup, 0, sizeof(warmup)); 671 | asm volatile( 672 | _pfc_asm_code_(add) 673 | _pfc_asm_code_(sub) 674 | : /* Outputs */ 675 | : "r"((warmup)) /* Inputs */ 676 | : "memory", "rax", "rcx", "rdx" 677 | ); 678 | } 679 | 680 | for(i=0;i<7;i++){ 681 | b[i] += warmup[i]*mul; 682 | b[i] &= masks[i]; 683 | } 684 | } 685 | 686 | const char *pfcErrorString(int err) { 687 | if(-err >= sizeof(PFC_ERROR_MESSAGES)/sizeof(PFC_ERROR_MESSAGES[0])){ 688 | return "Unknown Error"; 689 | } 690 | 691 | return PFC_ERROR_MESSAGES[-err]; 692 | } 693 | 694 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | # Library source files 2 | libpfcSrcs = files( 3 | 'libpfc.c' 4 | ) 5 | 6 | 7 | # Dependencies 8 | # 9 | # To be reviewed: 10 | # 11 | cc = meson.get_compiler('c') 12 | mDep = cc .find_library('m', required : false) 13 | 14 | libpfcDeps = [mDep] 15 | libpfcIncs = [libpfcIncs, kmodIncs] 16 | 17 | libpfc = library('pfc', libpfcSrcs, 18 | include_directories: libpfcIncs, 19 | dependencies: libpfcDeps, 20 | install: true) 21 | 22 | 23 | 24 | # Utilities 25 | pfcutilSrcs = files('pfcutil.c') 26 | pfcutilDeps = [mDep] 27 | pfcutil = executable('pfcutil', pfcutilSrcs, 28 | include_directories: libpfcIncs, 29 | link_with: [libpfc], 30 | dependencies: pfcutilDeps, 31 | install: true) 32 | 33 | 34 | # Demo applications 35 | 36 | # Simple FMA+VPADDD loop 37 | pfcdemoSrcs = files('pfcdemo.c') 38 | pfcdemoDeps = [mDep] 39 | 40 | pfcdemo = executable('pfcdemo', pfcdemoSrcs, 41 | include_directories: libpfcIncs, 42 | link_with: [libpfc], 43 | dependencies: pfcdemoDeps, 44 | install: true) 45 | 46 | 47 | # A much more complicated example to analyze the behaviour of cycle counters 48 | # under TurboBoost. 49 | pfcdemoreftscSrcs = files('pfcdemoreftsc.cpp') 50 | pfcdemoreftscDeps = [mDep] 51 | 52 | pfcdemoreftsc = executable('pfcdemoreftsc', pfcdemoreftscSrcs, 53 | include_directories: libpfcIncs, 54 | link_with: [libpfc], 55 | dependencies: pfcdemoreftscDeps, 56 | install: true) 57 | 58 | 59 | # Determine the TurboBoost hits. 60 | pfctbhitSrcs = files('pfctbhit.c', 'pfctbhitasm.S') 61 | pfctbhitDeps = [mDep] 62 | 63 | pfctbhit = executable('pfctbhit', pfctbhitSrcs, 64 | include_directories: libpfcIncs, 65 | link_with: [libpfc], 66 | dependencies: pfctbhitDeps, 67 | install: true) 68 | 69 | -------------------------------------------------------------------------------- /src/pfcdemo.c: -------------------------------------------------------------------------------- 1 | /* Includes */ 2 | #include "libpfc.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | /* Typedefs */ 13 | typedef unsigned long long ull; 14 | typedef signed long long sll; 15 | 16 | 17 | /** 18 | * Code snippet to bench 19 | */ 20 | 21 | static inline void code1(void){ 22 | asm volatile( 23 | "vzeroall\n\t" 24 | "xor %%eax, %%eax\n\t" 25 | "mov $1000000000, %%rsi\n\t" 26 | "0:\n\t" 27 | "vfmadd231ps %%ymm0, %%ymm15, %%ymm0\n\t" 28 | "vfmadd231ps %%ymm1, %%ymm15, %%ymm1\n\t" 29 | "vfmadd231ps %%ymm2, %%ymm15, %%ymm2\n\t" 30 | "vpaddd %%ymm10, %%ymm15, %%ymm10\n\t" 31 | "vfmadd231ps %%ymm3, %%ymm15, %%ymm3\n\t" 32 | "vfmadd231ps %%ymm4, %%ymm15, %%ymm4\n\t" 33 | "vfmadd231ps %%ymm5, %%ymm15, %%ymm5\n\t" 34 | "vpaddd %%ymm11, %%ymm15, %%ymm11\n\t" 35 | "vfmadd231ps %%ymm6, %%ymm15, %%ymm6\n\t" 36 | "vfmadd231ps %%ymm7, %%ymm15, %%ymm7\n\t" 37 | "vpaddd %%ymm12, %%ymm15, %%ymm12\n\t" 38 | "vpaddd %%ymm13, %%ymm15, %%ymm13\n\t" 39 | "vfmadd231ps %%ymm8, %%ymm15, %%ymm8\n\t" 40 | "vfmadd231ps %%ymm9, %%ymm15, %%ymm9\n\t" 41 | "vpaddd %%ymm14, %%ymm15, %%ymm14\n\t" 42 | "dec %%rsi\n\t" 43 | "jnz 0b\n\t" 44 | : /* No outputs we care about */ 45 | : /* No inputs we care about */ 46 | : "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", 47 | "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", 48 | "rsi", "rax", "rbx", "rcx", "rdx", 49 | "memory" 50 | ); 51 | } 52 | 53 | 54 | /* Test Schedule */ 55 | static const char* const SCHEDULE[]; 56 | static const int NUMCOUNTS; 57 | 58 | 59 | /** 60 | * Main 61 | */ 62 | 63 | int main(int argc, char* argv[]){ 64 | int i; 65 | 66 | int verbose = 0, dump = 0, option; 67 | 68 | /** 69 | * Process command line arguments 70 | */ 71 | while ((option = getopt(argc, argv, "dv")) != -1) { 72 | switch (option) { 73 | case 'v': 74 | verbose = 1; 75 | break; 76 | case 'd': 77 | dump = 1; 78 | break; 79 | default: 80 | fprintf(stderr, "Usage: pfcdemo [-d] [-v]\n" 81 | "\t-d\n\t\tDump the list of available events and quit\n" 82 | "\t-v\n\t\tVerbose output\n"); 83 | exit(1); 84 | } 85 | } 86 | 87 | (void)verbose; 88 | 89 | if(dump){ 90 | pfcDumpEvts(); 91 | exit(1); 92 | } 93 | 94 | /** 95 | * Initialize library. 96 | */ 97 | 98 | pfcPinThread(3); 99 | if(pfcInit() != 0){ 100 | printf("Could not open /sys/module/pfc/* handles; Is module loaded?\n"); 101 | exit(1); 102 | } 103 | 104 | 105 | 106 | 107 | /** 108 | * Warm up. 109 | */ 110 | 111 | static const PFC_CNT ZERO_CNT[7] = {0,0,0,0,0,0,0}; 112 | PFC_CNT CNT[7] = {0,0,0,0,0,0,0}; 113 | PFC_CFG CFG[7] = {2,2,2,0,0,0,0}; 114 | for(i=0;i<1;i++){ 115 | code1(); 116 | } 117 | 118 | /** 119 | * Run master loop under all counter configurations in SCHEDULE. 120 | */ 121 | 122 | for(i=0;i=1", 199 | "uops_issued.any>=2", 200 | /* Batch */ 201 | "uops_issued.any>=3", 202 | "uops_issued.any>=4", 203 | "uops_issued.any>=5", 204 | "uops_issued.any>=6", 205 | /* Batch */ 206 | "uops_executed_port.port_0", 207 | "uops_executed_port.port_1", 208 | "uops_executed_port.port_2", 209 | "uops_executed_port.port_3", 210 | /* Batch */ 211 | "uops_executed_port.port_4", 212 | "uops_executed_port.port_5", 213 | "uops_executed_port.port_6", 214 | "uops_executed_port.port_7", 215 | /* Batch */ 216 | "resource_stalls.any", 217 | "resource_stalls.rs", 218 | "resource_stalls.sb", 219 | "resource_stalls.rob", 220 | /* Batch */ 221 | "uops_retired.all", 222 | "uops_retired.all<1", 223 | "uops_retired.all>=1", 224 | "uops_retired.all>=2", 225 | /* Batch */ 226 | "uops_retired.all>=3", 227 | "uops_retired.all>=4", 228 | "uops_retired.all>=5", 229 | "uops_retired.all>=6", 230 | /* Batch */ 231 | "inst_retired.any_p", 232 | "inst_retired.any_p<1", 233 | "inst_retired.any_p>=1", 234 | "inst_retired.any_p>=2", 235 | /* Batch */ 236 | "inst_retired.any_p>=3", 237 | "inst_retired.any_p>=4", 238 | "inst_retired.any_p>=5", 239 | "inst_retired.any_p>=6", 240 | /* Batch */ 241 | "idq_uops_not_delivered.core", 242 | "idq_uops_not_delivered.core<1", 243 | "idq_uops_not_delivered.core>=1", 244 | "idq_uops_not_delivered.core>=2", 245 | /* Batch */ 246 | "idq_uops_not_delivered.core>=3", 247 | "idq_uops_not_delivered.core>=4", 248 | "rs_events.empty", 249 | "idq.empty", 250 | /* Batch */ 251 | "idq.mite_uops", 252 | "idq.dsb_uops", 253 | "idq.ms_dsb_uops", 254 | "idq.ms_mite_uops", 255 | /* Batch */ 256 | "idq.mite_all_uops", 257 | "idq.mite_all_uops<1", 258 | "idq.mite_all_uops>=1", 259 | "idq.mite_all_uops>=2", 260 | /* Batch */ 261 | "idq.mite_all_uops>=3", 262 | "idq.mite_all_uops>=4", 263 | "move_elimination.int_not_eliminated", 264 | "move_elimination.simd_not_eliminated", 265 | /* Batch */ 266 | "lsd.uops", 267 | "lsd.uops<1", 268 | "lsd.uops>=1", 269 | "lsd.uops>=2", 270 | /* Batch */ 271 | "lsd.uops>=3", 272 | "lsd.uops>=4", 273 | "ild_stall.lcp", 274 | "ild_stall.iq_full", 275 | /* Batch */ 276 | "br_inst_exec.all_branches", 277 | "br_inst_exec.0x81", 278 | "br_inst_exec.0x82", 279 | "icache.misses", 280 | /* Batch */ 281 | "br_misp_exec.all_branches", 282 | "br_misp_exec.0x81", 283 | "br_misp_exec.0x82", 284 | "fp_assist.any", 285 | /* Batch */ 286 | "cpu_clk_unhalted.core_clk", 287 | "cpu_clk_unhalted.ref_xclk", 288 | "baclears.any", 289 | "idq.ms_uops" 290 | }; 291 | static const int NUMCOUNTS = sizeof(SCHEDULE)/sizeof(*SCHEDULE); 292 | 293 | -------------------------------------------------------------------------------- /src/pfcdemoreftsc.cpp: -------------------------------------------------------------------------------- 1 | /* Includes */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "libpfc.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | /** 14 | * A 1CC/iter workload. 15 | * 16 | * Almost exactly one *core* clock cycle per iteration on modern x86 CPUs 17 | * capable of macro-fusion of the arithmetic+conditional jump pattern. 18 | */ 19 | 20 | extern "C" void add_calibration(uint64_t x); 21 | asm volatile( 22 | ".text\n\t" 23 | ".global add_calibration\n\t" 24 | "add_calibration:\n\t" 25 | "sub $1, %rdi\n\t" 26 | "jnz add_calibration\n\t" 27 | "ret\n\t" 28 | ); 29 | 30 | 31 | /** 32 | * The C++ timer, which uses rdtsc anyhow. 33 | */ 34 | 35 | int64_t nanos(){ 36 | auto t = std::chrono::high_resolution_clock::now(); 37 | return std::chrono::time_point_cast(t).time_since_epoch().count(); 38 | } 39 | 40 | 41 | /** 42 | * Main. 43 | */ 44 | 45 | int main(int argc, char* argv[]){ 46 | uint64_t CALIBRATION_NANOS = 1000000000, 47 | CALIBRATION_ITERS = 100, 48 | CALIBRATION_ADDS; 49 | int CORE = 0; 50 | FILE* LOG = NULL; 51 | int i, ret, isterminal = isatty(1); 52 | uint64_t maxNonTurbo = 0; 53 | uint64_t miscEn = 0, platInfo = 0, tempTarget = 0, 54 | throttleReason, pkgThermStatus, pkgThermInterrupt; 55 | PFC_CFG cfg[7] = {7,7,7,0,0,0,0}; 56 | PFC_CNT cnt[7] = {0,0,0,0,0,0,0}; 57 | const char* startDel = isterminal ? "\033[1m* " : "* ", 58 | * endDel = isterminal ? "\033[0m" : ""; 59 | 60 | 61 | /* El-Cheapo Argument Parsing */ 62 | for(i=1;i> 8) & 0xFF; 111 | CALIBRATION_ADDS = CALIBRATION_NANOS*maxNonTurbo/10; 112 | 113 | 114 | /* Configure a couple counters. */ 115 | /* Reference XCLK */ 116 | cfg[3] = pfcParseCfg("cpu_clk_unhalted.ref_xclk:auk"); 117 | /* OS-mode Core Clocks, serves as a loose measure of kernel overhead. */ 118 | cfg[4] = pfcParseCfg("cpu_clk_unhalted.core_clk:k"); 119 | /* User-OS Transition Count */ 120 | cfg[5] = pfcParseCfg("*cpl_cycles.ring0>=1:uk"); 121 | pfcWrCfgs(0, 7, cfg); 122 | pfcWrCnts(0, 7, cnt); 123 | 124 | 125 | /* Warm up the CPU. */ 126 | for(volatile int v = 0; v < 1000000000; v++){} 127 | 128 | 129 | /* Periodically dump the synchronized measurements of several counters, timers and MSRs. */ 130 | for(i=0;i<(int)CALIBRATION_ITERS;i++){ 131 | memset(cnt, 0, sizeof(cnt)); 132 | int64_t start = nanos(), now; 133 | PFCSTART(cnt); 134 | int64_t tsc =__rdtsc(); 135 | add_calibration(CALIBRATION_ADDS); 136 | PFCEND(cnt); 137 | int64_t tsc_delta = __rdtsc() - tsc; 138 | now = nanos(); 139 | 140 | int64_t delta = now - start; 141 | pfcRdMSR(MSR_CORE_PERF_LIMIT_REASONS, &throttleReason); 142 | pfcRdMSR(MSR_IA32_PACKAGE_THERM_STATUS, &pkgThermStatus); 143 | pfcRdMSR(MSR_IA32_PACKAGE_THERM_INTERRUPT, &pkgThermInterrupt); 144 | printf("CPU %d, measured CLK_REF_TSC MHz : %16.2f\n", sched_getcpu(), 1000.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC] / delta); 145 | printf("CPU %d, measured rdtsc MHz : %16.2f\n", sched_getcpu(), 1000.0 * tsc_delta / delta); 146 | printf("CPU %d, measured add MHz : %16.2f\n", sched_getcpu(), 1000.0 * CALIBRATION_ADDS / delta); 147 | printf("CPU %d, measured XREF_CLK time (s) : %16.8f\n", sched_getcpu(), cnt[3]/1e8); 148 | printf("CPU %d, measured delta time (s) : %16.8f\n", sched_getcpu(), delta/1e9); 149 | printf("CPU %d, measured tsc_delta time (s) : %16.8f\n", sched_getcpu(), tsc_delta/2.4e9); 150 | printf("CPU %d, ratio ref_tsc :ref_xclk : %16.8f\n", sched_getcpu(), (double)cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/cnt[3]); 151 | printf("CPU %d, ratio ref_core:ref_xclk : %16.8f\n", sched_getcpu(), (double)cnt[PFC_FIXEDCNT_CPU_CLK_UNHALTED]/cnt[3]); 152 | printf("CPU %d, ratio rdtsc :ref_xclk : %16.8f\n", sched_getcpu(), (double)tsc_delta/cnt[3]); 153 | printf("CPU %d, core CLK cycles in OS : %16llu\n", sched_getcpu(), (unsigned long long)(cnt[4])); 154 | printf("CPU %d, User-OS transitions : %16llu\n", sched_getcpu(), (unsigned long long)(cnt[5])); 155 | printf("CPU %d, rdtsc-reftsc overcount : %16lld\n", sched_getcpu(), (signed long long)(tsc_delta - cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC])); 156 | printf("CPU %d, MSR_IA32_PACKAGE_THERM_STATUS : %016llx\n", sched_getcpu(), (unsigned long long)pkgThermStatus); 157 | printf("CPU %d, MSR_IA32_PACKAGE_THERM_INTERRUPT: %016llx\n", sched_getcpu(), (unsigned long long)pkgThermInterrupt); 158 | printf("CPU %d, MSR_CORE_PERF_LIMIT_REASONS : %016llx\n", sched_getcpu(), (unsigned long long)throttleReason); 159 | printf(" %sPROCHOT%s\n", throttleReason & 0x00010000 ? startDel : " ", throttleReason & 0x00010000 ? endDel : ""); 160 | printf(" %sThermal%s\n", throttleReason & 0x00020000 ? startDel : " ", throttleReason & 0x00020000 ? endDel : ""); 161 | printf(" %sGraphics Driver%s\n", throttleReason & 0x00100000 ? startDel : " ", throttleReason & 0x00100000 ? endDel : ""); 162 | printf(" %sAutonomous Utilization-Based Frequency Control%s\n", throttleReason & 0x00200000 ? startDel : " ", throttleReason & 0x00200000 ? endDel : ""); 163 | printf(" %sVoltage Regulator Thermal Alert%s\n", throttleReason & 0x00400000 ? startDel : " ", throttleReason & 0x00400000 ? endDel : ""); 164 | printf(" %sElectrical Design Point (e.g. Current)%s\n", throttleReason & 0x01000000 ? startDel : " ", throttleReason & 0x01000000 ? endDel : ""); 165 | printf(" %sCore Power Limiting%s\n", throttleReason & 0x02000000 ? startDel : " ", throttleReason & 0x02000000 ? endDel : ""); 166 | printf(" %sPackage-Level PL1 Power Limiting%s\n", throttleReason & 0x04000000 ? startDel : " ", throttleReason & 0x04000000 ? endDel : ""); 167 | printf(" %sPackage-Level PL2 Power Limiting%s\n", throttleReason & 0x08000000 ? startDel : " ", throttleReason & 0x08000000 ? endDel : ""); 168 | printf(" %sMax Turbo Limit (Multi-Core Turbo)%s\n", throttleReason & 0x10000000 ? startDel : " ", throttleReason & 0x10000000 ? endDel : ""); 169 | printf(" %sTurbo Transition Attenuation%s\n", throttleReason & 0x20000000 ? startDel : " ", throttleReason & 0x20000000 ? endDel : ""); 170 | 171 | fprintf(LOG, "%.2f,%.2f,%.2f,%lld,%llu,%llu\n", 172 | (double)cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/cnt[3], 173 | (double)cnt[PFC_FIXEDCNT_CPU_CLK_UNHALTED]/cnt[3], 174 | (double)tsc_delta/cnt[3], 175 | (signed long long)(tsc_delta - cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]), 176 | (unsigned long long)(cnt[4]), 177 | (unsigned long long)(cnt[5])); 178 | } 179 | fflush(LOG); 180 | fclose(LOG); 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /src/pfctbhit.c: -------------------------------------------------------------------------------- 1 | /* Includes */ 2 | #include "libpfc.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | /** 12 | * Data structures. 13 | */ 14 | 15 | struct TBHIT; 16 | typedef struct TBHIT TBHIT; 17 | struct TBHIT{ 18 | uint64_t tstart, tend, hit; 19 | }; 20 | 21 | 22 | /* Assembler function */ 23 | extern void pfcSampleTBHITs(TBHIT* tbhit, 24 | size_t n, 25 | size_t THRESH); 26 | 27 | 28 | /** 29 | * Main 30 | */ 31 | 32 | int main(int argc, char* argv[]){ 33 | size_t i; 34 | int CORE = 0; 35 | size_t THRESH = 1000; 36 | size_t BUFFER = 1024; 37 | TBHIT* hits = NULL; 38 | PFC_CFG cfg[7] = {7,7,7,0,0,0,0}; 39 | PFC_CNT cnt[7] = {0,0,0,0,0,0,0}; 40 | 41 | 42 | /* Arg parse */ 43 | for(i=1;argv[i];i++){ 44 | if (!strcmp(argv[i], "--core")){ 45 | CORE = argv[i+1] ? strtol (argv[++i], 0, 0) : CORE; 46 | }else if(!strcmp(argv[i], "--thresh")){ 47 | THRESH = argv[i+1] ? strtoull(argv[++i], 0, 0) : THRESH; 48 | }else if(!strcmp(argv[i], "--buf")){ 49 | BUFFER = argv[i+1] ? strtoull(argv[++i], 0, 0) : BUFFER; 50 | }else{ 51 | fprintf(stderr, "Unknown argument %s!\n", argv[i]); 52 | exit(1); 53 | } 54 | } 55 | 56 | /* Buffer allocate, then touch each page with memset() */ 57 | hits = malloc(sizeof(*hits) * BUFFER); 58 | memset(hits, 0, sizeof(*hits) * BUFFER); 59 | 60 | /* libpfc init */ 61 | pfcPinThread(CORE); 62 | if(pfcInit() != 0){ 63 | printf("Could not open /sys/module/pfc/* handles; Is module loaded?\n"); 64 | exit(1); 65 | } 66 | 67 | /* Setup counters */ 68 | cfg[3] = pfcParseCfg("*cpl_cycles.ring0>=1:uk"); 69 | pfcWrCfgs(0, 7, cfg); 70 | pfcWrCnts(0, 7, cnt); 71 | 72 | /* Iterate endlessly. */ 73 | while(1){ 74 | pfcSampleTBHITs(hits, BUFFER, THRESH); 75 | for(i=0;i 0 indicates how many slots we must still fill in that buffers, and 15 | # r8 contains the THRESHold. 16 | mov ecx, 0 17 | rdpmc 18 | shl rdx, 32 19 | or rdx, rax 20 | mov r9, rdx 21 | rdtscp 22 | shl rdx, 32 23 | or rdx, rax 24 | mov r10, rdx 25 | mov r11, rdx 26 | 1: 27 | # Core loop iteration. 28 | # At this stage: 29 | # r8 contains the cycle threshold to consider as a TB preemption. 30 | # r9 contains OS preemption count at entry into outer loop 31 | # r10 contains timestamp at entry into core loop 32 | # r11 contains timestamp at entry into outer loop 33 | rdtscp 34 | shl rdx, 32 35 | or rdx, rax 36 | mov rbx, rdx 37 | mov ecx, 0 38 | rdpmc 39 | shl rdx, 32 40 | or rdx, rax 41 | 42 | # Were we preempted by OS? 43 | cmp rdx, r9 44 | jne 0b 45 | 46 | # No OS preemption... but maybe TB preemption? 47 | xchg r10, rbx 48 | neg rbx 49 | add rbx, r10 50 | cmp rbx, r8 51 | jb 1b 52 | 53 | # We have been preempted for more than THRESH cycles. Record this event in the buffer. 54 | mov [rdi+ 0], r11 55 | mov [rdi+ 8], r10 56 | mov [rdi+16], rbx 57 | add rdi, 24 58 | dec rsi 59 | jne 0b 60 | 61 | 10: 62 | # Return. 63 | pop rbx 64 | retq 65 | 66 | .att_syntax noprefix 67 | -------------------------------------------------------------------------------- /src/pfcutil.c: -------------------------------------------------------------------------------- 1 | /* Includes */ 2 | #include "libpfc.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | /** 12 | * Main 13 | */ 14 | 15 | int main(int argc, char* argv[]){ 16 | if(pfcInit() != 0){ 17 | printf("Could not open /sys/module/pfc/* handles; Is module loaded?\n"); 18 | exit(1); 19 | } 20 | 21 | unsigned msrNum = MSR_IA32_ENERGY_PERF_BIAS; 22 | uint64_t msr = 0; 23 | if(pfcRdMSR(msrNum, &msr) == sizeof(msr)){ 24 | printf("MSR %03x = %016llx\n", msrNum, (unsigned long long)msr); 25 | }else{ 26 | printf("Failed to read MSR %03x!\n", msrNum); 27 | } 28 | 29 | 30 | return 0; 31 | } 32 | --------------------------------------------------------------------------------