From 0d119262664ebaed47b562deb1eea17d3bf1db59 Mon Sep 17 00:00:00 2001 From: Marius Hillenbrand Date: Fri, 23 Oct 2015 11:30:55 +0200 Subject: [PATCH] original source of v2.8 --- .bdsignore.all | 24 + .gitattributes | 1 + .gitignore | 19 + Doxyfile | 1237 +++++ KSysGuard HOWTO.docx | Bin 0 -> 345803 bytes KSysGuard HOWTO.pdf | Bin 0 -> 257840 bytes MacMSRDriver/DriverInterface.c | 163 + MacMSRDriver/DriverInterface.h | 28 + MacMSRDriver/MSRAccessor.cpp | 111 + MacMSRDriver/MSRAccessor.h | 36 + MacMSRDriver/MSRAccessorPublic.h | 35 + MacMSRDriver/MSRKernel.h | 49 + MacMSRDriver/Makefile | 25 + MacMSRDriver/PCIDriverInterface.cpp | 235 + MacMSRDriver/PCIDriverInterface.h | 43 + MacMSRDriver/PcmMsr.xcodeproj/project.pbxproj | 438 ++ .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 92777 bytes .../xcdebugger/Breakpoints.xcbkptlist | 105 + .../xcschemes/PcmMsr.xcscheme | 58 + .../xcschemes/PcmMsrLibrary.xcscheme | 58 + .../xcschemes/xcschememanagement.plist | 37 + MacMSRDriver/PcmMsr/PcmMsr-Info.plist | 57 + MacMSRDriver/PcmMsr/PcmMsr-Prefix.pch | 4 + MacMSRDriver/PcmMsr/PcmMsr.cpp | 307 ++ MacMSRDriver/PcmMsr/PcmMsr.h | 58 + MacMSRDriver/PcmMsr/PcmMsrClient.cpp | 333 ++ MacMSRDriver/PcmMsr/PcmMsrClient.h | 81 + MacMSRDriver/PcmMsr/UserKernelShared.h | 67 + .../PcmMsr/en.lproj/InfoPlist.strings | 2 + MacMSRDriver/kextload.sh | 6 + MacMSRDriver/kextunload.sh | 4 + Makefile | 63 + PCM-MSR_Win/pcm-msr-win.cpp | 6 + PCM-MSR_Win/pcm-msr-win.vcproj | 275 + PCM-MSR_Win/stdafx.cpp | 8 + PCM-MSR_Win/stdafx.h | 16 + PCM-Memory_Win/pcm-memory-win.cpp | 6 + PCM-Memory_Win/pcm-memory-win.vcproj | 275 + PCM-Memory_Win/stdafx.cpp | 8 + PCM-Memory_Win/stdafx.h | 16 + PCM-NUMA_Win/pcm-numa-win.cpp | 6 + PCM-NUMA_Win/pcm-numa-win.vcproj | 275 + PCM-NUMA_Win/stdafx.cpp | 8 + PCM-NUMA_Win/stdafx.h | 16 + PCM-PCIE_Win/pcm-pcie-win.cpp | 6 + PCM-PCIE_Win/pcm-pcie-win.vcproj | 275 + PCM-PCIE_Win/stdafx.cpp | 8 + PCM-PCIE_Win/stdafx.h | 16 + PCM-Power_Win/pcm-power-win.cpp | 6 + PCM-Power_Win/pcm-power-win.vcproj | 275 + PCM-Power_Win/stdafx.cpp | 8 + PCM-Power_Win/stdafx.h | 16 + PCM-Service_Win/AssemblyInfo.cpp | 56 + PCM-Service_Win/PCM-Service.exe.config | 5 + PCM-Service_Win/PCMInstaller.cpp | 18 + PCM-Service_Win/PCMInstaller.h | 97 + PCM-Service_Win/PCMInstaller.resx | 129 + PCM-Service_Win/PCMService.cpp | 82 + PCM-Service_Win/PCMService.h | 517 ++ PCM-Service_Win/PCMService.resX | 123 + PCM-Service_Win/PCMService.vcproj | 511 ++ PCM-Service_Win/app.ico | Bin 0 -> 1078 bytes PCM-Service_Win/app.rc | 63 + PCM-Service_Win/resource.h | 3 + PCM-TSX_Win/pcm-tsx-win.cpp | 6 + PCM-TSX_Win/pcm-tsx-win.vcproj | 275 + PCM-TSX_Win/stdafx.cpp | 8 + PCM-TSX_Win/stdafx.h | 16 + PCM_Win/pcm.cpp | 19 + PCM_Win/pcm.vcproj | 274 + PCM_Win/stdafx.cpp | 21 + PCM_Win/stdafx.h | 29 + PCM_Win/windriver.h | 159 + TODO | 8 + WINDOWS_HOWTO.rtf | 1330 +++++ WinMSRDriver/Win7/makefile | 1 + WinMSRDriver/Win7/msr.h | 28 + WinMSRDriver/Win7/msrmain.c | 256 + WinMSRDriver/Win7/msrstruct.h | 50 + WinMSRDriver/Win7/mymake.bat | 9 + WinMSRDriver/Win7/sources | 24 + WinMSRDriver/WinXP/makefile | 1 + WinMSRDriver/WinXP/msr.h | 27 + WinMSRDriver/WinXP/msrmain.c | 191 + WinMSRDriver/WinXP/msrstruct.h | 40 + WinMSRDriver/WinXP/mymake.bat | 4 + WinMSRDriver/WinXP/sources | 16 + build_all.bat | 35 + check_win_build.sh | 6 + client_bw.cpp | 232 + client_bw.h | 66 + cpuasynchcounter.h | 198 + cpucounters.cpp | 4406 +++++++++++++++++ cpucounters.h | 2170 ++++++++ freegetopt/ChangeLog | 35 + freegetopt/LICENSE | 33 + freegetopt/README | 43 + freegetopt/getopt.c | 252 + freegetopt/getopt.h | 63 + gen_new_win_project.sh | 15 + intelpcm.so/Makefile | 15 + memoptest.cpp | 147 + msr.cpp | 234 + msr.h | 145 + msrtest.cpp | 93 + pci.cpp | 645 +++ pci.h | 160 + pcm-memory.cpp | 532 ++ pcm-msr.cpp | 120 + pcm-numa.cpp | 348 ++ pcm-pcie.cpp | 896 ++++ pcm-power.cpp | 516 ++ pcm-sensor.cpp | 602 +++ pcm-tsx.cpp | 471 ++ pcm.cpp | 1198 +++++ readmem.cpp | 89 + readmem.sh | 15 + realtime.cpp | 283 ++ types.h | 750 +++ utils.cpp | 397 ++ utils.h | 156 + width_extender.h | 163 + winpmem/winpmem.cpp | 213 + winpmem/winpmem.h | 58 + winring0/OlsApi.h | 580 +++ winring0/OlsApiInit.h | 322 ++ winring0/OlsApiInitDef.h | 98 + winring0/OlsApiInitExt.h | 100 + winring0/OlsDef.h | 63 + 130 files changed, 26575 insertions(+) create mode 100644 .bdsignore.all create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Doxyfile create mode 100644 KSysGuard HOWTO.docx create mode 100644 KSysGuard HOWTO.pdf create mode 100644 MacMSRDriver/DriverInterface.c create mode 100644 MacMSRDriver/DriverInterface.h create mode 100644 MacMSRDriver/MSRAccessor.cpp create mode 100644 MacMSRDriver/MSRAccessor.h create mode 100644 MacMSRDriver/MSRAccessorPublic.h create mode 100644 MacMSRDriver/MSRKernel.h create mode 100644 MacMSRDriver/Makefile create mode 100644 MacMSRDriver/PCIDriverInterface.cpp create mode 100644 MacMSRDriver/PCIDriverInterface.h create mode 100644 MacMSRDriver/PcmMsr.xcodeproj/project.pbxproj create mode 100644 MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/aiott.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist create mode 100644 MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsr.xcscheme create mode 100644 MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsrLibrary.xcscheme create mode 100644 MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 MacMSRDriver/PcmMsr/PcmMsr-Info.plist create mode 100644 MacMSRDriver/PcmMsr/PcmMsr-Prefix.pch create mode 100644 MacMSRDriver/PcmMsr/PcmMsr.cpp create mode 100644 MacMSRDriver/PcmMsr/PcmMsr.h create mode 100644 MacMSRDriver/PcmMsr/PcmMsrClient.cpp create mode 100644 MacMSRDriver/PcmMsr/PcmMsrClient.h create mode 100644 MacMSRDriver/PcmMsr/UserKernelShared.h create mode 100644 MacMSRDriver/PcmMsr/en.lproj/InfoPlist.strings create mode 100644 MacMSRDriver/kextload.sh create mode 100644 MacMSRDriver/kextunload.sh create mode 100644 Makefile create mode 100644 PCM-MSR_Win/pcm-msr-win.cpp create mode 100644 PCM-MSR_Win/pcm-msr-win.vcproj create mode 100644 PCM-MSR_Win/stdafx.cpp create mode 100644 PCM-MSR_Win/stdafx.h create mode 100644 PCM-Memory_Win/pcm-memory-win.cpp create mode 100644 PCM-Memory_Win/pcm-memory-win.vcproj create mode 100644 PCM-Memory_Win/stdafx.cpp create mode 100644 PCM-Memory_Win/stdafx.h create mode 100644 PCM-NUMA_Win/pcm-numa-win.cpp create mode 100644 PCM-NUMA_Win/pcm-numa-win.vcproj create mode 100644 PCM-NUMA_Win/stdafx.cpp create mode 100644 PCM-NUMA_Win/stdafx.h create mode 100644 PCM-PCIE_Win/pcm-pcie-win.cpp create mode 100644 PCM-PCIE_Win/pcm-pcie-win.vcproj create mode 100644 PCM-PCIE_Win/stdafx.cpp create mode 100644 PCM-PCIE_Win/stdafx.h create mode 100644 PCM-Power_Win/pcm-power-win.cpp create mode 100644 PCM-Power_Win/pcm-power-win.vcproj create mode 100644 PCM-Power_Win/stdafx.cpp create mode 100644 PCM-Power_Win/stdafx.h create mode 100644 PCM-Service_Win/AssemblyInfo.cpp create mode 100644 PCM-Service_Win/PCM-Service.exe.config create mode 100644 PCM-Service_Win/PCMInstaller.cpp create mode 100644 PCM-Service_Win/PCMInstaller.h create mode 100644 PCM-Service_Win/PCMInstaller.resx create mode 100644 PCM-Service_Win/PCMService.cpp create mode 100644 PCM-Service_Win/PCMService.h create mode 100644 PCM-Service_Win/PCMService.resX create mode 100644 PCM-Service_Win/PCMService.vcproj create mode 100644 PCM-Service_Win/app.ico create mode 100644 PCM-Service_Win/app.rc create mode 100644 PCM-Service_Win/resource.h create mode 100644 PCM-TSX_Win/pcm-tsx-win.cpp create mode 100644 PCM-TSX_Win/pcm-tsx-win.vcproj create mode 100644 PCM-TSX_Win/stdafx.cpp create mode 100644 PCM-TSX_Win/stdafx.h create mode 100644 PCM_Win/pcm.cpp create mode 100644 PCM_Win/pcm.vcproj create mode 100644 PCM_Win/stdafx.cpp create mode 100644 PCM_Win/stdafx.h create mode 100644 PCM_Win/windriver.h create mode 100644 TODO create mode 100644 WINDOWS_HOWTO.rtf create mode 100644 WinMSRDriver/Win7/makefile create mode 100644 WinMSRDriver/Win7/msr.h create mode 100644 WinMSRDriver/Win7/msrmain.c create mode 100644 WinMSRDriver/Win7/msrstruct.h create mode 100644 WinMSRDriver/Win7/mymake.bat create mode 100644 WinMSRDriver/Win7/sources create mode 100644 WinMSRDriver/WinXP/makefile create mode 100644 WinMSRDriver/WinXP/msr.h create mode 100644 WinMSRDriver/WinXP/msrmain.c create mode 100644 WinMSRDriver/WinXP/msrstruct.h create mode 100644 WinMSRDriver/WinXP/mymake.bat create mode 100644 WinMSRDriver/WinXP/sources create mode 100644 build_all.bat create mode 100644 check_win_build.sh create mode 100644 client_bw.cpp create mode 100644 client_bw.h create mode 100644 cpuasynchcounter.h create mode 100644 cpucounters.cpp create mode 100644 cpucounters.h create mode 100644 freegetopt/ChangeLog create mode 100644 freegetopt/LICENSE create mode 100644 freegetopt/README create mode 100644 freegetopt/getopt.c create mode 100644 freegetopt/getopt.h create mode 100644 gen_new_win_project.sh create mode 100644 intelpcm.so/Makefile create mode 100644 memoptest.cpp create mode 100644 msr.cpp create mode 100644 msr.h create mode 100644 msrtest.cpp create mode 100644 pci.cpp create mode 100644 pci.h create mode 100644 pcm-memory.cpp create mode 100644 pcm-msr.cpp create mode 100644 pcm-numa.cpp create mode 100644 pcm-pcie.cpp create mode 100644 pcm-power.cpp create mode 100644 pcm-sensor.cpp create mode 100644 pcm-tsx.cpp create mode 100644 pcm.cpp create mode 100644 readmem.cpp create mode 100644 readmem.sh create mode 100644 realtime.cpp create mode 100644 types.h create mode 100644 utils.cpp create mode 100644 utils.h create mode 100644 width_extender.h create mode 100644 winpmem/winpmem.cpp create mode 100644 winpmem/winpmem.h create mode 100644 winring0/OlsApi.h create mode 100644 winring0/OlsApiInit.h create mode 100644 winring0/OlsApiInitDef.h create mode 100644 winring0/OlsApiInitExt.h create mode 100644 winring0/OlsDef.h diff --git a/.bdsignore.all b/.bdsignore.all new file mode 100644 index 0000000..827cc63 --- /dev/null +++ b/.bdsignore.all @@ -0,0 +1,24 @@ +.bdsignore.all +Makefile +license.txt +Doxyfile +LICENSE +README +ChangeLog +TODO +Debug +Release +Intel_SSA +My Inspector XE Results - pcm +.gitignore +.gitattributes +.*\.txt +.*\.pdf +.*\.docx +.*\.rtf +.*\.vcproj +.*\.xcodeproj +.*\.sh +.*\.htm +.*\.bat +.*\.strings diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8eb4448 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +cpucounters.h export-subst diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba7a906 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +*.o +*.x +*.d +*.xml +/.project +*.XML +*.htm +*.html +*.dll +*.txt +*.patch +*.orig +*.vcxproj* +*.out +*.log +*.sys +*.vxd +*.exe +*.tgz diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..24c3ed5 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1237 @@ +# Doxyfile 1.4.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "Intel(r) Performance Counter Monitor" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = *.h *.cpp *.c + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/KSysGuard HOWTO.docx b/KSysGuard HOWTO.docx new file mode 100644 index 0000000000000000000000000000000000000000..8c843fadbed59a45b694cc48d84d814f8737bf69 GIT binary patch literal 345803 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 z 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%0D!4~mLcdH2gX{NsR6FEmYZf#5@c5V_Ek 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

AXL|tuB4i7CD%{&-5589 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 !kE`7*wiH}ft&!ngA6 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%0Dr{74P0}jy17XMbMYy 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 ZsV#&B!%RrT;$U|`^L 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 +#include +#include +#include +#include "PcmMsr/UserKernelShared.h" + +kern_return_t openMSRClient(io_connect_t connect); +kern_return_t closeMSRClient(io_connect_t connect); +kern_return_t readMSR(io_connect_t connect, pcm_msr_data_t* idata, size_t* idata_size, pcm_msr_data_t* odata, size_t* odata_size); +kern_return_t writeMSR(io_connect_t connect, pcm_msr_data_t* data, size_t* data_size); +kern_return_t getTopologyInfo(io_connect_t connect, topologyEntry* data, size_t* data_size); +kern_return_t getNumClients(io_connect_t connect, uint32_t* num_insts); +kern_return_t incrementNumClients(io_connect_t connect, uint32_t* num_insts); +kern_return_t decrementNumClients(io_connect_t connect, uint32_t* num_insts); \ No newline at end of file diff --git a/MacMSRDriver/MSRAccessor.cpp b/MacMSRDriver/MSRAccessor.cpp new file mode 100644 index 0000000..c9de3b5 --- /dev/null +++ b/MacMSRDriver/MSRAccessor.cpp @@ -0,0 +1,111 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#include "MSRAccessor.h" +#include +MSRAccessor::MSRAccessor(){ + service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kPcmMsrDriverClassName)); + openConnection(); +} + +int32_t MSRAccessor::buildTopology(uint32_t num_cores ,void* pTopos){ + topologyEntry *entries = (topologyEntry*)pTopos; + size_t size = sizeof(topologyEntry)*num_cores; + kern_return_t ret = getTopologyInfo(connect, entries, &size); + return (ret == KERN_SUCCESS) ? 0 : -1; +} + +int32_t MSRAccessor::read(uint32_t core_num, uint64_t msr_num, uint64_t * value){ + pcm_msr_data_t idatas, odatas; + size_t size = sizeof(pcm_msr_data_t); + idatas.msr_num = msr_num; + idatas.cpu_num = core_num; + kern_return_t ret = readMSR(connect, &idatas, &size, &odatas, &size); + if(ret == KERN_SUCCESS) + { + *value = odatas.value; + return sizeof(uint64_t); + } + else{ + return -1; + } +} + +int32_t MSRAccessor::write(uint32_t core_num, uint64_t msr_num, uint64_t value){ + pcm_msr_data_t idatas; + size_t size = sizeof(pcm_msr_data_t); + idatas.value = value; + idatas.msr_num = msr_num; + idatas.cpu_num = core_num; + kern_return_t ret = writeMSR(connect, &idatas, &size); + if(ret == KERN_SUCCESS) + { + return sizeof(uint64_t); + } + else + { + return -1; + } +} + +uint32_t MSRAccessor::getNumInstances(){ + uint32_t num_instances; + getNumClients(connect, &num_instances); + return num_instances; +} + +uint32_t MSRAccessor::incrementNumInstances(){ + uint32_t num_instances; + incrementNumClients(connect, &num_instances); + return num_instances; +} + +uint32_t MSRAccessor::decrementNumInstances(){ + uint32_t num_instances; + decrementNumClients(connect, &num_instances); + return num_instances; +} + +MSRAccessor::~MSRAccessor(){ + closeConnection(); +} + +kern_return_t MSRAccessor::openConnection(){ + kern_return_t kernResult = IOServiceOpen(service, mach_task_self(), 0, &connect); + + if (kernResult != KERN_SUCCESS) { + fprintf(stderr, "IOServiceOpen returned 0x%08x\n", kernResult); + } + else { + kernResult = openMSRClient(connect); + + if (kernResult != KERN_SUCCESS) { + fprintf(stderr, "openClient returned 0x%08x.\n\n", kernResult); + } + } + + return kernResult; +} + +void MSRAccessor::closeConnection(){ + kern_return_t kernResult = closeMSRClient(connect); + if (kernResult != KERN_SUCCESS) { + fprintf(stderr, "closeClient returned 0x%08x.\n\n", kernResult); + } + + kernResult = IOServiceClose(connect); + if (kernResult != KERN_SUCCESS) { + fprintf(stderr, "IOServiceClose returned 0x%08x\n\n", kernResult); + } +} \ No newline at end of file diff --git a/MacMSRDriver/MSRAccessor.h b/MacMSRDriver/MSRAccessor.h new file mode 100644 index 0000000..3b5897d --- /dev/null +++ b/MacMSRDriver/MSRAccessor.h @@ -0,0 +1,36 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#include +extern "C" { +#include "DriverInterface.h" +} + +class MSRAccessor{ +private: + io_service_t service; + io_connect_t connect; + kern_return_t openConnection(); + void closeConnection(); +public: + MSRAccessor(); + int32_t read(uint32_t cpu_num,uint64_t msr_num, uint64_t * value); + int32_t write(uint32_t cpu_num, uint64_t msr_num, uint64_t value); + int32_t buildTopology(uint32_t num_cores, void*); + + uint32_t getNumInstances(); + uint32_t incrementNumInstances(); + uint32_t decrementNumInstances(); + ~MSRAccessor(); +}; diff --git a/MacMSRDriver/MSRAccessorPublic.h b/MacMSRDriver/MSRAccessorPublic.h new file mode 100644 index 0000000..d82f1cc --- /dev/null +++ b/MacMSRDriver/MSRAccessorPublic.h @@ -0,0 +1,35 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#ifndef MSRACCESSOR_HEADER +#define MSRACCESSOR_HEADER + +#include +#include +class MSRAccessor{ +private: + uint64_t num_cores; + void closeConnection(); +public: + MSRAccessor(); + int32_t read(uint32_t cpu_num, uint64_t msr_num, uint64_t * value); + int32_t write(uint32_t cpu_num, uint64_t msr_num, uint64_t value); + int32_t buildTopology(uint32_t num_cores, void*); + uint32_t getNumInstances(); + uint32_t incrementNumInstances(); + uint32_t decrementNumInstances(); + ~MSRAccessor(); +}; + +#endif diff --git a/MacMSRDriver/MSRKernel.h b/MacMSRDriver/MSRKernel.h new file mode 100644 index 0000000..353f99e --- /dev/null +++ b/MacMSRDriver/MSRKernel.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#define PcmMsrDriverClassName com_intel_driver_PcmMsr +#define kPcmMsrDriverClassName "com_intel_driver_PcmMsr" +#ifndef MSR_KERNEL_SHARED +#define MSR_KERNEL_SHARED +#include +typedef struct { + uint64_t value; + uint32_t cpu_num; + uint32_t msr_num; +} pcm_msr_data_t; + +/* +// The topologyEntry struct that is used by PCM +typedef struct{ + uint32_t os_id; + uint32_t socket; + uint32_t core_id; +} topologyEntry; + +// A kernel version of the topology entry structure. It has +// an extra unused int to explicitly align the struct on a 64bit +// boundary, preventing the compiler from adding extra padding. +enum { + kOpenDriver, + kCloseDriver, + kReadMSR, + kWriteMSR, + kBuildTopology, + kGetNumInstances, + kIncrementNumInstances, + kDecrementNumInstances, + kNumberOfMethods +}; +*/ +#endif diff --git a/MacMSRDriver/Makefile b/MacMSRDriver/Makefile new file mode 100644 index 0000000..728ff6a --- /dev/null +++ b/MacMSRDriver/Makefile @@ -0,0 +1,25 @@ +# +# Copyright (c) 2012 Intel Corporation +# written by Austen Ott +# +# Build and install the PcmMsr kext and dynamically linked library. + +kext: + xcodebuild -configuration Release -target PcmMsrDriver clean build + +library: + xcodebuild -configuration Release -target PcmMsrLibrary clean build + +install: kext library + sudo sh ./kextload.sh + sudo cp build/Release/libPcmMsr.dylib /usr/lib/ + sudo cp MSRAccessorPublic.h /usr/include/MSRAccessor.h + sudo cp MSRKernel.h /usr/include/MSRKernel.h + sudo cp PCIDriverInterface.h /usr/include/PCIDriverInterface.h + +uninstall: + sudo sh ./kextunload.sh + sudo rm /usr/include/MSRKernel.h + sudo rm /usr/include/MSRAccessor.h + sudo rm /usr/lib/libPcmMsr.dylib + sudo rm /usr/include/PCIDriverInterface.h diff --git a/MacMSRDriver/PCIDriverInterface.cpp b/MacMSRDriver/PCIDriverInterface.cpp new file mode 100644 index 0000000..8334f31 --- /dev/null +++ b/MacMSRDriver/PCIDriverInterface.cpp @@ -0,0 +1,235 @@ +/* + Copyright (c) 2013, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Patrick Konsor +// + +#include +#include +#include "PCIDriverInterface.h" +#include +#include "PcmMsr/UserKernelShared.h" + +io_connect_t PCIDriver_connect = 0; +std::map PCIDriver_mmap; + +// setupDriver +#ifdef __cplusplus +extern "C" +#endif +int PCIDriver_setupDriver() +{ + kern_return_t kern_result; + io_iterator_t iterator; + bool driverFound = false; + io_service_t local_driver_service; + + // get services + kern_result = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(kPcmMsrDriverClassName), &iterator); + if (kern_result != KERN_SUCCESS) { + fprintf(stderr, "[error] IOServiceGetMatchingServices returned 0x%08x\n", kern_result); + return kern_result; + } + + // find service + while ((local_driver_service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { + driverFound = true; + break; + } + if (driverFound == false) { + fprintf(stderr, "[error] No matching drivers found \"%s\".\n", kPcmMsrDriverClassName); + return KERN_FAILURE; + } + IOObjectRelease(iterator); + + // connect to service + kern_result = IOServiceOpen(local_driver_service, mach_task_self(), 0, &PCIDriver_connect); + if (kern_result != KERN_SUCCESS) { + fprintf(stderr, "[error] IOServiceOpen returned 0x%08x\n", kern_result); + return kern_result; + } + + return KERN_SUCCESS; +} + + +// read32 +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_read32(uint32_t addr, uint32_t* val) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + + uint64_t input[] = { (uint64_t)addr }; + uint64_t val_ = 0; + uint32_t outputCnt = 1; + kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kRead, input, 1, &val_, &outputCnt); + *val = (uint32_t)val_; + return result; +} + + +// read64 +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_read64(uint32_t addr, uint64_t* val) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + + kern_return_t result; + uint64_t input[] = { (uint64_t)addr }; + uint64_t lo = 0; + uint64_t hi = 0; + uint32_t outputCnt = 1; + result = IOConnectCallScalarMethod(PCIDriver_connect, kRead, input, 1, &lo, &outputCnt); + input[0] = (uint64_t)addr + 4; + result |= IOConnectCallScalarMethod(PCIDriver_connect, kRead, input, 1, &hi, &outputCnt); + *val = (hi << 32) | lo; + return result; +} + + +// write32 +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_write32(uint32_t addr, uint32_t val) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + + uint64_t input[] = { (uint64_t)addr, (uint64_t)val }; + return IOConnectCallScalarMethod(PCIDriver_connect, kWrite, input, 2, NULL, 0); +} + + +// write64 +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_write64(uint32_t addr, uint64_t val) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + + kern_return_t result; + uint64_t input[] = { (uint64_t)addr, val & 0xffffffff }; + result = IOConnectCallScalarMethod(PCIDriver_connect, kWrite, input, 2, NULL, 0); + input[0] = (uint64_t)addr + 4; + input[1] = val >> 32; + result |= IOConnectCallScalarMethod(PCIDriver_connect, kWrite, input, 2, NULL, 0); + return result; +} + +// mapMemory +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_mapMemory(uint32_t address, uint8_t** virtual_address) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + + uint64_t input[] = { (uint64_t)address }; + uint64_t output[2]; + uint32_t outputCnt = 2; + kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kMapMemory, input, 1, output, &outputCnt); + PCIDriver_mmap[(uint8_t*)output[1]] = (void*)output[0]; + *virtual_address = (uint8_t*)output[1]; + return result; +} + + +// unmapMemory +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_unmapMemory(uint8_t* virtual_address) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + + void* memory_map = PCIDriver_mmap[virtual_address]; + if (memory_map != NULL) { + uint64_t input[] = { (uint64_t)memory_map }; + kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kUnmapMemory, input, 1, NULL, 0); + PCIDriver_mmap.erase(virtual_address); // remove from map + return result; + } else { + return KERN_INVALID_ADDRESS; + } +} + +// readMemory32 +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_readMemory32(uint8_t* address, uint32_t* val) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + uint64_t input[] = { (uint64_t)address }; + uint64_t val_ = 0; + uint32_t outputCnt = 1; + kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kReadMemory, input, 1, &val_, &outputCnt); + *val = (uint32_t)val_; + return result; +} + + +// readMemory64 +#ifdef __cplusplus +extern "C" +#endif +uint32_t PCIDriver_readMemory64(uint8_t* address, uint64_t* val) +{ + if (!PCIDriver_connect) { + if (PCIDriver_setupDriver() != KERN_SUCCESS) { + return KERN_FAILURE; + } + } + kern_return_t result; + uint64_t input[] = { (uint64_t)address }; + uint64_t lo = 0; + uint64_t hi = 0; + uint32_t outputCnt = 1; + result = IOConnectCallScalarMethod(PCIDriver_connect, kReadMemory, input, 1, &lo, &outputCnt); + input[0] = (uint64_t)address + 4; + result |= IOConnectCallScalarMethod(PCIDriver_connect, kReadMemory, input, 1, &hi, &outputCnt); + *val = (hi << 32) | lo; + return result; +} diff --git a/MacMSRDriver/PCIDriverInterface.h b/MacMSRDriver/PCIDriverInterface.h new file mode 100644 index 0000000..b66e1ad --- /dev/null +++ b/MacMSRDriver/PCIDriverInterface.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2013, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Patrick Konsor +// + +#ifndef pci_driver_driverinterface_h +#define pci_driver_driverinterface_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define PCI_ENABLE 0x80000000 +#define FORM_PCI_ADDR(bus,dev,fun,off) (((PCI_ENABLE)) | \ + ((bus & 0xFF) << 16) | \ + ((dev & 0x1F) << 11) | \ + ((fun & 0x07) << 8) | \ + ((off & 0xFF) << 0)) + +uint32_t PCIDriver_read32(uint32_t addr, uint32_t* val); +uint32_t PCIDriver_read64(uint32_t addr, uint64_t* val); +uint32_t PCIDriver_write32(uint32_t addr, uint32_t val); +uint32_t PCIDriver_write64(uint32_t addr, uint64_t val); +uint32_t PCIDriver_mapMemory(uint32_t address, uint8_t** virtual_address); +uint32_t PCIDriver_unmapMemory(uint8_t* virtual_address); +uint32_t PCIDriver_readMemory32(uint8_t* address, uint32_t* val); +uint32_t PCIDriver_readMemory64(uint8_t* address, uint64_t* val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/MacMSRDriver/PcmMsr.xcodeproj/project.pbxproj b/MacMSRDriver/PcmMsr.xcodeproj/project.pbxproj new file mode 100644 index 0000000..753043f --- /dev/null +++ b/MacMSRDriver/PcmMsr.xcodeproj/project.pbxproj @@ -0,0 +1,438 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 81ADBF0A156EBD73006D9B47 /* PcmMsrClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81ADBF09156EBD73006D9B47 /* PcmMsrClient.cpp */; }; + 81ADBF1A156EEDB9006D9B47 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81ADBF0B156EDBA1006D9B47 /* IOKit.framework */; }; + 81DEAF6315703531005E8EC6 /* MSRAccessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81ADBF1C156EFF69006D9B47 /* MSRAccessor.cpp */; }; + 81DEAF6615703946005E8EC6 /* DriverInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 81DEAF6515703946005E8EC6 /* DriverInterface.c */; }; + 81DEAF67157039F6005E8EC6 /* DriverInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 81ADBF17156EECDA006D9B47 /* DriverInterface.h */; }; + 81DEAF68157039FB005E8EC6 /* MSRAccessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 81ADBF1B156EFF56006D9B47 /* MSRAccessor.h */; }; + 81F91BC6156D9BF8007DD788 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 81F91BC4156D9BF8007DD788 /* InfoPlist.strings */; }; + 81F91BC9156D9BF8007DD788 /* PcmMsr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81F91BC8156D9BF8007DD788 /* PcmMsr.cpp */; }; + 895805FC1760E6E5006ED117 /* PCIDriverInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 895805FA1760E6E5006ED117 /* PCIDriverInterface.cpp */; }; + 895805FD1760E6E5006ED117 /* PCIDriverInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 895805FB1760E6E5006ED117 /* PCIDriverInterface.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 816FC6A5158296D200D9DEB4 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + fileType = pattern.proxy; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 816FC6A31582965F00D9DEB4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/local/lib; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 81ADBF08156EBD65006D9B47 /* PcmMsrClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PcmMsrClient.h; sourceTree = ""; }; + 81ADBF09156EBD73006D9B47 /* PcmMsrClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PcmMsrClient.cpp; sourceTree = ""; }; + 81ADBF0B156EDBA1006D9B47 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 81ADBF0D156EDD11006D9B47 /* UserKernelShared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = UserKernelShared.h; path = PcmMsr/UserKernelShared.h; sourceTree = ""; }; + 81ADBF12156EEB93006D9B47 /* libPcmMsr.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libPcmMsr.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 81ADBF17156EECDA006D9B47 /* DriverInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DriverInterface.h; sourceTree = ""; }; + 81ADBF1B156EFF56006D9B47 /* MSRAccessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSRAccessor.h; sourceTree = ""; }; + 81ADBF1C156EFF69006D9B47 /* MSRAccessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MSRAccessor.cpp; sourceTree = ""; }; + 81DEAF6515703946005E8EC6 /* DriverInterface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DriverInterface.c; sourceTree = ""; }; + 81F91BBC156D9BF8007DD788 /* PcmMsrDriver.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PcmMsrDriver.kext; sourceTree = BUILT_PRODUCTS_DIR; }; + 81F91BC3156D9BF8007DD788 /* PcmMsr-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PcmMsr-Info.plist"; sourceTree = ""; }; + 81F91BC5156D9BF8007DD788 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 81F91BC7156D9BF8007DD788 /* PcmMsr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PcmMsr.h; sourceTree = ""; }; + 81F91BC8156D9BF8007DD788 /* PcmMsr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PcmMsr.cpp; sourceTree = ""; }; + 81F91BCA156D9BF8007DD788 /* PcmMsr-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PcmMsr-Prefix.pch"; sourceTree = ""; }; + 895805FA1760E6E5006ED117 /* PCIDriverInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCIDriverInterface.cpp; sourceTree = ""; }; + 895805FB1760E6E5006ED117 /* PCIDriverInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCIDriverInterface.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 81ADBF0F156EEB93006D9B47 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81ADBF1A156EEDB9006D9B47 /* IOKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81F91BB7156D9BF8007DD788 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 81ADBF16156EECC6006D9B47 /* PcmMsrLibrary */ = { + isa = PBXGroup; + children = ( + 81ADBF17156EECDA006D9B47 /* DriverInterface.h */, + 81DEAF6515703946005E8EC6 /* DriverInterface.c */, + 81ADBF1B156EFF56006D9B47 /* MSRAccessor.h */, + 81ADBF1C156EFF69006D9B47 /* MSRAccessor.cpp */, + 895805FA1760E6E5006ED117 /* PCIDriverInterface.cpp */, + 895805FB1760E6E5006ED117 /* PCIDriverInterface.h */, + 81F91BC1156D9BF8007DD788 /* PcmMsr */, + ); + name = PcmMsrLibrary; + sourceTree = ""; + }; + 81F91BAF156D9BF8007DD788 = { + isa = PBXGroup; + children = ( + 81ADBF0D156EDD11006D9B47 /* UserKernelShared.h */, + 81ADBF16156EECC6006D9B47 /* PcmMsrLibrary */, + 81F91BBE156D9BF8007DD788 /* Frameworks */, + 81F91BBD156D9BF8007DD788 /* Products */, + ); + sourceTree = ""; + }; + 81F91BBD156D9BF8007DD788 /* Products */ = { + isa = PBXGroup; + children = ( + 81F91BBC156D9BF8007DD788 /* PcmMsrDriver.kext */, + 81ADBF12156EEB93006D9B47 /* libPcmMsr.dylib */, + ); + name = Products; + sourceTree = ""; + }; + 81F91BBE156D9BF8007DD788 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 81ADBF0B156EDBA1006D9B47 /* IOKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 81F91BC1156D9BF8007DD788 /* PcmMsr */ = { + isa = PBXGroup; + children = ( + 81F91BC7156D9BF8007DD788 /* PcmMsr.h */, + 81F91BC8156D9BF8007DD788 /* PcmMsr.cpp */, + 81F91BC2156D9BF8007DD788 /* Supporting Files */, + 81ADBF08156EBD65006D9B47 /* PcmMsrClient.h */, + 81ADBF09156EBD73006D9B47 /* PcmMsrClient.cpp */, + ); + path = PcmMsr; + sourceTree = ""; + }; + 81F91BC2156D9BF8007DD788 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 81F91BC3156D9BF8007DD788 /* PcmMsr-Info.plist */, + 81F91BC4156D9BF8007DD788 /* InfoPlist.strings */, + 81F91BCA156D9BF8007DD788 /* PcmMsr-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 81ADBF10156EEB93006D9B47 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 81DEAF67157039F6005E8EC6 /* DriverInterface.h in Headers */, + 81DEAF68157039FB005E8EC6 /* MSRAccessor.h in Headers */, + 895805FD1760E6E5006ED117 /* PCIDriverInterface.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81F91BB8156D9BF8007DD788 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 81ADBF11156EEB93006D9B47 /* PcmMsrLibrary */ = { + isa = PBXNativeTarget; + buildConfigurationList = 81ADBF13156EEB93006D9B47 /* Build configuration list for PBXNativeTarget "PcmMsrLibrary" */; + buildPhases = ( + 81ADBF0E156EEB93006D9B47 /* Sources */, + 81ADBF0F156EEB93006D9B47 /* Frameworks */, + 81ADBF10156EEB93006D9B47 /* Headers */, + 816FC6A31582965F00D9DEB4 /* CopyFiles */, + ); + buildRules = ( + 816FC6A5158296D200D9DEB4 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = PcmMsrLibrary; + productName = PcmMsrLibrary; + productReference = 81ADBF12156EEB93006D9B47 /* libPcmMsr.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + 81F91BBB156D9BF8007DD788 /* PcmMsrDriver */ = { + isa = PBXNativeTarget; + buildConfigurationList = 81F91BCD156D9BF8007DD788 /* Build configuration list for PBXNativeTarget "PcmMsrDriver" */; + buildPhases = ( + 81F91BB6156D9BF8007DD788 /* Sources */, + 81F91BB7156D9BF8007DD788 /* Frameworks */, + 81F91BB8156D9BF8007DD788 /* Headers */, + 81F91BB9156D9BF8007DD788 /* Resources */, + 81F91BBA156D9BF8007DD788 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PcmMsrDriver; + productName = PcmMsr; + productReference = 81F91BBC156D9BF8007DD788 /* PcmMsrDriver.kext */; + productType = "com.apple.product-type.kernel-extension"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 81F91BB1156D9BF8007DD788 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0430; + }; + buildConfigurationList = 81F91BB4156D9BF8007DD788 /* Build configuration list for PBXProject "PcmMsr" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 81F91BAF156D9BF8007DD788; + productRefGroup = 81F91BBD156D9BF8007DD788 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 81F91BBB156D9BF8007DD788 /* PcmMsrDriver */, + 81ADBF11156EEB93006D9B47 /* PcmMsrLibrary */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 81F91BB9156D9BF8007DD788 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81F91BC6156D9BF8007DD788 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 81F91BBA156D9BF8007DD788 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 81ADBF0E156EEB93006D9B47 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81DEAF6315703531005E8EC6 /* MSRAccessor.cpp in Sources */, + 81DEAF6615703946005E8EC6 /* DriverInterface.c in Sources */, + 895805FC1760E6E5006ED117 /* PCIDriverInterface.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81F91BB6156D9BF8007DD788 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81F91BC9156D9BF8007DD788 /* PcmMsr.cpp in Sources */, + 81ADBF0A156EBD73006D9B47 /* PcmMsrClient.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 81F91BC4156D9BF8007DD788 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 81F91BC5156D9BF8007DD788 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 81ADBF14156EEB93006D9B47 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEPLOYMENT_LOCATION = NO; + EXECUTABLE_PREFIX = lib; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/lib; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = PcmMsr; + }; + name = Debug; + }; + 81ADBF15156EEB93006D9B47 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEPLOYMENT_LOCATION = NO; + EXECUTABLE_PREFIX = lib; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/lib; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = PcmMsr; + }; + name = Release; + }; + 81F91BCB156D9BF8007DD788 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = ""; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 81F91BCC156D9BF8007DD788 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = ""; + SDKROOT = macosx; + }; + name = Release; + }; + 81F91BCE156D9BF8007DD788 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CURRENT_PROJECT_VERSION = 1.0.0d1; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "PcmMsr/PcmMsr-Prefix.pch"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + "HEADER_SEARCH_PATHS[arch=*]" = /usr/include; + INFOPLIST_FILE = "PcmMsr/PcmMsr-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MODULE_NAME = com.intel.driver.PcmMsrDriver; + MODULE_VERSION = 1.0.0d1; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = PcmMsrDriver; + SDKROOT = macosx10.8; + WRAPPER_EXTENSION = kext; + }; + name = Debug; + }; + 81F91BCF156D9BF8007DD788 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CURRENT_PROJECT_VERSION = 1.0.0d1; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "PcmMsr/PcmMsr-Prefix.pch"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + "HEADER_SEARCH_PATHS[arch=*]" = /usr/include; + INFOPLIST_FILE = "PcmMsr/PcmMsr-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MODULE_NAME = com.intel.driver.PcmMsrDriver; + MODULE_VERSION = 1.0.0d1; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = PcmMsrDriver; + SDKROOT = macosx10.8; + WRAPPER_EXTENSION = kext; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 81ADBF13156EEB93006D9B47 /* Build configuration list for PBXNativeTarget "PcmMsrLibrary" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 81ADBF14156EEB93006D9B47 /* Debug */, + 81ADBF15156EEB93006D9B47 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 81F91BB4156D9BF8007DD788 /* Build configuration list for PBXProject "PcmMsr" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 81F91BCB156D9BF8007DD788 /* Debug */, + 81F91BCC156D9BF8007DD788 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 81F91BCD156D9BF8007DD788 /* Build configuration list for PBXNativeTarget "PcmMsrDriver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 81F91BCE156D9BF8007DD788 /* Debug */, + 81F91BCF156D9BF8007DD788 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 81F91BB1156D9BF8007DD788 /* Project object */; +} diff --git a/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..c39911a --- /dev/null +++ b/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/aiott.xcuserdatad/UserInterfaceState.xcuserstate b/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/aiott.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..28016b08e9971571f2b16592a82e0cfbdf52d2a9 GIT binary patch literal 92777 zcmdR12VfJ&(!Sk2-APv@xyZDTn9x$drnis;S8#BP8$F7#1sH70v24JU38x6B3a1ID3ug;g2%Ci~g)PEW!qvhx!nMM6!dBsK z;X∾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 X!8>mJndm(-{!H!u_x=B~?u`8f{kY~i literal 0 HcmV?d00001 diff --git a/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist new file mode 100644 index 0000000..d6f81fa --- /dev/null +++ b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsr.xcscheme b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsr.xcscheme new file mode 100644 index 0000000..a36963e --- /dev/null +++ b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsr.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsrLibrary.xcscheme b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsrLibrary.xcscheme new file mode 100644 index 0000000..2a910c4 --- /dev/null +++ b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsrLibrary.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/xcschememanagement.plist b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..be6a1cf --- /dev/null +++ b/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,37 @@ + + + + + SchemeUserState + + PcmMsr.xcscheme + + orderHint + 0 + + PcmMsrLibrary.xcscheme + + orderHint + 1 + + + SuppressBuildableAutocreation + + 81ADBF11156EEB93006D9B47 + + primary + + + 81DEAF55157008B7005E8EC6 + + primary + + + 81F91BBB156D9BF8007DD788 + + primary + + + + + diff --git a/MacMSRDriver/PcmMsr/PcmMsr-Info.plist b/MacMSRDriver/PcmMsr/PcmMsr-Info.plist new file mode 100644 index 0000000..d8c8408 --- /dev/null +++ b/MacMSRDriver/PcmMsr/PcmMsr-Info.plist @@ -0,0 +1,57 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + PcmMsrDriver + CFBundleIdentifier + com.intel.driver.PcmMsr + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + PcmMsrDriver + CFBundlePackageType + KEXT + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + IOKitPersonalities + + PcmMsrClient + + CFBundleIdentifier + com.intel.driver.PcmMsr + IOClass + com_intel_driver_PcmMsr + IOMatchCategory + com_intel_driver_PcmMsr + IOProbeScore + 1000 + IOProviderClass + IOResources + IOResourceMatch + IOKit + IOUserClientClass + com_intel_driver_PcmMsrClient + + + OSBundleLibraries + + com.apple.kpi.bsd + 10.8 + com.apple.kpi.mach + 10.8 + com.apple.kpi.unsupported + 10.8 + com.apple.kpi.iokit + 10.8 + com.apple.kpi.libkern + 10.8 + + + diff --git a/MacMSRDriver/PcmMsr/PcmMsr-Prefix.pch b/MacMSRDriver/PcmMsr/PcmMsr-Prefix.pch new file mode 100644 index 0000000..9e1e7e4 --- /dev/null +++ b/MacMSRDriver/PcmMsr/PcmMsr-Prefix.pch @@ -0,0 +1,4 @@ +// +// Prefix header for all source files of the 'PcmMsr' target in the 'PcmMsr' project +// + diff --git a/MacMSRDriver/PcmMsr/PcmMsr.cpp b/MacMSRDriver/PcmMsr/PcmMsr.cpp new file mode 100644 index 0000000..2d36c99 --- /dev/null +++ b/MacMSRDriver/PcmMsr/PcmMsr.cpp @@ -0,0 +1,307 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#include +#include +#include "PcmMsr.h" + +PcmMsrDriverClassName *g_pci_driver = NULL; + +#define wrmsr(msr,lo,hi) \ +asm volatile ("wrmsr" : : "c" (msr), "a" (lo), "d" (hi)) +#define rdmsr(msr,lo,hi) \ +asm volatile ("\trdmsr\n" : "=a" (lo), "=d" (hi) : "c" (msr)) +#define cpuid(func1, func2, a, b, c, d) \ +asm volatile ("cpuid" : "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (func1), "c" (func2)); + +extern "C" { + extern void mp_rendezvous_no_intrs(void (*func)(void *), + void *arg); + extern int cpu_number(void); +} + +inline uint64_t RDMSR(uint32_t msr) +{ + uint64_t value; + uint32_t low, hi; + rdmsr(msr, low, hi); + value = ((uint64_t) hi << 32) | low; + return value; +} + +inline void WRMSR(uint32_t msr, uint64_t value) +{ + uint32_t low, hi; + low = (uint32_t)value; + hi = (uint32_t) (value >> 32); + wrmsr(msr, low, hi); +} + +void cpuReadMSR(void* pIData){ + pcm_msr_data_t* data = (pcm_msr_data_t*)pIData; + volatile uint cpu = cpu_number(); + if(data->cpu_num == cpu) + { + data->value = RDMSR(data->msr_num); + } +} + +void cpuWriteMSR(void* pIDatas){ + pcm_msr_data_t* idatas = (pcm_msr_data_t*)pIDatas; + volatile uint cpu = cpu_number(); + if(idatas->cpu_num == cpu) + { + WRMSR(idatas->msr_num, idatas->value); + } +} + +void cpuGetTopoData(void* pTopos){ + kTopologyEntry* entries = (kTopologyEntry*)pTopos; + volatile uint cpu = cpu_number(); + int info[4]; + entries[cpu].os_id = cpu; + cpuid(0xB, 1, info[0], info[1], info[2], info[3]); + entries[cpu].socket = info[3] >> info[0] & 0xF; + + cpuid(0xB, 0, info[0], info[1], info[2], info[3]); + entries[cpu].core_id = info[3] >> info[0] & 0xF; +} + +OSDefineMetaClassAndStructors(com_intel_driver_PcmMsr, IOService) + +#define super IOService + +bool PcmMsrDriverClassName::start(IOService* provider){ + bool success; + success = super::start(provider); + + if (!g_pci_driver) { + g_pci_driver = this; + } + + if (success) { + registerService(); + } + + return success; +} +uint32_t PcmMsrDriverClassName::getNumCores() +{ + size_t size; + char* pParam; + uint32_t ret = 0; + if(!sysctlbyname("hw.logicalcpu", NULL, &size, NULL, 0)) + { + if(NULL != (pParam = (char*)IOMalloc(size))) + { + if(!sysctlbyname("hw.logicalcpu", (void*)pParam, &size, NULL, 0)) + { + if(sizeof(int) == size) + ret = *(int*)pParam; + else if(sizeof(long) == size) + ret = *(long*)pParam; + else if(sizeof(long long) == size) + ret = *(long long*)pParam; + else + ret = *(int*)pParam; + } + IOFree(pParam, size); + } + } + return ret; +} + +bool PcmMsrDriverClassName::init(OSDictionary *dict) +{ + num_cores = getNumCores(); + bool result = super::init(dict); + topologies = 0; + if(result && num_cores != 0) + { + topologies = (kTopologyEntry*)IOMallocAligned(sizeof(kTopologyEntry)*num_cores, 128); + } + return (result && topologies && num_cores != 0); +} + +void PcmMsrDriverClassName::free() +{ + if(topologies) + IOFreeAligned(topologies, sizeof(kTopologyEntry)*num_cores); + super::free(); +} + +// We override handleOpen, handleIsOpen, and handleClose to allow multiple clients to access the driver +// simultaneously. We always return true for these because we don't care who is accessing and we +// don't know how many people will be accessing it. +bool PcmMsrDriverClassName::handleOpen(IOService * forClient, IOOptionBits opts, void* args){ + return true; +} + +bool PcmMsrDriverClassName::handleIsOpen(const IOService* forClient) const{ + return true; +} + +void PcmMsrDriverClassName::handleClose(IOService* forClient, IOOptionBits opts){ +} + +IOReturn PcmMsrDriverClassName::readMSR(pcm_msr_data_t* idatas,pcm_msr_data_t* odatas){ + // All the msr_nums should be the same, so we just use the first one to pass to all cores + IOReturn ret = kIOReturnBadArgument; + if(idatas->cpu_num < num_cores) + { + mp_rendezvous_no_intrs(cpuReadMSR, (void*)idatas); + + odatas->cpu_num = idatas->cpu_num; + odatas->msr_num = idatas->msr_num; + odatas->value = idatas->value; + ret = kIOReturnSuccess; + } + else + { + IOLog("Tried to read from a core with id higher than max core id.\n"); + } + return ret; +} + +IOReturn PcmMsrDriverClassName::writeMSR(pcm_msr_data_t* idata){ + IOReturn ret = kIOReturnBadArgument; + if(idata->cpu_num < num_cores) + { + mp_rendezvous_no_intrs(cpuWriteMSR, (void*)idata); + + ret = kIOReturnSuccess; + } + else + { + IOLog("Tried to write to a core with id higher than max core id.\n"); + } + + return ret; +} + +IOReturn PcmMsrDriverClassName::buildTopology(topologyEntry* odata, uint32_t input_num_cores){ + mp_rendezvous_no_intrs(cpuGetTopoData, (void*)topologies); + for(uint32_t i = 0; i < num_cores && i < input_num_cores; i++) + { + odata[i].core_id = topologies[i].core_id; + odata[i].os_id = topologies[i].os_id; + odata[i].socket = topologies[i].socket; + } + return kIOReturnSuccess; +} + +IOReturn PcmMsrDriverClassName::getNumInstances(uint32_t* num_insts){ + *num_insts = num_clients; + return kIOReturnSuccess; +} + +IOReturn PcmMsrDriverClassName::incrementNumInstances(uint32_t* num_insts){ + *num_insts = ++num_clients; + return kIOReturnSuccess; +} + +IOReturn PcmMsrDriverClassName::decrementNumInstances(uint32_t* num_insts){ + *num_insts = --num_clients; + return kIOReturnSuccess; +} + +// read +uint32_t PcmMsrDriverClassName::read(uint32_t pci_address) +{ + uint32_t value = 0; + + __asm__("\t" + "movw $0xCF8,%%dx\n\t" + "andb $0xFC,%%al\n\t" + "outl %%eax,%%dx\n\t" + "movl $0xCFC,%%edx\n\t" + "in %%dx,%%eax\n" + : "=a"(value) + : "a"(pci_address) + : "%edx"); + + return value; +} + + +// write +void PcmMsrDriverClassName::write(uint32_t pci_address, uint32_t value) +{ + + __asm__("\t" + "movw $0xCF8,%%dx\n\t" + "andb $0xFC,%%al\n\t" + "outl %%eax,%%dx\n\t" + "movl $0xCFC,%%edx\n\t" + "movl %%ebx,%%eax\n\t" + "outl %%eax,%%dx\n" + : + : "a"(pci_address), "b"(value) + : "%edx"); +} + + +// mapMemory +void* PcmMsrDriverClassName::mapMemory (uint32_t address, UInt8 **virtual_address) +{ + PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); + + IOMemoryMap *memory_map = NULL; + IOMemoryDescriptor *memory_descriptor = NULL; + + memory_descriptor = IOMemoryDescriptor::withPhysicalAddress(address, + 4096, + kIODirectionInOut); + if (memory_descriptor) { + IOReturn ioErr = memory_descriptor->prepare(kIODirectionInOut); + if (ioErr == kIOReturnSuccess) { + memory_map = memory_descriptor->map(); + if (memory_map) { + if (virtual_address) { + *virtual_address = (UInt8*)memory_map->getVirtualAddress(); + } else { + IOLog("%s[%p]::%s() -- virtual_address is null\n", getName(), this, __FUNCTION__); + } + } else { + IOLog("%s[%p]::%s() -- IOMemoryDescriptor::map() failure\n", getName(), this, __FUNCTION__); + } + } + else { + memory_descriptor->release(); + IOLog("%s[%p]::%s() -- IOMemoryDescriptor::prepare() failure\n", getName(), this, __FUNCTION__); + } + } else { + IOLog("%s[%p]::%s() -- IOMemoryDescriptor::withPhysicalAddress() failure\n", getName(), this, __FUNCTION__); + } + + return (void*)memory_map; +} + + +// unmapMemory +void PcmMsrDriverClassName::unmapMemory (void *memory_map) +{ + PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); + + IOMemoryMap *m_map = (IOMemoryMap*)memory_map; + + if (m_map) { + m_map->getMemoryDescriptor()->complete(); + m_map->getMemoryDescriptor()->release(); + m_map->unmap(); + m_map->release(); + } + + return; +} diff --git a/MacMSRDriver/PcmMsr/PcmMsr.h b/MacMSRDriver/PcmMsr/PcmMsr.h new file mode 100644 index 0000000..84b153f --- /dev/null +++ b/MacMSRDriver/PcmMsr/PcmMsr.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#include +#include "UserKernelShared.h" + +class PcmMsrDriverClassName : public IOService +{ + OSDeclareDefaultStructors(com_intel_driver_PcmMsr) +public: + // IOService methods + virtual bool start(IOService* provider); + + virtual IOReturn writeMSR(pcm_msr_data_t* data); + virtual IOReturn readMSR(pcm_msr_data_t* idata,pcm_msr_data_t* odata); + virtual IOReturn buildTopology(topologyEntry* odata, uint32_t input_num_cores); + virtual bool init(OSDictionary *dict); + virtual void free(void); + virtual bool handleOpen(IOService* forClient, IOOptionBits opts, void* args); + virtual bool handleIsOpen(const IOService* forClient) const; + virtual void handleClose(IOService* forClient, IOOptionBits opts); + + virtual uint32_t getNumCores(); + + virtual IOReturn incrementNumInstances(uint32_t* num_instances); + virtual IOReturn decrementNumInstances(uint32_t* num_instances); + virtual IOReturn getNumInstances(uint32_t* num_instances); + + // PCI classes + static uint32_t read(uint32_t pci_address); + static void write(uint32_t pci_address, uint32_t value); + void* mapMemory(uint32_t address, UInt8 **virtual_address); + void unmapMemory(void* memory_map); + +private: + // number of providers currently using the driver + uint32_t num_clients = 0; + uint32_t num_cores; + kTopologyEntry *topologies; +}; + +#ifdef DEBUG +#define _DEBUG 1 +#else +#define _DEBUG 0 +#endif +#define PRINT_DEBUG if (_DEBUG) IOLog diff --git a/MacMSRDriver/PcmMsr/PcmMsrClient.cpp b/MacMSRDriver/PcmMsr/PcmMsrClient.cpp new file mode 100644 index 0000000..46bf8f0 --- /dev/null +++ b/MacMSRDriver/PcmMsr/PcmMsrClient.cpp @@ -0,0 +1,333 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#include +#include +#include +#include "PcmMsrClient.h" + +#define super IOUserClient + +OSDefineMetaClassAndStructors(com_intel_driver_PcmMsrClient, IOUserClient) + +const IOExternalMethodDispatch PcmMsrClientClassName::sMethods[kNumberOfMethods] = { + { (IOExternalMethodAction) &PcmMsrClientClassName::sOpenDriver, 0, 0, 0, 0}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sCloseDriver, 0, 0, 0, 0}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sReadMSR, 0, kIOUCVariableStructureSize, 0, kIOUCVariableStructureSize}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sWriteMSR, 0, kIOUCVariableStructureSize, 0, 0}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sBuildTopology, 0, 0, 0, kIOUCVariableStructureSize}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sGetNumInstances, 0, 0, 1, 0}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sIncrementNumInstances, 0, 0, 1, 0}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sDecrementNumInstances, 0, 0, 1, 0}, + { (IOExternalMethodAction) &PcmMsrClientClassName::sRead, 1, 0, 1, 0 }, + { (IOExternalMethodAction) &PcmMsrClientClassName::sWrite, 2, 0, 0, 0 }, + { (IOExternalMethodAction) &PcmMsrClientClassName::sMapMemory, 1, 0, 2, 0 }, + { (IOExternalMethodAction) &PcmMsrClientClassName::sUnmapMemory, 1, 0, 0, 0 }, + { (IOExternalMethodAction) &PcmMsrClientClassName::sReadMemory, 1, 0, 1, 0 } + +}; + +IOReturn PcmMsrClientClassName::externalMethod(uint32_t selector, IOExternalMethodArguments* args, IOExternalMethodDispatch* dispatch, OSObject* target, void* reference) +{ + if (selector < (uint32_t) kNumberOfMethods) { + dispatch = (IOExternalMethodDispatch *) &sMethods[selector]; + + if (!target) { + target = this; + } + } + + return super::externalMethod(selector, args, dispatch, target, reference); +} + +bool PcmMsrClientClassName::start(IOService* provider) +{ + bool result = false; + + fProvider = OSDynamicCast(PcmMsrDriverClassName, provider); + + if (fProvider != NULL) { + result = super::start(provider); + } + else + IOLog("PcmMsrClientClassName::start failed.\n"); + + return result; +} + +IOReturn PcmMsrClientClassName::clientClose(void) +{ + closeUserClient(); + + if (!terminate()) { + IOLog("PcmMsrClientClassName::clientClose failed.\n"); + } + + return kIOReturnSuccess; +} + +bool PcmMsrClientClassName::didTerminate(IOService* provider, IOOptionBits options, bool* defer) +{ + closeUserClient(); + *defer = false; + + return super::didTerminate(provider, options, defer); +} + + +IOReturn PcmMsrClientClassName::sOpenDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) +{ + return target->openUserClient(); +} + +IOReturn PcmMsrClientClassName::openUserClient(void) +{ + IOReturn result = kIOReturnSuccess; + + if (fProvider == NULL || isInactive()) { + result = kIOReturnNotAttached; + IOLog("%s::%s returned kIOReturnNotAttached.\n", getName(), __FUNCTION__); + } else if (!fProvider->open(this)) { + result = kIOReturnExclusiveAccess; + IOLog("%s::%s returned kIOReturnExclusiveAccess.\n", getName(), __FUNCTION__); + } + + return result; +} + +IOReturn PcmMsrClientClassName::checkActiveAndOpened (const char* memberFunction) +{ + if (fProvider == NULL || isInactive()) { + IOLog("%s::%s returned kIOReturnNotAttached.\n", getName(), memberFunction); + return kIOReturnNotAttached; + + } else if (!fProvider->isOpen(this)) { + IOLog("%s::%s returned kIOReturnNotOpen.\n", getName(), memberFunction); + return kIOReturnNotOpen; + } + return kIOReturnSuccess; +} + + +IOReturn PcmMsrClientClassName::sCloseDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) +{ + return target->closeUserClient(); +} + +IOReturn PcmMsrClientClassName::closeUserClient(void) +{ + IOReturn result = checkActiveAndOpened (__FUNCTION__); + + if (result == kIOReturnSuccess) + fProvider->close(this); + + return result; +} + +IOReturn PcmMsrClientClassName::sReadMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments){ + return target->readMSR((pcm_msr_data_t*) arguments->structureInput, (pcm_msr_data_t*) arguments->structureOutput); +} + +IOReturn PcmMsrClientClassName::readMSR(pcm_msr_data_t* idata, pcm_msr_data_t* odata) +{ + IOReturn result = checkActiveAndOpened (__FUNCTION__); + + if (result == kIOReturnSuccess) + result = fProvider->readMSR(idata, odata); + + return result; +} + +IOReturn PcmMsrClientClassName::sWriteMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments){ + return target -> writeMSR((pcm_msr_data_t*)arguments->structureInput); +} + +IOReturn PcmMsrClientClassName::writeMSR(pcm_msr_data_t* data) +{ + IOReturn result = checkActiveAndOpened (__FUNCTION__); + + if (result == kIOReturnSuccess) + result = fProvider->writeMSR(data); + + return result; +} + +IOReturn PcmMsrClientClassName::sBuildTopology(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ + return target -> buildTopology((topologyEntry*)args->structureOutput, args->structureOutputSize); +} + +IOReturn PcmMsrClientClassName::buildTopology(topologyEntry* data, size_t output_size) +{ + uint32_t num_cores = output_size / sizeof(topologyEntry); + IOReturn result = checkActiveAndOpened (__FUNCTION__); + + if (result == kIOReturnSuccess) + result = fProvider->buildTopology(data, num_cores); + + return result; +} + +IOReturn PcmMsrClientClassName::sGetNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ + return target->getNumInstances((uint32_t*)&args->scalarOutput[0]); +} +IOReturn PcmMsrClientClassName::getNumInstances(uint32_t* num_insts){ + return fProvider->getNumInstances(num_insts); +} + +IOReturn PcmMsrClientClassName::sIncrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ + return target->incrementNumInstances((uint32_t*)&args->scalarOutput[0]); +} +IOReturn PcmMsrClientClassName::incrementNumInstances(uint32_t* num_insts){ + return fProvider->incrementNumInstances(num_insts); +} + +IOReturn PcmMsrClientClassName::sDecrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ + return target->decrementNumInstances((uint32_t*)&args->scalarOutput[0]); +} +IOReturn PcmMsrClientClassName::decrementNumInstances(uint32_t* num_insts){ + return fProvider->decrementNumInstances(num_insts); +} + + + +extern PcmMsrDriverClassName* g_pci_driver; + +// read32 +IOReturn PcmMsrClientClassName::sRead(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { + return target->read(arguments->scalarInput, arguments->scalarInputCount, arguments->scalarOutput, arguments->scalarOutputCount); +} +IOReturn PcmMsrClientClassName::read(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize) +{ + PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); + + if (inputSize != 1) { + IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); + return kIOReturnBadArgument; + } + + uint32_t addr = (uint32_t)input[0]; + PRINT_DEBUG("addr: %x\n", addr); + + if (g_pci_driver) { + output[0] = g_pci_driver->read(addr); + } + IOLog("val: %llx\n", output[0]); + + return kIOReturnSuccess; +} + + +// write32 +IOReturn PcmMsrClientClassName::sWrite(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { + return target->write(arguments->scalarInput, arguments->scalarInputCount); +} +IOReturn PcmMsrClientClassName::write(const uint64_t* input, uint32_t inputSize) +{ + PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); + + if (inputSize != 2) { + IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); + return kIOReturnBadArgument; + } + + uint32_t addr = (uint32_t)input[0]; + uint32_t val = (uint32_t)input[1]; + PRINT_DEBUG("addr: %x, val: %x\n", addr, val); + + if (g_pci_driver) { + g_pci_driver->write(addr, val); + } + + return kIOReturnSuccess; +} + + +// mapMemory +IOReturn PcmMsrClientClassName::sMapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { + return target->mapMemory(arguments->scalarInput, arguments->scalarInputCount, arguments->scalarOutput, arguments->scalarOutputCount); +} +IOReturn PcmMsrClientClassName::mapMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize) +{ + PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); + + if (inputSize != 1) { + IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); + return kIOReturnBadArgument; + } + + uint32_t address = (uint32_t)input[0]; + PRINT_DEBUG("address: %x\n", address); + + if (g_pci_driver) { + uint8_t* virtual_address = NULL; + void* memory_map = g_pci_driver->mapMemory(address, (uint8_t**)&virtual_address); + output[0] = (uint64_t)memory_map; + output[1] = (uint64_t)virtual_address; + PRINT_DEBUG("memory_map: %p\n", memory_map); + PRINT_DEBUG("virtual_address: %p\n", virtual_address); + } + + return kIOReturnSuccess; +} + + +// unmapMemory +IOReturn PcmMsrClientClassName::sUnmapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { + return target->unmapMemory(arguments->scalarInput, arguments->scalarInputCount); +} +IOReturn PcmMsrClientClassName::unmapMemory(const uint64_t* input, uint32_t inputSize) +{ + PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); + + if (inputSize != 1) { + IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); + return kIOReturnBadArgument; + } + + void* memory_map = (void*)input[0]; + PRINT_DEBUG("memory_map: %p\n", memory_map); + + if (g_pci_driver) { + g_pci_driver->unmapMemory(memory_map); + } + + return kIOReturnSuccess; +} + + +// readMemory +IOReturn PcmMsrClientClassName::sReadMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { + return target->readMemory(arguments->scalarInput, arguments->scalarInputCount, arguments->scalarOutput, arguments->scalarOutputCount); +} +IOReturn PcmMsrClientClassName::readMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize) +{ + PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); + + if (inputSize != 1) { + IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); + return kIOReturnBadArgument; + } + + uint8_t* address = (uint8_t*)input[0]; + PRINT_DEBUG("address: %p\n", address); + + uint32_t val = 0; + if (g_pci_driver) { + val = *(uint32_t*)address; + } + output[0] = (uint64_t)val; + PRINT_DEBUG("val: %x\n", val); + + return kIOReturnSuccess; +} diff --git a/MacMSRDriver/PcmMsr/PcmMsrClient.h b/MacMSRDriver/PcmMsr/PcmMsrClient.h new file mode 100644 index 0000000..59a4d0b --- /dev/null +++ b/MacMSRDriver/PcmMsr/PcmMsrClient.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#include +#include +#include "PcmMsr.h" + +#define PcmMsrClientClassName com_intel_driver_PcmMsrClient + +class PcmMsrClientClassName : public IOUserClient +{ + OSDeclareDefaultStructors(com_intel_driver_PcmMsrClient) + +protected: + PcmMsrDriverClassName* fProvider; + static const IOExternalMethodDispatch sMethods[kNumberOfMethods]; + +public: + virtual bool start(IOService *provider); + + virtual IOReturn clientClose(void); + + virtual bool didTerminate(IOService* provider, IOOptionBits opts, bool* defer); + +protected: + IOReturn checkActiveAndOpened (const char* memberFunction); + + virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments* arguments, + IOExternalMethodDispatch* dispatch, OSObject* target, void* reference); + + static IOReturn sOpenDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn openUserClient(void); + + static IOReturn sCloseDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn closeUserClient(void); + + static IOReturn sReadMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn readMSR(pcm_msr_data_t* idata, pcm_msr_data_t* odata); + + static IOReturn sWriteMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn writeMSR(pcm_msr_data_t* data); + + static IOReturn sBuildTopology(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn buildTopology(topologyEntry* data, size_t output_size); + + static IOReturn sGetNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn getNumInstances(uint32_t* num_insts); + + static IOReturn sIncrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn incrementNumInstances(uint32_t* num_insts); + + static IOReturn sDecrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); + virtual IOReturn decrementNumInstances(uint32_t* num_insts); + + // PCI functions + static IOReturn sRead(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); + virtual IOReturn read(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize); + + static IOReturn sWrite(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); + virtual IOReturn write(const uint64_t* input, uint32_t inputSize); + + static IOReturn sMapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); + virtual IOReturn mapMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize); + + static IOReturn sUnmapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); + virtual IOReturn unmapMemory(const uint64_t* input, uint32_t inputSize); + + static IOReturn sReadMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); + virtual IOReturn readMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize); +}; \ No newline at end of file diff --git a/MacMSRDriver/PcmMsr/UserKernelShared.h b/MacMSRDriver/PcmMsr/UserKernelShared.h new file mode 100644 index 0000000..00b06aa --- /dev/null +++ b/MacMSRDriver/PcmMsr/UserKernelShared.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Austen Ott +// +#define PcmMsrDriverClassName com_intel_driver_PcmMsr +#define kPcmMsrDriverClassName "com_intel_driver_PcmMsr" +#ifndef USER_KERNEL_SHARED +#define USER_KERNEL_SHARED +#include +typedef struct { + uint64_t value; + uint32_t cpu_num; + uint32_t msr_num; +} pcm_msr_data_t; + +typedef struct { + uint64_t value; + uint32_t msr_num; + bool mask; + char padding[115]; +} k_pcm_msr_data_t; + +// The topologyEntry struct that is used by PCM +typedef struct{ + uint32_t os_id; + uint32_t socket; + uint32_t core_id; +} topologyEntry; + +// A kernel version of the topology entry structure. It has +// an extra unused int to explicitly align the struct on a 64bit +// boundary, preventing the compiler from adding extra padding. +typedef struct{ + uint32_t os_id; + uint32_t socket; + uint32_t core_id; + char padding[116]; +} kTopologyEntry; + +enum { + kOpenDriver, + kCloseDriver, + kReadMSR, + kWriteMSR, + kBuildTopology, + kGetNumInstances, + kIncrementNumInstances, + kDecrementNumInstances, + // PCI functions + kRead, + kWrite, + kMapMemory, + kUnmapMemory, + kReadMemory, + kNumberOfMethods +}; +#endif \ No newline at end of file diff --git a/MacMSRDriver/PcmMsr/en.lproj/InfoPlist.strings b/MacMSRDriver/PcmMsr/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/MacMSRDriver/PcmMsr/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/MacMSRDriver/kextload.sh b/MacMSRDriver/kextload.sh new file mode 100644 index 0000000..773b727 --- /dev/null +++ b/MacMSRDriver/kextload.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +cp -R build/Release/PcmMsrDriver.kext /tmp/. +mv /tmp/PcmMsrDriver.kext /System/Library/Extensions +chown -R root:wheel /System/Library/Extensions/PcmMsrDriver.kext +kextload /System/Library/Extensions/PcmMsrDriver.kext diff --git a/MacMSRDriver/kextunload.sh b/MacMSRDriver/kextunload.sh new file mode 100644 index 0000000..4d9f7f2 --- /dev/null +++ b/MacMSRDriver/kextunload.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +kextunload /System/Library/Extensions/PcmMsrDriver.kext +rm -rf /System/Library/Extensions/PcmMsrDriver.kext diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f7a78d6 --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +# +# Copyright (c) 2009-2014 Intel Corporation +# written by Roman Dementiev and Jim Harris +# + +EXE = pcm-numa.x pcm-power.x pcm.x pcm-sensor.x pcm-msr.x pcm-memory.x pcm-tsx.x pcm-pcie.x + +all: $(EXE) + +CXXFLAGS += -Wall -g -O3 + +# uncomment if you want to rely on Linux perf support (user needs CAP_SYS_ADMIN privileges) +ifneq ($(wildcard /usr/include/linux/perf_event.h),) +#CXXFLAGS += -DPCM_USE_PERF +endif + +UNAME:=$(shell uname) + +ifeq ($(UNAME), Linux) +LIB= -pthread -lrt +endif +ifeq ($(UNAME), Darwin) +LIB= -lpthread /usr/lib/libPcmMsr.dylib +CXXFLAGS += -I/usr/include +endif +ifeq ($(UNAME), FreeBSD) +CXX=c++ +LIB= -lpthread -lc++ +endif + +COMMON_OBJS = msr.o cpucounters.o pci.o client_bw.o utils.o +EXE_OBJS = $(EXE:.x=.o) +OBJS = $(COMMON_OBJS) $(EXE_OBJS) + +# ensure 'make' does not delete the intermediate .o files +.PRECIOUS: $(OBJS) + +-include $(OBJS:.o=.d) +%.x: %.o $(COMMON_OBJS) + $(CXX) -o $@ $^ $(LIB) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $*.cpp -o $*.o + @# the following lines generate dependency files for the + @# target object + @# from http://scottmcpeak.com/autodepend/autodepend.html + $(CXX) -MM $(CXXFLAGS) $*.cpp > $*.d + @# these sed/fmt commands modify the .d file to add a target + @# rule for each .h and .cpp file with no dependencies; + @# this will force 'make' to rebuild any objects that + @# depend on a file that has been renamed rather than + @# exiting with an error + @mv -f $*.d $*.d.tmp + @sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d + @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \ + sed -e 's/^ *//' -e 's/$$/:/' >> $*.d + @rm -f $*.d.tmp + +nice: + uncrustify --replace -c ~/uncrustify.cfg *.cpp *.h WinMSRDriver/Win7/*.h WinMSRDriver/Win7/*.c WinMSRDriver/WinXP/*.h WinMSRDriver/WinXP/*.c PCM_Win/*.h PCM_Win/*.cpp + +clean: + rm -rf *.x *.o *~ *.d diff --git a/PCM-MSR_Win/pcm-msr-win.cpp b/PCM-MSR_Win/pcm-msr-win.cpp new file mode 100644 index 0000000..7f57212 --- /dev/null +++ b/PCM-MSR_Win/pcm-msr-win.cpp @@ -0,0 +1,6 @@ +// pcm-msr-win.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +#include "../pcm-msr.cpp" diff --git a/PCM-MSR_Win/pcm-msr-win.vcproj b/PCM-MSR_Win/pcm-msr-win.vcproj new file mode 100644 index 0000000..538f292 --- /dev/null +++ b/PCM-MSR_Win/pcm-msr-win.vcproj @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM-MSR_Win/stdafx.cpp b/PCM-MSR_Win/stdafx.cpp new file mode 100644 index 0000000..db9b2d1 --- /dev/null +++ b/PCM-MSR_Win/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pcm.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/PCM-MSR_Win/stdafx.h b/PCM-MSR_Win/stdafx.h new file mode 100644 index 0000000..4360537 --- /dev/null +++ b/PCM-MSR_Win/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + +// TODO: reference additional headers your program requires here diff --git a/PCM-Memory_Win/pcm-memory-win.cpp b/PCM-Memory_Win/pcm-memory-win.cpp new file mode 100644 index 0000000..dad11a2 --- /dev/null +++ b/PCM-Memory_Win/pcm-memory-win.cpp @@ -0,0 +1,6 @@ +// pcm-memory-win.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +#include "../pcm-memory.cpp" diff --git a/PCM-Memory_Win/pcm-memory-win.vcproj b/PCM-Memory_Win/pcm-memory-win.vcproj new file mode 100644 index 0000000..b953a95 --- /dev/null +++ b/PCM-Memory_Win/pcm-memory-win.vcproj @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM-Memory_Win/stdafx.cpp b/PCM-Memory_Win/stdafx.cpp new file mode 100644 index 0000000..db9b2d1 --- /dev/null +++ b/PCM-Memory_Win/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pcm.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/PCM-Memory_Win/stdafx.h b/PCM-Memory_Win/stdafx.h new file mode 100644 index 0000000..4360537 --- /dev/null +++ b/PCM-Memory_Win/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + +// TODO: reference additional headers your program requires here diff --git a/PCM-NUMA_Win/pcm-numa-win.cpp b/PCM-NUMA_Win/pcm-numa-win.cpp new file mode 100644 index 0000000..54ded7d --- /dev/null +++ b/PCM-NUMA_Win/pcm-numa-win.cpp @@ -0,0 +1,6 @@ +// pcm-numa-win.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +#include "../pcm-numa.cpp" diff --git a/PCM-NUMA_Win/pcm-numa-win.vcproj b/PCM-NUMA_Win/pcm-numa-win.vcproj new file mode 100644 index 0000000..0cf2f94 --- /dev/null +++ b/PCM-NUMA_Win/pcm-numa-win.vcproj @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM-NUMA_Win/stdafx.cpp b/PCM-NUMA_Win/stdafx.cpp new file mode 100644 index 0000000..db9b2d1 --- /dev/null +++ b/PCM-NUMA_Win/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pcm.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/PCM-NUMA_Win/stdafx.h b/PCM-NUMA_Win/stdafx.h new file mode 100644 index 0000000..4360537 --- /dev/null +++ b/PCM-NUMA_Win/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + +// TODO: reference additional headers your program requires here diff --git a/PCM-PCIE_Win/pcm-pcie-win.cpp b/PCM-PCIE_Win/pcm-pcie-win.cpp new file mode 100644 index 0000000..686ae70 --- /dev/null +++ b/PCM-PCIE_Win/pcm-pcie-win.cpp @@ -0,0 +1,6 @@ +// pcm-pcie-win.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +#include "../pcm-pcie.cpp" diff --git a/PCM-PCIE_Win/pcm-pcie-win.vcproj b/PCM-PCIE_Win/pcm-pcie-win.vcproj new file mode 100644 index 0000000..8d9c84a --- /dev/null +++ b/PCM-PCIE_Win/pcm-pcie-win.vcproj @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM-PCIE_Win/stdafx.cpp b/PCM-PCIE_Win/stdafx.cpp new file mode 100644 index 0000000..db9b2d1 --- /dev/null +++ b/PCM-PCIE_Win/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pcm.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/PCM-PCIE_Win/stdafx.h b/PCM-PCIE_Win/stdafx.h new file mode 100644 index 0000000..4360537 --- /dev/null +++ b/PCM-PCIE_Win/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + +// TODO: reference additional headers your program requires here diff --git a/PCM-Power_Win/pcm-power-win.cpp b/PCM-Power_Win/pcm-power-win.cpp new file mode 100644 index 0000000..92825c6 --- /dev/null +++ b/PCM-Power_Win/pcm-power-win.cpp @@ -0,0 +1,6 @@ +// pcm-power-win.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +#include "../pcm-power.cpp" diff --git a/PCM-Power_Win/pcm-power-win.vcproj b/PCM-Power_Win/pcm-power-win.vcproj new file mode 100644 index 0000000..8871e23 --- /dev/null +++ b/PCM-Power_Win/pcm-power-win.vcproj @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM-Power_Win/stdafx.cpp b/PCM-Power_Win/stdafx.cpp new file mode 100644 index 0000000..db9b2d1 --- /dev/null +++ b/PCM-Power_Win/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pcm.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/PCM-Power_Win/stdafx.h b/PCM-Power_Win/stdafx.h new file mode 100644 index 0000000..4360537 --- /dev/null +++ b/PCM-Power_Win/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + +// TODO: reference additional headers your program requires here diff --git a/PCM-Service_Win/AssemblyInfo.cpp b/PCM-Service_Win/AssemblyInfo.cpp new file mode 100644 index 0000000..51bd41e --- /dev/null +++ b/PCM-Service_Win/AssemblyInfo.cpp @@ -0,0 +1,56 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* +** Written by Otto Bruggeman +*/ + + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("PCMService")]; +[assembly:AssemblyDescriptionAttribute("")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("Intel GmbH")]; +[assembly:AssemblyProductAttribute("PCMService")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) Intel GmbH 2010")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("1.0.*")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; + +[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; + diff --git a/PCM-Service_Win/PCM-Service.exe.config b/PCM-Service_Win/PCM-Service.exe.config new file mode 100644 index 0000000..8e2df6b --- /dev/null +++ b/PCM-Service_Win/PCM-Service.exe.config @@ -0,0 +1,5 @@ + + + + + diff --git a/PCM-Service_Win/PCMInstaller.cpp b/PCM-Service_Win/PCMInstaller.cpp new file mode 100644 index 0000000..45ef625 --- /dev/null +++ b/PCM-Service_Win/PCMInstaller.cpp @@ -0,0 +1,18 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* +** Written by Otto Bruggeman +*/ + + +#include "PCMInstaller.h" diff --git a/PCM-Service_Win/PCMInstaller.h b/PCM-Service_Win/PCMInstaller.h new file mode 100644 index 0000000..7d996ad --- /dev/null +++ b/PCM-Service_Win/PCMInstaller.h @@ -0,0 +1,97 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* +** Written by Otto Bruggeman +*/ + +#pragma once + +using namespace System; +using namespace System::ComponentModel; +using namespace System::Collections; +using namespace System::Configuration::Install; + + +namespace PMUService { + + [RunInstaller(true)] + + ///

+ /// Summary for ProjectInstaller + /// + public ref class ProjectInstaller : public System::Configuration::Install::Installer + { + public: + ProjectInstaller(void) + { + InitializeComponent(); + // + //TODO: Add the constructor code here + // + } + + protected: + /// + /// Clean up any resources being used. + /// + ~ProjectInstaller() + { + if (components) + { + delete components; + } + } + private: System::ServiceProcess::ServiceProcessInstaller^ serviceProcessInstaller1; + protected: + private: System::ServiceProcess::ServiceInstaller^ serviceInstaller1; + + private: + /// + /// Required designer variable. + /// + System::ComponentModel::Container ^components; + +#pragma region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + void InitializeComponent(void) + { + this->serviceProcessInstaller1 = (gcnew System::ServiceProcess::ServiceProcessInstaller()); + this->serviceInstaller1 = (gcnew System::ServiceProcess::ServiceInstaller()); + // + // serviceProcessInstaller1 + // + this->serviceProcessInstaller1->Account = System::ServiceProcess::ServiceAccount::LocalSystem; + this->serviceProcessInstaller1->Password = nullptr; + this->serviceProcessInstaller1->Username = nullptr; + // + // serviceInstaller1 + // + this->serviceInstaller1->Description = L"This service provides performance counters for perfmon to show hardware events ov" + L"er time such as Clockticks, Instruction Retired, Cache Misses and Memory Bandwi" + L"dth."; + this->serviceInstaller1->DisplayName = L"Intel Performance Counter Monitor Service"; + this->serviceInstaller1->ServiceName = L"PCMService"; + this->serviceInstaller1->StartType = System::ServiceProcess::ServiceStartMode::Automatic; + // + // PCMInstaller + // + this->Installers->AddRange(gcnew cli::array< System::Configuration::Install::Installer^ >(2) {this->serviceProcessInstaller1, + this->serviceInstaller1}); + + } +#pragma endregion + }; +} diff --git a/PCM-Service_Win/PCMInstaller.resx b/PCM-Service_Win/PCMInstaller.resx new file mode 100644 index 0000000..8e1d424 --- /dev/null +++ b/PCM-Service_Win/PCMInstaller.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 54 + + + 187, 17 + + + False + + \ No newline at end of file diff --git a/PCM-Service_Win/PCMService.cpp b/PCM-Service_Win/PCMService.cpp new file mode 100644 index 0000000..05e1c9c --- /dev/null +++ b/PCM-Service_Win/PCMService.cpp @@ -0,0 +1,82 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* +** Written by Otto Bruggeman and Roman Dementiev +*/ + +// PCMService.cpp : main Windows Service project file. + +#include "PCMService.h" + +#include +#include +#include + +using namespace PCMServiceNS; +using namespace System::Text; +using namespace System::Security::Policy; +using namespace System::Reflection; + +//To install/uninstall the service, type: "PMU Service.exe -Install [-u]" +int _tmain(int argc, _TCHAR* argv[]) +{ + if (argc >= 2) + { + if (argv[1][0] == _T('/')) + { + argv[1][0] = _T('-'); + } + + if (_tcsicmp(argv[1], _T("-Install")) == 0) + { + array^ myargs = System::Environment::GetCommandLineArgs(); + array^ args = gcnew array(myargs->Length - 1); + + // Set args[0] with the full path to the assembly, + Assembly^ assem = Assembly::GetExecutingAssembly(); + args[0] = assem->Location; + + Array::Copy(myargs, 2, args, 1, args->Length - 1); + AppDomain^ dom = AppDomain::CreateDomain(L"execDom"); + Type^ type = System::Object::typeid; + String^ path = type->Assembly->Location; + StringBuilder^ sb = gcnew StringBuilder(path->Substring(0, path->LastIndexOf(L"\\"))); + sb->Append(L"\\InstallUtil.exe"); + dom->ExecuteAssembly(sb->ToString(), args); + } + else + if (_tcsicmp(argv[1], _T("-Uninstall")) == 0) + { + array^ myargs = System::Environment::GetCommandLineArgs(); + array^ args = gcnew array(2); + + args[0] = L"-u"; + + // Set args[0] with the full path to the assembly, + Assembly^ assem = Assembly::GetExecutingAssembly(); + args[1] = assem->Location; + + AppDomain^ dom = AppDomain::CreateDomain(L"execDom"); + Type^ type = System::Object::typeid; + String^ path = type->Assembly->Location; + StringBuilder^ sb = gcnew StringBuilder(path->Substring(0, path->LastIndexOf(L"\\"))); + sb->Append(L"\\InstallUtil.exe"); + dom->ExecuteAssembly(sb->ToString(), args); + } + + } + else + { + ServiceBase::Run(gcnew PCMService); + } +} diff --git a/PCM-Service_Win/PCMService.h b/PCM-Service_Win/PCMService.h new file mode 100644 index 0000000..1be7dc2 --- /dev/null +++ b/PCM-Service_Win/PCMService.h @@ -0,0 +1,517 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* +** Written by Otto Bruggeman, Roman Dementiev +*/ + +#pragma once + +#pragma unmanaged +#include "..\Intelpcm.dll\Intelpcm.h" +#include "..\PCM_Win\windriver.h" +#include +#pragma managed + +using namespace System; +using namespace System::Collections; +using namespace System::ServiceProcess; +using namespace System::ComponentModel; +using namespace System::Diagnostics; +using namespace System::Threading; +using namespace System::Runtime::InteropServices; + +namespace PCMServiceNS { + + ref class MeasureThread + { + public: + MeasureThread( System::Diagnostics::EventLog^ log ) : log_(log) + { + // Get a Monitor instance which sets up the PMU, it also figures out the number of cores and sockets which we need later on to create the performance counters + m_ = PCM::getInstance(); + if ( !m_->good() ) + { + log_->WriteEntry("PCMService", "Monitor Instance could not be created.", EventLogEntryType::Error); + m_->cleanup(); + String^ s = gcnew String(m_->getErrorMessage().c_str()); + throw gcnew Exception(s); + } + log_->WriteEntry( "PCMService", "PCM: Number of cores detected: " + UInt32(m_->getNumCores()).ToString() ); + + m_->program(); + + log_->WriteEntry( "PCMService", "PMU Programmed." ); + + // This here will only create the necessary registry entries, the actual counters are created later. + // New unified category + if ( PerformanceCounterCategory::Exists( "PCM Core Counters" ) ) + { + PerformanceCounterCategory::Delete( "PCM Core Counters" ); + } + if ( PerformanceCounterCategory::Exists( "PCM Socket Counters" ) ) + { + PerformanceCounterCategory::Delete( "PCM Socket Counters" ); + } + if ( PerformanceCounterCategory::Exists( "PCM QPI Counters" ) ) + { + PerformanceCounterCategory::Delete( "PCM QPI Counters" ); + } + log_->WriteEntry( "PCMService", "Old categories deleted." ); + // First create the collection, then add counters to it so we add them all at once + CounterCreationDataCollection^ counterCollection = gcnew CounterCreationDataCollection; + + // Here we add the counters one by one, need list of counters currently collected. + // This is a stub: copy and paste when new counters are added to ipcustat "library". + // CounterCreationData^ counter = gcnew CounterCreationData( "counter", "helptext for counter", PerformanceCounterType::NumberOfItems64 ); + // counterCollection->Add( counter ); + CounterCreationData^ counter; + counter = gcnew CounterCreationData( "Clockticks", "Displays the number of clockticks elapsed since previous measurement.", PerformanceCounterType::CounterDelta64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Instructions Retired", "Displays the number of instructions retired since previous measurement.", PerformanceCounterType::CounterDelta64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "L2 Cache Misses", "Displays the L2 Cache Misses caused by this core.", PerformanceCounterType::CounterDelta64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "L3 Cache Misses", "Displays the L3 Cache Misses caused by this core.", PerformanceCounterType::CounterDelta64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Instructions Per Clocktick (IPC)", "Displays the instructions per clocktick executed for this core.", PerformanceCounterType::AverageCount64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Base ticks IPC", "Not visible", PerformanceCounterType::AverageBase ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Relative Frequency (%)", "Displays the current frequency of the core to its rated frequency in percent.", PerformanceCounterType::SampleFraction ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Nominal Frequency", "Not visible", PerformanceCounterType::SampleBase ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Thermal Headroom below TjMax", "Displays temperature reading in 1 degree Celsius relative to the TjMax temperature. 0 corresponds to the max temperature.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "core C0-state residency (%)", "Displays the residency of core or socket in core C0-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "core C3-state residency (%)", "Displays the residency of core or socket in core C3-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "core C6-state residency (%)", "Displays the residency of core or socket in core C6-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "core C7-state residency (%)", "Displays the residency of core or socket in core C7-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + PerformanceCounterCategory::Create( "PCM Core Counters", "Intel Performance Counter Monitor", PerformanceCounterCategoryType::MultiInstance, counterCollection ); + + counterCollection->Clear(); + counter = gcnew CounterCreationData( "Memory Read Bandwidth", "Displays the memory read bandwidth in bytes/s of this socket.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Memory Write Bandwidth", "Displays the memory write bandwidth in bytes/s of this socket.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "Package/Socket Consumed Energy", "Displays the energy in Joules consumed by this socket.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "DRAM/Memory Consumed Energy", "Displays the energy in Joules consumed by DRAM memory attached to the memory controller of this socket.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "package C2-state residency (%)", "Displays the residency of socket in package C2-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "package C3-state residency (%)", "Displays the residency of socket in package C3-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "package C6-state residency (%)", "Displays the residency of socket in package C6-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + counter = gcnew CounterCreationData( "package C7-state residency (%)", "Displays the residency of socket in package C7-state in percent.", PerformanceCounterType::NumberOfItems64 ); + counterCollection->Add( counter ); + PerformanceCounterCategory::Create( "PCM Socket Counters", "Intel Performance Counter Monitor", PerformanceCounterCategoryType::MultiInstance, counterCollection ); + + counterCollection->Clear(); + counter = gcnew CounterCreationData( "QPI Link Bandwidth", "Displays the incoming bandwidth in bytes/s of this QPI link.", PerformanceCounterType::CounterDelta64 ); + counterCollection->Add( counter ); + PerformanceCounterCategory::Create( "PCM QPI Counters", "Intel Performance Counter Monitor", PerformanceCounterCategoryType::MultiInstance, counterCollection ); + + log_->WriteEntry( "PCMService", "New categories added." ); + + // Registry entries created, now we need to create the programmatic counters. For some things you may want one instance for every core/thread/socket/qpilink so create in a loop. + // PerformanceCounter^ pc1 = gcnew PerformanceCounter( "SomeCounterName", "nameOfCounterAsEnteredInTheRegistry", "instanceNameOfCounterAsANumber" ); + + // Create #processors instances of the core specific performance counters + String^ s; // Used for creating the instance name and the string to search for in the hashtable + for ( unsigned int i = 0; i < m_->getNumCores(); ++i ) + { + s = UInt32(i).ToString(); // For core counters we use just the number of the core + ticksHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Clockticks", s, false ) ); + instRetHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Instructions Retired", s, false ) ); + ipcHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Instructions Per Clocktick (IPC)", s, false ) ); + baseTicksForIpcHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Base ticks IPC", s, false ) ); + relFreqHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Relative Frequency (%)", s, false ) ); + baseTicksForRelFreqHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Nominal Frequency", s, false ) ); + l2CacheMissHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "L2 Cache Misses", s, false ) ); + l3CacheMissHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "L3 Cache Misses", s, false ) ); + thermalHeadroomHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Thermal Headroom below TjMax", s, false ) ); + CoreC0StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C0-state residency (%)", s, false ) ); + CoreC3StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C3-state residency (%)", s, false ) ); + CoreC6StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C6-state residency (%)", s, false ) ); + CoreC7StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C7-state residency (%)", s, false ) ); + } + + // Create socket instances of the common core counters, names are Socket+number + for ( unsigned int i=0; igetNumSockets(); ++i ) + { + s = "Socket"+UInt32(i).ToString(); + ticksHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Clockticks", s, false ) ); + instRetHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Instructions Retired", s, false ) ); + ipcHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Instructions Per Clocktick (IPC)", s, false ) ); + baseTicksForIpcHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Base ticks IPC", s, false ) ); + relFreqHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Relative Frequency (%)", s, false ) ); + baseTicksForRelFreqHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Nominal Frequency", s, false ) ); + l2CacheMissHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "L2 Cache Misses", s, false ) ); + l3CacheMissHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "L3 Cache Misses", s, false ) ); + thermalHeadroomHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Thermal Headroom below TjMax", s, false ) ); + CoreC0StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C0-state residency (%)", s, false ) ); + CoreC3StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C3-state residency (%)", s, false ) ); + CoreC6StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C6-state residency (%)", s, false ) ); + CoreC7StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C7-state residency (%)", s, false ) ); + mrbHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "Memory Read Bandwidth", s, false ) ); + mwbHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "Memory Write Bandwidth", s, false ) ); + qpiHash_.Add( s, gcnew PerformanceCounter( "PCM QPI Counters", "QPI Link Bandwidth", s, false ) ); // Socket aggregate + packageEnergyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "Package/Socket Consumed Energy", s, false ) ); + DRAMEnergyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "DRAM/Memory Consumed Energy", s, false ) ); + PackageC2StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C2-state residency (%)", s, false ) ); + PackageC3StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C3-state residency (%)", s, false ) ); + PackageC6StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C6-state residency (%)", s, false ) ); + PackageC7StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C7-state residency (%)", s, false ) ); + String^ t; + for ( unsigned int j=0; jgetQPILinksPerSocket(); ++j ) + { + t = s + "_Link" + UInt32(j).ToString(); + qpiHash_.Add( t, gcnew PerformanceCounter( "PCM QPI Counters", "QPI Link Bandwidth", t, false ) ); + } + } + + // Create #system instances of the system specific performance counters, just kidding, there is only one system so 1 instance + s = "Total_"; + ticksHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Clockticks", s, false ) ); + instRetHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Instructions Retired", s, false ) ); + ipcHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Instructions Per Clocktick (IPC)", s, false ) ); + baseTicksForIpcHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "Base ticks IPC", s, false ) ); + relFreqHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Relative Frequency (%)", s, false ) ); + baseTicksForRelFreqHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Nominal Frequency", s, false ) ); + l2CacheMissHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "L2 Cache Misses", s, false ) ); + l3CacheMissHash_.Add(s, gcnew PerformanceCounter( "PCM Core Counters", "L3 Cache Misses", s, false ) ); + thermalHeadroomHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "Thermal Headroom below TjMax", s, false ) ); + CoreC0StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C0-state residency (%)", s, false ) ); + CoreC3StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C3-state residency (%)", s, false ) ); + CoreC6StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C6-state residency (%)", s, false ) ); + CoreC7StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Core Counters", "core C7-state residency (%)", s, false ) ); + mrbHash_.Add(s, gcnew PerformanceCounter( "PCM Socket Counters", "Memory Read Bandwidth", s, false ) ); + mwbHash_.Add(s, gcnew PerformanceCounter( "PCM Socket Counters", "Memory Write Bandwidth", s, false ) ); + packageEnergyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "Package/Socket Consumed Energy", s, false ) ); + DRAMEnergyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "DRAM/Memory Consumed Energy", s, false ) ); + PackageC2StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C2-state residency (%)", s, false ) ); + PackageC3StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C3-state residency (%)", s, false ) ); + PackageC6StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C6-state residency (%)", s, false ) ); + PackageC7StateResidencyHash_.Add( s, gcnew PerformanceCounter( "PCM Socket Counters", "package C7-state residency (%)", s, false ) ); + qpiHash_.Add( s, gcnew PerformanceCounter( "PCM QPI Counters", "QPI Link Bandwidth", s, false ) ); + + log_->WriteEntry( "PCMService", "All instances of the performance counter categories have been created." ); + } + + void doMeasurements( void ) + { + // FIXME: Do we care about hot swappability of CPUs? + const size_t numSockets = m_->getNumSockets(); + const size_t numCores = m_->getNumCores(); + const size_t numQpiLinks = (size_t) m_->getQPILinksPerSocket(); + + // The structures + SystemCounterState oldSystemState; + SocketCounterState* oldSocketStates = new SocketCounterState[numSockets]; + CoreCounterState* oldCoreStates = new CoreCounterState[numCores]; + + try { + while ( 1 ) + { + // Sampling roughly once per second is enough + Thread::Sleep(1000); + + // Fetch counter data here and store in the PerformanceCounter instances + SystemCounterState systemState = getSystemCounterState(); + // Set system performance counters + String^ s = "Total_"; + __int64 totalTicks = getCycles(systemState); + __int64 totalRefTicks = m_->getNominalFrequency() * numCores; + __int64 totalInstr = getInstructionsRetired(systemState); + ((PerformanceCounter^)ticksHash_["Total_"])->RawValue = totalTicks; + ((PerformanceCounter^)instRetHash_["Total_"])->RawValue = totalInstr; + ((PerformanceCounter^)ipcHash_["Total_"])->RawValue = totalInstr >> 17; + ((PerformanceCounter^)baseTicksForIpcHash_["Total_"])->RawValue = totalTicks >> 17; + ((PerformanceCounter^)relFreqHash_["Total_"])->RawValue = totalTicks >> 17; + ((PerformanceCounter^)baseTicksForRelFreqHash_["Total_"])->IncrementBy(totalRefTicks >> 17); + ((PerformanceCounter^)CoreC0StateResidencyHash_["Total_"])->RawValue = __int64(100.*getCoreCStateResidency(0,oldSystemState, systemState)); + ((PerformanceCounter^)CoreC3StateResidencyHash_["Total_"])->RawValue = __int64(100.*getCoreCStateResidency(3,oldSystemState, systemState)); + ((PerformanceCounter^)CoreC6StateResidencyHash_["Total_"])->RawValue = __int64(100.*getCoreCStateResidency(6,oldSystemState, systemState)); + ((PerformanceCounter^)CoreC7StateResidencyHash_["Total_"])->RawValue = __int64(100.*getCoreCStateResidency(7,oldSystemState, systemState)); + ((PerformanceCounter^)PackageC2StateResidencyHash_["Total_"])->RawValue = __int64(100.*getPackageCStateResidency(2,oldSystemState, systemState)); + ((PerformanceCounter^)PackageC3StateResidencyHash_["Total_"])->RawValue = __int64(100.*getPackageCStateResidency(3,oldSystemState, systemState)); + ((PerformanceCounter^)PackageC6StateResidencyHash_["Total_"])->RawValue = __int64(100.*getPackageCStateResidency(6,oldSystemState, systemState)); + ((PerformanceCounter^)PackageC7StateResidencyHash_["Total_"])->RawValue = __int64(100.*getPackageCStateResidency(7,oldSystemState, systemState)); + //log_->WriteEntry("PCMService", "Std: " + UInt64(totalTicks).ToString()); + //log_->WriteEntry("PCMService", "Ref: " + UInt64(totalRefTicks).ToString()); + ((PerformanceCounter^)l2CacheMissHash_["Total_"])->IncrementBy(getL2CacheMisses(oldSystemState, systemState)); + ((PerformanceCounter^)l3CacheMissHash_["Total_"])->IncrementBy(getL3CacheMisses(oldSystemState, systemState)); + ((PerformanceCounter^)mrbHash_["Total_"])->RawValue = getBytesReadFromMC(oldSystemState, systemState); + ((PerformanceCounter^)mwbHash_["Total_"])->RawValue = getBytesWrittenToMC(oldSystemState, systemState); + ((PerformanceCounter^)qpiHash_["Total_"])->RawValue = getAllIncomingQPILinkBytes(systemState); + ((PerformanceCounter^)packageEnergyHash_["Total_"])->RawValue = (__int64)getConsumedJoules(oldSystemState, systemState); + ((PerformanceCounter^)DRAMEnergyHash_["Total_"])->RawValue = (__int64)getDRAMConsumedJoules(oldSystemState, systemState); + ((PerformanceCounter^)thermalHeadroomHash_["Total_"])->RawValue = systemState.getThermalHeadroom(); + + // Copy current state to old state + oldSystemState = systemState; + + // Set socket performance counters + for ( unsigned int i = 0; i < numSockets; ++i ) + { + s = "Socket"+UInt32(i).ToString(); + SocketCounterState socketState = getSocketCounterState(i); + __int64 socketTicks = getCycles(socketState); + __int64 socketRefTicks = m_->getNominalFrequency()* numCores / numSockets; + __int64 socketInstr = getInstructionsRetired(socketState); + ((PerformanceCounter^)instRetHash_[s])->RawValue = socketInstr; + ((PerformanceCounter^)ipcHash_[s])->RawValue = socketInstr >> 17; + ((PerformanceCounter^)ticksHash_[s])->RawValue = socketTicks; + ((PerformanceCounter^)baseTicksForIpcHash_[s])->RawValue = socketTicks >> 17; + ((PerformanceCounter^)relFreqHash_[s])->RawValue = socketTicks >> 17; + ((PerformanceCounter^)baseTicksForRelFreqHash_[s])->IncrementBy(socketRefTicks >> 17); + ((PerformanceCounter^)l2CacheMissHash_[s])->IncrementBy(getL2CacheMisses(oldSocketStates[i], socketState)); + ((PerformanceCounter^)l3CacheMissHash_[s])->IncrementBy(getL3CacheMisses(oldSocketStates[i], socketState)); + ((PerformanceCounter^)mrbHash_[s])->RawValue = getBytesReadFromMC(oldSocketStates[i], socketState); + ((PerformanceCounter^)mwbHash_[s])->RawValue = getBytesWrittenToMC(oldSocketStates[i], socketState); + ((PerformanceCounter^)qpiHash_[s])->RawValue = getSocketIncomingQPILinkBytes(i, systemState); + ((PerformanceCounter^)packageEnergyHash_[s])->RawValue = (__int64)getConsumedJoules(oldSocketStates[i], socketState); + ((PerformanceCounter^)DRAMEnergyHash_[s])->RawValue = (__int64)getDRAMConsumedJoules(oldSocketStates[i], socketState); + ((PerformanceCounter^)thermalHeadroomHash_[s])->RawValue = socketState.getThermalHeadroom(); + ((PerformanceCounter^)CoreC0StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(0,oldSocketStates[i], socketState)); + ((PerformanceCounter^)CoreC3StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(3,oldSocketStates[i], socketState)); + ((PerformanceCounter^)CoreC6StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(6,oldSocketStates[i], socketState)); + ((PerformanceCounter^)CoreC7StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(7,oldSocketStates[i], socketState)); + ((PerformanceCounter^)PackageC2StateResidencyHash_[s])->RawValue = __int64(100.*getPackageCStateResidency(2,oldSocketStates[i], socketState)); + ((PerformanceCounter^)PackageC3StateResidencyHash_[s])->RawValue = __int64(100.*getPackageCStateResidency(3,oldSocketStates[i], socketState)); + ((PerformanceCounter^)PackageC6StateResidencyHash_[s])->RawValue = __int64(100.*getPackageCStateResidency(6,oldSocketStates[i], socketState)); + ((PerformanceCounter^)PackageC7StateResidencyHash_[s])->RawValue = __int64(100.*getPackageCStateResidency(7,oldSocketStates[i], socketState)); + String^ t; + // and qpi link counters per socket + for ( unsigned int j=0; jRawValue = getIncomingQPILinkBytes(i, j, systemState); + } + oldSocketStates[i] = socketState; + } + + // Set core performance counters + for ( unsigned int i = 0; i < numCores; ++i ) + { + s = UInt32(i).ToString(); + CoreCounterState coreState = getCoreCounterState(i); + __int64 ticks = getCycles(coreState); + __int64 refTicks = m_->getNominalFrequency(); + __int64 instr = getInstructionsRetired(coreState); + ((PerformanceCounter^)instRetHash_[s])->RawValue = instr; + ((PerformanceCounter^)ipcHash_[s])->RawValue = instr >> 17; + ((PerformanceCounter^)ticksHash_[s])->RawValue = ticks; + ((PerformanceCounter^)baseTicksForIpcHash_[s])->RawValue = ticks >> 17; + ((PerformanceCounter^)relFreqHash_[s])->RawValue = ticks >> 17; + ((PerformanceCounter^)baseTicksForRelFreqHash_[s])->IncrementBy(refTicks >> 17); + ((PerformanceCounter^)l2CacheMissHash_[s])->IncrementBy(getL2CacheMisses(oldCoreStates[i], coreState)); + ((PerformanceCounter^)l3CacheMissHash_[s])->IncrementBy(getL3CacheMisses(oldCoreStates[i], coreState)); + ((PerformanceCounter^)thermalHeadroomHash_[s])->RawValue = coreState.getThermalHeadroom(); + ((PerformanceCounter^)CoreC0StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(0,oldCoreStates[i], coreState)); + ((PerformanceCounter^)CoreC3StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(3,oldCoreStates[i], coreState)); + ((PerformanceCounter^)CoreC6StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(6,oldCoreStates[i], coreState)); + ((PerformanceCounter^)CoreC7StateResidencyHash_[s])->RawValue = __int64(100.*getCoreCStateResidency(7,oldCoreStates[i], coreState)); + oldCoreStates[i] = coreState; + } + } + } + catch( ThreadAbortException^ ) + { + // We get here when for instance the service gets stopped or something bad happens. + // In order to do our cleanup, like unprogram the MSRs, close the driver and such, in case we get stopped we want to execute normally after this + // so this resets the abort and allows a normal exit + Thread::ResetAbort(); + } + // Here we now have the chance to do cleanup after catching the ThreadAbortException because of the ResetAbort + m_->cleanup(); + } + + private: + // Core counter hashtables + System::Collections::Hashtable ticksHash_; + System::Collections::Hashtable instRetHash_; + System::Collections::Hashtable ipcHash_; + System::Collections::Hashtable baseTicksForIpcHash_; + System::Collections::Hashtable relFreqHash_; + System::Collections::Hashtable baseTicksForRelFreqHash_; + System::Collections::Hashtable l3CacheMissHash_; + System::Collections::Hashtable l2CacheMissHash_; + // Socket counter hashtables + System::Collections::Hashtable mrbHash_; + System::Collections::Hashtable mwbHash_; + // QPI counter hashtables + System::Collections::Hashtable qpiHash_; + // Energy counters + System::Collections::Hashtable packageEnergyHash_; + System::Collections::Hashtable DRAMEnergyHash_; + // Thermal headroom + System::Collections::Hashtable thermalHeadroomHash_; + // C-state Residencies + System::Collections::Hashtable CoreC0StateResidencyHash_; + System::Collections::Hashtable CoreC3StateResidencyHash_; + System::Collections::Hashtable CoreC6StateResidencyHash_; + System::Collections::Hashtable CoreC7StateResidencyHash_; + System::Collections::Hashtable PackageC2StateResidencyHash_; + System::Collections::Hashtable PackageC3StateResidencyHash_; + System::Collections::Hashtable PackageC6StateResidencyHash_; + System::Collections::Hashtable PackageC7StateResidencyHash_; + + System::Diagnostics::EventLog^ log_; + + PCM* m_; + }; + + /// + /// Summary for PMCService + /// + /// + /// WARNING: If you change the name of this class, you will need to change the + /// 'Resource File Name' property for the managed resource compiler tool + /// associated with all .resx files this class depends on. Otherwise, + /// the designers will not be able to interact properly with localized + /// resources associated with this form. + public ref class PCMService : public System::ServiceProcess::ServiceBase + { + [DllImport ("advapi32.dll")] + static bool SetServiceStatus (IntPtr hServiceStatus, LPSERVICE_STATUS lpServiceStatus); + + private: + void SetServiceFail (int ErrorCode) + { + SERVICE_STATUS ServiceStatus_; + ServiceStatus_.dwCurrentState = (int)SERVICE_STOPPED; + ServiceStatus_.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ServiceStatus_.dwWaitHint = 0; + ServiceStatus_.dwWin32ExitCode = ErrorCode; + ServiceStatus_.dwServiceSpecificExitCode = 0; + ServiceStatus_.dwCheckPoint = 0; + ServiceStatus_.dwControlsAccepted = 0 | + (this->CanStop ? (int) SERVICE_ACCEPT_STOP : 0) | + (this->CanShutdown ? (int) SERVICE_ACCEPT_SHUTDOWN : 0) | + (this->CanPauseAndContinue ? (int) SERVICE_ACCEPT_PAUSE_CONTINUE : 0) | + (this->CanHandleSessionChangeEvent ? (int) SERVICE_ACCEPT_SESSIONCHANGE : 0) | + (this->CanHandlePowerEvent ? (int) SERVICE_ACCEPT_POWEREVENT : 0); + SetServiceStatus (this->ServiceHandle, &ServiceStatus_); + } + + + public: + PCMService() + { + InitializeComponent(); + // + //TODO: Add the constructor code here + // + } + protected: + /// + /// Clean up any resources being used. + /// + ~PCMService() + { + if (components) + { + delete components; + } + } + + /// + /// Set things in motion so your service can do its work. + /// + virtual void OnStart(array^ args) override + { + this->RequestAdditionalTime(4000); + // We should open the driver here + TCHAR driverPath[] = L"c:\\windows\\system32\\msr.sys"; + + EventLog->WriteEntry("PCMService", "Trying to start the driver...", EventLogEntryType::Information); + drv_ = new Driver; + try + { + drv_->start(driverPath); + } + catch (std::runtime_error* e) + { + String^ s = gcnew String("Cannot open the driver msr.sys.\nYou must have a signed msr.sys driver in c:\\windows\\system32\\ and have administrator rights to run this program.\n\n"); + String^ es = gcnew String(e->what()); + + throw gcnew Exception(s + es); + } + + // TODO: Add code here to start your service. + MeasureThread^ mt; + EventLog->WriteEntry("PCMService", "Trying to create the measure thread...", EventLogEntryType::Information); + try + { + mt = gcnew MeasureThread(EventLog); + } + catch (Exception^ e) + { + EventLog->WriteEntry("PCMService", e->Message, EventLogEntryType::Error); + SetServiceFail(0x80886); + throw e; + } + + // Create thread, pretty obvious comment here + workerThread_ = gcnew Thread( gcnew ThreadStart( mt, &MeasureThread::doMeasurements ) ); + // Start timer/thread to read out registers and fill performance counter structures + workerThread_->Start(); +// EventLog->WriteEntry("PCMService", System::DateTime::Now.ToLongTimeString() + " Monitor could not initialize PMU, aborting.", EventLogEntryType::Error); + + } + + /// + /// Stop this service. + /// + virtual void OnStop() override + { + // TODO: Add code here to perform any tear-down necessary to stop your service. + this->RequestAdditionalTime(4000); + // Stop timer/thread + // doMeasurements will do cleanup itself, might have to do some sanity checks here + workerThread_->Abort(); + drv_->stop(); + } + + private: + /// + /// Required designer variable. + /// + System::ComponentModel::Container ^components; + System::Threading::Thread^ workerThread_; + Driver* drv_; + +#pragma region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + void InitializeComponent(void) + { + // + // PCMService + // + this->CanPauseAndContinue = true; + this->ServiceName = L"PCMService"; + + } +#pragma endregion + }; +} diff --git a/PCM-Service_Win/PCMService.resX b/PCM-Service_Win/PCMService.resX new file mode 100644 index 0000000..3e82cf3 --- /dev/null +++ b/PCM-Service_Win/PCMService.resX @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + \ No newline at end of file diff --git a/PCM-Service_Win/PCMService.vcproj b/PCM-Service_Win/PCMService.vcproj new file mode 100644 index 0000000..57772a3 --- /dev/null +++ b/PCM-Service_Win/PCMService.vcproj @@ -0,0 +1,511 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM-Service_Win/app.ico b/PCM-Service_Win/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..3a5525fd794f7a7c5c8e6187f470ea3af38cd2b6 GIT binary patch literal 1078 zcmeHHJr05}7=1t!Hp3A*8IHkVf+j?-!eHY14Gtcw1Eb*_9>Bq^zETJ@GKj{_2j4$w zo9}xCh!8{T3=X##Skq>ikMjsvB|y%crWBM2iW(4pI}c%z6%lW!=~4v77#3{z!dmB1 z__&l)-{KUYR+|8|;wB^R|9ET$J@(@=#rd^=)qs85?vAy(PSF5CyNkus435LVkZ$rj zNw|JG-P7^hF<(;#o*Vk}5R#e|^13tBbQkeF?djULtvqyxd3<{9 literal 0 HcmV?d00001 diff --git a/PCM-Service_Win/app.rc b/PCM-Service_Win/app.rc new file mode 100644 index 0000000..8c1d640 --- /dev/null +++ b/PCM-Service_Win/app.rc @@ -0,0 +1,63 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon placed first or with lowest ID value becomes application icon + +LANGUAGE 9, 1 +#pragma code_page(1252) +1 ICON "app.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" + "\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/PCM-Service_Win/resource.h b/PCM-Service_Win/resource.h new file mode 100644 index 0000000..1f2251c --- /dev/null +++ b/PCM-Service_Win/resource.h @@ -0,0 +1,3 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by app.rc diff --git a/PCM-TSX_Win/pcm-tsx-win.cpp b/PCM-TSX_Win/pcm-tsx-win.cpp new file mode 100644 index 0000000..0381a31 --- /dev/null +++ b/PCM-TSX_Win/pcm-tsx-win.cpp @@ -0,0 +1,6 @@ +// pcm-tsx-win.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +#include "../pcm-tsx.cpp" diff --git a/PCM-TSX_Win/pcm-tsx-win.vcproj b/PCM-TSX_Win/pcm-tsx-win.vcproj new file mode 100644 index 0000000..c94bf08 --- /dev/null +++ b/PCM-TSX_Win/pcm-tsx-win.vcproj @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM-TSX_Win/stdafx.cpp b/PCM-TSX_Win/stdafx.cpp new file mode 100644 index 0000000..db9b2d1 --- /dev/null +++ b/PCM-TSX_Win/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pcm.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/PCM-TSX_Win/stdafx.h b/PCM-TSX_Win/stdafx.h new file mode 100644 index 0000000..4360537 --- /dev/null +++ b/PCM-TSX_Win/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + +// TODO: reference additional headers your program requires here diff --git a/PCM_Win/pcm.cpp b/PCM_Win/pcm.cpp new file mode 100644 index 0000000..4da3c85 --- /dev/null +++ b/PCM_Win/pcm.cpp @@ -0,0 +1,19 @@ +/* +Copyright (c) 2009-2011, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// pcm.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +#include "../pcm.cpp" diff --git a/PCM_Win/pcm.vcproj b/PCM_Win/pcm.vcproj new file mode 100644 index 0000000..7a0fc09 --- /dev/null +++ b/PCM_Win/pcm.vcproj @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCM_Win/stdafx.cpp b/PCM_Win/stdafx.cpp new file mode 100644 index 0000000..3551370 --- /dev/null +++ b/PCM_Win/stdafx.cpp @@ -0,0 +1,21 @@ +/* +Copyright (c) 2009-2011, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// stdafx.cpp : source file that includes just the standard includes +// pcm.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/PCM_Win/stdafx.h b/PCM_Win/stdafx.h new file mode 100644 index 0000000..112702e --- /dev/null +++ b/PCM_Win/stdafx.h @@ -0,0 +1,29 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +/* +Copyright (c) 2009-2011, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + +// TODO: reference additional headers your program requires here diff --git a/PCM_Win/windriver.h b/PCM_Win/windriver.h new file mode 100644 index 0000000..9a50a40 --- /dev/null +++ b/PCM_Win/windriver.h @@ -0,0 +1,159 @@ +#ifndef WINDRIVER_HEADER +#define WINDRIVER_HEADER + +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// contact: Roman Dementiev +// WARNING: This driver code is only for testing purposes, not for production use +// + +#include +#include +#include "cpucounters.h" + +/*! \file windriver.h + \brief Loading and unloading custom Windows MSR (Model Specific Register) Driver +*/ + + +/*! \brief Manage custom Windows MSR (Model Specific Register) Driver + The driver is required to access hardware Model Specific Registers (MSRs) + under Windows. Currently only 64-bit Windows 7 has been tested. +*/ +class Driver +{ + SC_HANDLE hSCManager; + SC_HANDLE hService; + SERVICE_STATUS ss; + +public: + /*! \brief Installs and loads the driver + + Installs the driver if not installed and then loads it. + + \param driverPath full path to the driver + \return true iff driver start up was successful + */ + bool start(LPCWSTR driverPath) + { + hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (hSCManager) + { + hService = CreateService(hSCManager, L"Test MSR 4", L"Test MSR Driver 4", SERVICE_START | DELETE | SERVICE_STOP, + SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, driverPath, NULL, NULL, NULL, NULL, NULL); + + if (!hService) + { + hService = OpenService(hSCManager, L"Test MSR 4", SERVICE_START | DELETE | SERVICE_STOP); + } + + if (hService) + { + if (0 != StartService(hService, 0, NULL)) + { + return true; + } + DWORD err = GetLastError(); + if (err == ERROR_SERVICE_ALREADY_RUNNING) return true; + + _com_error error(err); + std::wcerr << "Starting MSR service failed with error " << err << " " << error.ErrorMessage() << std::endl; + + ControlService(hService, SERVICE_CONTROL_STOP, &ss); + + // DeleteService(hService); + + CloseServiceHandle(hService); + } + else + { + _com_error error(GetLastError()); + std::wcerr << "Opening service manager failed with error " << GetLastError() << " " << error.ErrorMessage() << std::endl; + } + + CloseServiceHandle(hSCManager); + } + else + { + _com_error error(GetLastError()); + std::wcerr << "Opening service manager failed with error " << GetLastError() << " " << error.ErrorMessage() << std::endl; + } + + + std::cerr << "Trying to load winring0.dll/winring0.sys driver..." << std::endl; + if(PCM::initWinRing0Lib()) + { + std::cerr << "Using winring0.dll/winring0.sys driver.\n" << std::endl; + return true; + } + else + { + std::cerr << "Failed to load winring0.dll/winring0.sys driver.\n" << std::endl; + } + + return false; + } + + //! \brief Stop and unload the driver + void stop() + { + hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (hSCManager) + { + hService = OpenService(hSCManager, L"Test MSR 4", SERVICE_START | DELETE | SERVICE_STOP); + DWORD res = 0; + if (hService) + { + ControlService(hService, SERVICE_CONTROL_STOP, &ss); + CloseServiceHandle(hService); + } + + CloseServiceHandle(hSCManager); + } + else + { + _com_error error(GetLastError()); + std::wcerr << "Opening service manager failed with error " << GetLastError() << " " << error.ErrorMessage() << std::endl; + } + } + + /*! \brief Uninstall the driver + + Uninstalls the driver. For successeful uninstallation you need to reboot the system after calling this method. + */ + void uninstall() + { + hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (hSCManager) + { + hService = OpenService(hSCManager, L"Test MSR 4", SERVICE_START | DELETE | SERVICE_STOP); + DWORD res = 0; + if (hService) + { + ControlService(hService, SERVICE_CONTROL_STOP, &ss); + DeleteService(hService); + CloseServiceHandle(hService); + } + + CloseServiceHandle(hSCManager); + } + else + { + _com_error error(GetLastError()); + std::wcerr << "Opening service manager failed with error " << GetLastError() << " " << error.ErrorMessage() << std::endl; + } + } +}; + + +#endif diff --git a/TODO b/TODO new file mode 100644 index 0000000..5b3e6b4 --- /dev/null +++ b/TODO @@ -0,0 +1,8 @@ + + TODO + + * Support non-zero invert and cmask fields for custom events + * Remember and handle properly the PMU program mode globally on the box/system + + + diff --git a/WINDOWS_HOWTO.rtf b/WINDOWS_HOWTO.rtf new file mode 100644 index 0000000..a79b40d --- /dev/null +++ b/WINDOWS_HOWTO.rtf @@ -0,0 +1,1330 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang2057\deflangfe2057\themelang2057\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\f34\fbidi \froman\fcharset204\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f37\fbidi \fswiss\fcharset204\fprq2{\*\panose 020f0502020204030204}Calibri;} +{\flomajor\f31500\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \froman\fcharset204\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset204\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f41\fbidi \froman\fcharset0\fprq2 Times New Roman;} +{\f39\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f381\fbidi \froman\fcharset0\fprq2 Cambria Math;} +{\f379\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f382\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f383\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f386\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} +{\f387\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f411\fbidi \fswiss\fcharset0\fprq2 Calibri;}{\f409\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f412\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;} +{\f413\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f416\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f417\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;} +{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fhimajor\f31530\fbidi \froman\fcharset0\fprq2 Cambria;}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;} +{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fhimajor\f31536\fbidi \froman\fcharset163\fprq2 Cambria (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;} +{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31550\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbminor\f31560\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;} +{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} +{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31580\fbidi \froman\fcharset0\fprq2 Times New Roman;} +{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0; +\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192; +\ctexttwo\ctint255\cshade255\red31\green73\blue125;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 +\snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext11 \ssemihidden \sunhideused +Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2 \sbasedon10 \ssemihidden \sunhideused \styrsid16320623 Hyperlink;}}{\*\listtable{\list\listtemplateid80663636\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace360\levelindent0{\leveltext\leveltemplateid134807567\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid134807577\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace360 +\levelindent0{\leveltext\leveltemplateid134807579\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0 +{\leveltext\leveltemplateid134807567\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext +\leveltemplateid134807577\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext +\leveltemplateid134807579\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext +\leveltemplateid134807567\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext +\leveltemplateid134807577\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext +\leveltemplateid134807579\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li6480\lin6480 }{\listname ;}\listid1155294070}}{\*\listoverridetable{\listoverride\listid1155294070\listoverridecount0\ls1}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0 +\sb0\sa0}}{\*\rsidtbl \rsid1514420\rsid1847683\rsid2696726\rsid3304547\rsid3891247\rsid4335690\rsid4744522\rsid5335986\rsid6562228\rsid6843258\rsid7106661\rsid7961100\rsid8066731\rsid8218016\rsid9256949\rsid9786799\rsid10518488\rsid10638995\rsid12127190 +\rsid12148899\rsid12648763\rsid13308151\rsid13645585\rsid13648796\rsid13713190\rsid14618714\rsid14954748\rsid15671676\rsid16320623\rsid16471996\rsid16592668}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1 +\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator Andrey Semin}{\creatim\yr2011\mo8\dy26\hr11\min43}{\revtim\yr2014\mo5\dy2\hr11\min22}{\version49}{\edmins33}{\nofpages2}{\nofwords496}{\nofchars2828}{\nofcharsws3318}{\vern49167}}{\*\xmlnstbl {\xmlns1 h +ttp://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 +\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale90\rsidroot16320623 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 +\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\qc \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 +\fs22\lang2057\langfe2057\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 {\rtlch\fcs1 \af37 \ltrch\fcs0 \b\f37\insrsid13308151\charrsid13308151 \hich\af37\dbch\af31505\loch\f37 COMPILATION AND }{\rtlch\fcs1 \af37 \ltrch\fcs0 +\b\f37\insrsid10518488\charrsid13308151 \hich\af37\dbch\af31505\loch\f37 INSTALLATION OF UTILITIES +\par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af37 \ltrch\fcs0 \i\f37\insrsid16592668\charrsid3304547 \hich\af37\dbch\af31505\loch\f37 For support of systems with more than }{\rtlch\fcs1 \af37 +\ltrch\fcs0 \b\i\f37\insrsid16592668\charrsid13308151 \hich\af37\dbch\af31505\loch\f37 64}{\rtlch\fcs1 \af37 \ltrch\fcs0 \i\f37\insrsid16592668\charrsid3304547 \hich\af37\dbch\af31505\loch\f37 \hich\f37 + logical cores you need to compile all binaries below in \'93}{\rtlch\fcs1 \af37 \ltrch\fcs0 \b\i\f37\insrsid16592668\charrsid15671676 \hich\af37\dbch\af31505\loch\f37 x64}{\rtlch\fcs1 \af37 \ltrch\fcs0 \i\f37\insrsid16592668\charrsid3304547 +\loch\af37\dbch\af31505\hich\f37 \'94\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \i\f37\insrsid8066731 \hich\af37\dbch\af31505\loch\f37 platform }{\rtlch\fcs1 \af37 \ltrch\fcs0 \i\f37\insrsid16592668\charrsid3304547 \hich\af37\dbch\af31505\loch\f37 +\hich\f37 mode (not the default \'93}{\rtlch\fcs1 \af37 \ltrch\fcs0 \b\i\f37\insrsid16592668\charrsid15671676 \hich\af37\dbch\af31505\loch\f37 Win32}{\rtlch\fcs1 \af37 \ltrch\fcs0 \i\f37\insrsid16592668\charrsid3304547 \loch\af37\dbch\af31505\hich\f37 +\'94\loch\f37 mode).}{\rtlch\fcs1 \af37 \ltrch\fcs0 \i\f37\insrsid10518488\charrsid3304547 +\par }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 Command-line utility: +\par \hich\af37\dbch\af31505\loch\f37 1.\tab Compile the windows MSR driver (msr.sys) with Windows* DDK Kit (see the sources in the WinMSRDriver directory). For Windows 7 you have to sign the msr.sys driver additionally (}{\field{\*\fldinst {\rtlch\fcs1 \af37 +\ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 HYPERLINK "http://\hich\af37\dbch\af31505\loch\f37 msdn.microsoft.com/en-us/library/ms537361(VS.85).aspx"}{\rtlch\fcs1 \af37 \ltrch\fcs0 +\f37\lang7\langfe2057\langnp7\insrsid16320623 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b9200000068007400740070003a002f002f006d00730064006e002e006d006900630072006f0073006f00660074002e0063006f006d002f0065006e002d00750073002f006c006900620072006100720079002f006d00 +73003500330037003300360031002800560053002e003800350029002e0061007300700078000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000ff0000000000}}}{\fldrslt {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\ul\cf2\insrsid10518488\charrsid16320623 +\hich\af37\dbch\af31505\loch\f37 http://msdn.microsoft.com/en-us/library/ms537361(VS.85).aspx}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 ). +\par \hich\af37\dbch\af31505\loch\f37 2.\tab \hich\af37\dbch\af31505\loch\f37 Build the pcm.exe utility in the PCM_Win using Microsoft* Visual Studio +\par \hich\af37\dbch\af31505\loch\f37 3.\tab Copy the msr.sys driver and pcm.exe into a single directory +\par \hich\af37\dbch\af31505\loch\f37 4.\tab }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid1847683 \hich\af37\dbch\af31505\loch\f37 R}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 +un pcm.exe utility from this directory +\par }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488 +\par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid13645585 {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid13645585 \hich\af37\dbch\af31505\loch\f37 +For Windows 7 and Windows Server 2008 R2 the Intel PCM utilities need to be\hich\af37\dbch\af31505\loch\f37 run as administrator: +\par }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang1024\langfe1024\noproof\insrsid13645585\charrsid13645585 {\*\shppict{\pict{\*\picprop\shplid1025{\sp{\sn shapeType}{\sv 75}}{\sp{\sn fFlipH}{\sv 0}} +{\sp{\sn fFlipV}{\sv 0}}{\sp{\sn fLockAspectRatio}{\sv 1}}{\sp{\sn fLockPosition}{\sv 0}}{\sp{\sn fLockAgainstSelect}{\sv 0}}{\sp{\sn fLockAgainstGrouping}{\sv 0}}{\sp{\sn pictureGray}{\sv 0}}{\sp{\sn pictureBiLevel}{\sv 0}}{\sp{\sn fFilled}{\sv 0}} +{\sp{\sn fNoFillHitTest}{\sv 0}}{\sp{\sn fLine}{\sv 0}}{\sp{\sn wzName}{\sv Picture 1}}{\sp{\sn dhgt}{\sv 251658240}}{\sp{\sn fLayoutInCell}{\sv 1}}{\sp{\sn fHidden}{\sv 0}}{\sp{\sn fLayoutInCell}{\sv 1}}} +\picscalex100\picscaley100\piccropl0\piccropr0\piccropt0\piccropb0\picw6802\pich1879\picwgoal3856\pichgoal1065\pngblip\bliptag1748430998{\*\blipuid 6836f096d239be683c305f9b4ee3f665} +89504e470d0a1a0a0000000d494844520000010100000047080200000088f8656f000000017352474200aece1ce9000000097048597300000ec300000ec301c7 +6fa8640000373e49444154785eed7d07801d55bdf7943b73676e2fdb4b363dbbe909214830f42a2a5fe0e103445410e5f1510414791a9417055410820201444a +80001f4a894080500c4280f490460aa9bbd97e7b9bfefdce9cbb379bcd26d9cd2694c79d0c87d999b967ce9cf9f776d82d2d26636f264bffcfeabac13b78cb62 +b06b86210abc2c33992cbd7a18360bfd16b72ffd0c98801486c1a72470c35a9cfd42a4e55986e33807c7b85cec3f5f7e7dc68c334c93910446c2a5cfe9ad3399 +8c2ccbc964d2e7f3d121000859b60be237b61288ccff452fe34f96200086ae28aa20085e2f8b3f33198b61f30833907729e2c04066ef0bf35be08091c7008b82 +0fc7dac48de779c6d401617ebf38ffa557669c7736c73222cf383f3f1cd075dde170188681b199a609145514c5e9c488c8c6ae6f0664038ff3e841de81634dd3 +703a798e03dce3e7e4f7382f0887078d8b38f08581e3010c84504303c06f430e65022cd78503baa65a96515aeafae74baf9d73ee99824d647195b70e030dedef +a001f1003980317000043d8fa5dd7a61d7345b64e83626b318226b1a9a9ecda63d2e972489a66e006fd005c703b17113de012f3ca0165c68803d0c7c0cc51e06 +380316c101ba1304d84b16625845c9b28c595e1e7a63e19ba79d768a2411d001d5ed2e6ef4179407787f8194d37e201779bdde3c1f58b5dbb45199087414075c +b2d0d1daca61d8860e1c70081c69451e6864915bf122036a6d7560403d0c7c0cc51e0ec70c501d92fcc7d9fa006b72545a56d51ce49f7038b87cf9d28993c6bb +2589b10c9eb5c1e733dfc007b0e572395114210bd9429a9f086c5d1bbbbcc9b0a19fc8fa60554083cd1bd727e251af5b3634d5d035a743c865d31c7e24f294f7 +0db065c0570e473f031f49b18781cc401708017c2802d8c8000909b4d2321d0e4e96c4d6d6e6503828cb4e6808e964ea33877ff2408c4792244dd3a8448316fa +f1f4e9d38109793eb0a451c3447004b081033adaced666b7ec1c535f073dc634a0e2e4c53de3304930e001982e0860c5f64b3d037980ee22ed5419002c691a23 +88300de5c146d335c12110e1e173b207261209b7db4d3900c6d0d8d8b86eddbad34e3badc00ad88f1a29f4e373189c0566a67fb2f6e3f27068d2c406433552b1 +a8ec72eaaa0ae1296768fdc182fc14d95363125ec3300e93c8403a2119ac4078a665503d8488959488f4771b9066324099b83fb3f1bf759cf6f7eab20b914f68 +31903a00332e972b954a8682dee6e666517480fa4a92abbf5ff7b0dc8f91c00a8451610ca0fd1d1d1d1f7cf0c1b9e79e5be89c830114906f58bc49443af0038e +87ecef101d3c415cfc1e4621f00ec3d41c8c05910ea77bb40e1626e1fc791b8b4cb450ad61365052595ed74596518d1cc7996ed60c48328b1e05a7cc8801c105 +1131e0f7e4527197c091fe194bcd66f05b9fdb65a88a24387425a764d222b1551938764b4eecb84716053c1746e862fb79ce003e01f90a96bd1bd879de70bb05 +7cad5c36e5e0594a830501764819e0f4b96c9d9d9da9540a16218fc793cd660bcab1aaaa140d3802ab061192b0d103c0e51e3f00517108a3b0b519d2d2e383b7 +0c934ea72b2b4a1c16eb14201c5ad148bbc3e58eb6b5eabaa6ab9aa599e9785a122132b6f9bc6e40bfec14d3a95438149225299948c0349b49a79da25855599e +88c773d92cce1bba1e8b461d3c9fcd640e3e86be8cb378cf406680c8b37bed5dc6a282d5e8b090f201750285988a3d500c08c4dbfa31ec4268bb70c0d22d4637 +4d9db8c44cc3b420aa407f878188a839b601870a2afdb6ec969796a5538a24bba3f19828b0436aabdb1a9b44d9e5774bba925135cd13f6261351076702479b9a +9a80af55559518685b5b1b8807ac0a74f491480c64a4b2b282ba36c0944a4bc3d46551dc8a3370d019000851680107a03800b6b07dfb76a001c0899c31d4aca6 +647435abab3955cbe88a0295a0cb676663336bdac6e0fe6ea6aa2ae8a7331107046bb9a46165fde1129677c63a5bdc4e4e74bb5bdbe29228b8243e1e8f0d1931 +9c73f089441203adabab050fddbe7d27c09d724f6833cdcd2d183170039caab5b51d4ca6bf032adeffd59c01e0400f3e001cd8b163075a2a0e71019f3be4f784 +7c6ebf4f8268ee0fb8f3ec8c68ab343882ec878006f0334017777bbd16c7eab9543619bbed8ebb86d68f9afeb5a3ce9ff18df59b36c91ebf65288ca98a4e474b +6b0b060a280776c662093b40c34bc24e6c0c0632e00c8ee3f1384e06ededabf9458b6fdddf19000105cc104906920e89036221054133469b978e76efdcd6b46b +5b23d9b7db07db6120822c0405df36d7e459411732f47500d019946c1ac6a8443a93cde52a2acbefbeeb8e3fdd73cf47cb576cdeb2c12b0be77fe702cd628075 +5ebfd7e3715594578442218c351c0ec39711006abaddb0e902312014c1be0b34f0933b3d180190a1c807fafa25bef2f77557c4699c1c90011cc0f663d8c1728b +d7efc6791873584687e280b671d78eb27018eebd6c3a65ea2ab47b8a3dc47cda9bf9929a5de954efb1015b060c3bbe40492cadf9bc7cf3d6f7be79ce8c6f9c7f +e30d37fc5c52b6b6b7758c9c34e381bf3eaa25d6efdcb1219636b76e6ffabfff75cd39e79c031168d6ac599b376f9e366ddacf7ffef3f7de7bef8d37de40dc5f +341a3dfae8a36fbcf146300a3c11180287fc57fefb1627e0e0334083e4d042cc0631052c3dfae8a365656513274e1c3a7428aca5dcf886ca71f595e3ebabc7d5 +d7c12f36a67e18228c8805dfb6727659ee0fc5c74d9c73ba2288522c160b85038843129cf09db0be50109d3b6dd7ddca95cb67dff5f88c19e740f2f9c52f7eb1 +6ddb0ec07d2412b9ebaebb5e7bedb59ffce427bb76ed7af8e1878f3beeb8fafafa471e7964c3860df47d8009077ffbe21dc519b0e9729ede93903742afd142b4 +a63114441f80b88d9d5278dad60eaa56b21981cb4799da3f3c946027a01d24195864e164808e9bc9e4a08510994c5561058234565353839e274fae3be9a4932e +bef8e24f3ffd74f1e2c52fbdf4d292254b66cc9801c20f64a5480c1c183f7e3c3c7c400f88432525a142e06bf12b1767e0c03340619d6e149281060541c88e07 +b5a3bab103fa6d598844ced90e5bdb1694cf8d38141c80c82e0a12e52465a515279f72ca6baf2eb00c93e11d1f2d5d565b533368d020b84ee2c9642a95019240 +1f00eb00c25c71c5158d8d3b77eedc7eebadb7e24f883de8012c2c10081077752ed7dcdc4a83608b5b7106063e0308efb3e1be8b09d00028db0b964f89206840 +f77ec532589cd7eb871ae1724ac978c2e174fff1ced94307d57ee7dc19a79e76e6638f3f75f79d7f1ad750e1e0c5f6cef8d9679ffd8b1b6ffce10f7f78fae9a7 +cf9c39f3f7bffffda9a79e3e7efcc4575e7905be02d07e28c14062a0018ebd5ef81d05e0ccc05fbed843710688ae6b99dd647d9bdcaf5fbf1e7208c40f182289 +b4648b4c7be9bb7bcf5caf3a31588c5310217a3978a76ea693c9ede17089a6956cdab43993dc5959595d51352e9b317e74d9392b567d30ef99bf33ac6370ddf0 +60308ca485152b56418e829d74cc98313880190b2c02400fb10aac03da0570c08e823a14ee54fcea5fb51920d28e9d4603f907223434c9c71e7baca2a262c284 +09c3860d8368bd77981ad0011c00f6fc3de9a194fc1f42341ba3e930628a918e0e4b374a2a6a9a5a3bf46caaa6ac7ce2d1d3caaa86702613f4f126e3f0074b27 +4d3ceaa849932b2b2b21f1c7e3c9912347d6d6d68e1b370ee41f09a0182ebc01e5e5e54000803e9404084530957ed5be65f17d8fd00c40c8b1e3210a7b2fcfd9 +932edd9f4170eded1d485a08797dc0bcddad91d2d26aa46786cbc3f194e2903ca9681cf2d6e9679e75fa99df52551890d4cef68e61c38682ab80ff00e27100ac +a54a30540b1c4337686f6f873e00551b32527f0653bcb73803fb9d011b07ba6ff9f450729e70833dca40bf2771d0e04199b4aa6b0a0287584e54342b9749673a +3b1c927b67536ba8c21f6f895df4dd4b6efffd1d881402886383d6be6ddb36483e10c620fd03e2abaa2ad0c26b467d7bb8647b0614308d7e0fa8f883e20cf436 +03487eeb21dde34fea1ea6073472ee50b6588408ee0a4285924951907339c55756a6193a3cd0907022ad9d4e190a83d9dcd25a5e5e9948a4589edbbdbb65c284 +719954da2dbb7c1e2f7cc63b76ec024f486732f027c04f0ceb2a3c772e514825e2c8fc21c93ff97c565b8fb777ba21d895ee7d90e5fa28e9156221bf40419187 +f2618abfd97b066c1cd87b476810890e22004402e668d85cffe3464d1e09018c8e583c8fcf2f587cd8174ac6e3083017d0b196752330c989671841af07f1db16 +cbeb86e5f6f892f1942488a6aa31069cd44a30e85775c5ed73c75371c0bcc7258b30f22aaa032178aac20aa2aa215115e590185e3791ab805870647e6a868a50 +5841969cb21bb90ab98c626b38e4bdecdd460f1b85b02359229b5361b3452645063fcea6f07311b90b0e87a61960416050f049238fc1e7f3c41351321b8599c9 +cfe621aa4c4568fc22ccc03e38d073508718386d935f2b998c57d5d6466231d101ca9d6e8b44914d0189a8341c520d359a88067c5e507dc836081aedae79dbd1 +4a794d3c994e0364dd5e174975d31453d3f55c16c8e3f1f8daa29150c80f3149e0043c02d90586aa81f9c008a0686a2c9980e7019bc78df03b1a6bddf53add94 +7eb8e1e0a6808e815fe1b7f051e036d8a068e2054601510d4a39ac0a9d914eb71bc95054792a6eff4b66a08f62c0a1bc2d80265c52b26be74e40124294101931 +78f06052ae48d7773735c1b0432d9e3455ed007e5f2800804efc1c5651c02b5464d9e5420b110bf01a8d924c251ce371d4a14603ec70039e0b2a6e5bc4883a41 +c43aeafab011ac700cb79dec94483525d302226107a7418119f8eb6870154d45857bce2ed244bdeae02385a9a3ce93e2f6659d8123f8f1885fdab20094084b02 +31460b4d177009291fb3052a4b63a1016480549acdd0eb864bf8398da3c69d4021a495413f8685141b450f0aee3467142d3dc09d34d00308684b4064cbb73401 +dc3e83dc39c220740377f22c074077703c1009a09f4ea14e0e0f85a4b3332a389c707b135cca7b0c6d5667679fdabd14c5a1220eec3303804b5830413e01c400 +56003db5ea00eeabaaab01d0a0ebb0fd93900a11caf17ea50b803885726cd477e60b874bcbcba9e3030c0470e9451c9e24e112cd8a001cd35f51be611b948244 +beb235002265753b067ae810fc550da08fb05a645f3b580e399c006b9fcf4fea5426b2763e38e7767b3d6e5f97925d4080823af5650582aff8b88f201f0020c2 +feb37bf76e003d682a282ba2e2fefce73fc34bf7d4934f426e0104036469441dcd66e875c30f41a101df24d7992519f98c286efae413c496b6b4b43cf3cc33c0 +aba58b3ff870d122803b95e9710f49dc1704748b9fd83117ed846edb88d6a3251e443828505292234c809404e4f9443401bb1604ab1d3b1ae7cfff27384036ab +e83acae6885d83a41c80aa2d47701abfe200fa19bcfe11fc78347d938a2b6002a0d6bff9cd6f10130a687eebadb79e7bee398029024841688103a0d9fb7b5b80 +3e8d17a23e04dcb96dedda9b6eba093906e816ae65802ca24de7cf9f0f70a7fa06361a700ad51668366fde3cdc9c5703f6d107601620fc01921bc2464c0b6659 +e8061b376e5cb0e0f5644285fc06650323ec6a91c3d94d04a2832e0a429f01a81eb1471c411c0068020a69e2023001e41f6ce1a979f366de7c3352616ebffd76 +00745555150421b45449e875030280c0a3a58a01ba453f106f66cf9e8d908a5b6e9989a7e011e80707e009b8197f520b0f1ee1f311ef32222c0e308734b096ca +6392db0d5c82ab6ee1c285e864ca51e32ebdf452a8d95e8f1f3d178a54eee106366be9c28423f6a18a1d1fb119e8050768ce012d4cd77debef1840b0a9bc0e4a +0fd1ff9d77deb9f0c20b5122a5bdad6dc89021480a5bbb76ed830f3ef8e31ffff86b5ffbdaa851a35e7df55500faca952b8f39e698534e3965c18205a0c1c71e +7bec8f7ef4a3baba3ab4342101a69fcb2fbf7ccd9a35679c7106a4ac69d38e87d601220d20c613e7ce9d3b79f26474be65cb163001281b180510031802a671f2 +c9278f183102e90a4b972e45e710a590ad3676ecd8bffce52fe8fc9a6bae41f9b163a74e450d26846d83b15c74d1455bb7364f9932055801ce803c9e868606e4 +36e087a855867a7ddffffef771e6b7bffd2d9db1e2f6059c8183c2ed11e4033461878a1025a5a5806f04381464155c02c882b2befbeebbcb962d03ec3ef1c413 +b0c45f70c105575f7d35922aa139d0149cabaeba0aa1ac10a2680a4e6747c7f3afbc3269d22448f93595e50065c0377a06b8e30670981b6eb801a0f9ab5ffd2a +e40fe112ced3ec69a4687efbdbdf6e6a6dbdfefaeb7115cce7bbdffd2e9e0b6cb969e64d305b2166fb073ff801d5aaa16c7ce73bdf79eeb9a733d914981858da +4fafbbe6e9a79fdeba75cba9a79e0ae10ada3c3070d6ac5b8030cf3c338fc87287ea503fe8472ade704467e008e2004411c07d6969294019e41fc418700c9420 +d1aa1c077a5f5252829038c02b5a988c40ec217b009ab76edd0aa8254c239703fc41f20905433403081da2db785b1b28317409e8d1d46c0fe4018147e225c2ad +917d0338bee4924b9a5a9a70b3d74b1465f4af9bc6a8fa7aa4c84d3fe1f8af4f9fbe62e5ca8e48e7906143638978677b64f3962ddffbfe25db766cf707033ebf +1fe3847175e7ae46f44f23978060487c03431b3a743024af543a81f10f1f3ebcb2b23c5c12ece828c6f01d51403d829d1f411c8014040e00320c3806485d77dd +75f7dc730f4e8236235106220462a1413e57ad5a052046a2706b6b2bf5469d78e289bffedd2c649351ab0eb00285efa88d153361a7656610f90d111f68017007 +6f015ea16a1252a4a1fb22170764fefcf3ceafaea806aac0f7052c42e7b8e7ed45ffc2d3fffec2f3b0568d4745d586860f972ec1f060fac70d7013404bc11a24 +283e93cea521e4979797a6d3c96030505e51ea740aefbfff6fb48b162dc290504b1925615a5b5b301ed4d33c806df7087ebd62d78763068e200e003401b8a0dc +d05f116b00891f6237447fd04e5cbaefbefbe0ce05e880b21e7ffcf1bffce52f21554320814404125e1e0c9f75d659502400be1039a2f1281006fd0082d127b5 +b4e27c2c9e04dce311e807fa316ec6cf4f38e1041c5f74f145b1640c6774dd82930b0870fb1fff00c25f555bf3cf975f7efcc92710bd77cfbd7fb9eaeaab870c +1f76eb6db78d1e3716417b53a64e0d0483f563469f72eaa99f6edd7cd96597a2702c2ac8c272b468d13b37dffcab2143eb5009e694534ed27515e5ee801be130 +d806d90fc7e728f6f139ccc09eb228858743db83900d6591e62bd2dcfb036cfbcb23439859794545477b3ba016f49e862d007621d3a3f8ddbab56b41862161af +58b1020aa8e492dbda49bc7479495896b16403a364b24e0f6afd1a91443c180c659534c281f4644676882e9ed75135529412b96c65c01f8fc4828100a36ab168 +07c24bd3861a2e2d85370141757a96d8fbfd6e5f2c1ecda9498fd705772fa4b2e6e6760c3b142c8131094ab320b03945858483d7d43425168fc812ec4bbe741a +05016448619d9ded8899abaeaa5635551444ddd0e142b62b5c30709c29390d358949bdcae2f6c59b017ce8fee4911dd6170015871a802e01f470844164072187 +1002c21f8d44205ba398281584202fe1129800d805649b8ed6f68edd2d20ed5148d96d6d78818e48074dec0439a7a147d4e8849e114c8d83ed9bb62462b100a2 +aced6c9bcd5b88eb003de306ea99c649a0223004d27f7b6707001cf43e9dcd3422bb2716ddb6a311f67f1c744623a83b4c42b565c267207d65b3390854380033 +696c6acc64d22dadcdeded6dd080a1bd607e3399542219b323ed8adb977206580bd5de88b249d28aed953198551b362622ed271c33418bc7e175b57801ab07e0 +3c6f921c4bac21804d71604901949f402432efb09cc4d5cae7484786d35edd4c672c785e35a0010ab777b6b5c209150c87a87d0627a104d3caa1146401fd3104 +bd990c8957b318972cab3985e491f93da9641ca892caa2a8841ff10f8ca2cba2534d67e000b364172af78a88f131741464672da21907c361d5d411611a4f67a0 +b31a0aea5d472acbaba31d6d02191a6144f158d2762638f008c0311cc0b61a026a41b48b6c2e0d4d0308204a9e2e459c0399c7984b4b4b8090c02ebb102a29b4 +6fe8e021c02c093c93b0119886e02fdbbbc53cd17f3dafdaebe116d722392c3350403ec416e78f51338584c873585c034555f0e911402389ee683a75ff93f3ca +2aaba68c1d3772e8105055d6422e0abe99c754b180a6e5c0a218efacdbe850a3278e08329914e31ecc5802b2009063cc0a7e06700e3fa9e060fc06e344a96a00 +a58389bb192c3120771a86ceab65a422bd8084801439600d54fb254bbda2f835a2cd00c4845e5a065c5d08c801d0b9485ab0892aa288c3a1a2448fb821a8bd76 +4c5bd78b156ee0181e3047ffa4656068c60f83480a26996644c97eb4c060094e94d60686e9081d059ed9c10e50aff1745cc2d85219c69682304bf955697112f9 +ca4405b7e5c0031b3d69cc9cbd4e63af38807201f9f33dd732441e423106fb30b10e1a1249bc9cf86a76d574536034ac03c831887f07401a49583b50ba5689a5 +6f7af2a9924175a78c1937a60eda1d70200240d14d2f93e31997062acfff6bdd96323e3db67d0dd3d262b035114d488b26ef960496d7da53b572290aa667c296 +e9b152c90e0fe7e713253c8293bd1daaa57169bfc34475c614cb6659ac2980e005d497b3e51622939140520b2904345718b41357212681fa220e87238bd2f452 +2a627f2617205617745248caa30aa23fed482193ac0462c22814c82235269b0d867ca95402e171d046e0c1869c84750c106e8a36035e8034650c4210c0bf80ef +d0d793745df27de8fa7ecfeca9d79dffaef9e589ba5528eb0ef3f940d6c304035ff16e6ca80755331c0cbe1a3e217435415650990af2026b39b464ba0560e7e1 +7cdee34efcddd3cf089555274d98d83084e2808a45f6b0560c8120116bcde8cca71b9ac2d1e6b5b75d1f5db18a0b3474ea822ef3aa0822af409f752534535338 +1f975623e5156e431132512f2b7a348f62f00a9bd3cdac8e95770064390560c88a58394392a8bf0cd206f041d13540242c3644a2b0b102aa3096c014396498f5 +a27fd372c13d3756e778d05dcaf8f6cafc84d68b6aaa6d6d1d489dc14fedc4350eab1e74243b049753e01db0ff001601f59aa2f282c35e75534025187a8cf368 +b1089bcc0b5d8befa27f928676b0b6e71ae6f9e0bc7d8c0a76b748d901f3393c4b3e7fc51180b0709653099135090e403826861a08cd324e29bc2e7b78cb885a +d99c6c4ae7ff75ee8b2fbf6a0c1e346aea84ba11a4b60aa4687c5a3d6be793490407d88d2bb78c72f25b675e154867423ff915e309339561468b33568291c05f +ec842f2bcb64a20c3e229caa7c396321965323eb8d8113692aa30162442650c64493843d494e2208c1ff0a394414b0d8379381f51d8bb6016fe9a27ff4c01648 +f65db312bdf6b27ea2c1702a513c0a3890a7b126a319e411c92c83225f0e2793406552883d78b50c13f0100129932137402a23d58639781c88508403083f1806 +cadae1cf9cca2890e8fa035d34e3b4c0370a380386d8037ff2cb5ed99cba4fd8d5170cfc6adf834f49644ec003a45c8d883d908554997c0e23c3586926dbbef3 +d55796bff5d18ca7ffbee099e7723555a3a64daa1d69e34073d36e8bd3730ea29f39c1424ce7eaf5db8e2f096c9c751d978c4dbcff49f2f96a4a1815ba81c0c4 +53962527e3692697f455d6300acbf883442487a8ad031b0ca203e007282da49b8c2110fd04a1ce28b58b0d25ede8a2b01d1d4c30408e53290276803908dfb020 +9190fdde640f74d29bae493acc4bea7b277ca22b978b6802a92c23c94419806e904d313ee400606c1a41038c0ab386a7630f85ec357800be7811fb120e6086f2 +05fa8301f4de5e74e23dfa40f7b7d8d335d5698aedc06680901580998d03bc4660036c16fa004c110e8dc97632b2f1fefd73de79ee95994ffde3c117ff290eaa +9938ae7ef8302a0b412ee674cdd62801a4089e5fb9615b83a1af9f7955ac75f7d96ffc2b0571c9e3482277259dd31979e1fb6b810b21a6039691a993cfcc699c +c34bd6de74191c5810546742d37514d8cd8982dbe5f643128f600531644bcac43340eda4488287388464750476a2da28c421e2e722320fd63de020c6776f5103 +bbc719fb2ac6eae8b2ab9002a9a8946d5b184cb72447e2099fdb4bf2f41518894c483f0888266b04668919cae912b0ec0eac3ad4ec43b26492498c0ae7736915 +6650924d0f710ac216d863dfd663b6411accb827d69071da4bf752eb47b70d122c5dedb6b81d861980d192379d28130d45d4e0350535082d87539301d0524054 +a22d4e3fffea3db33ffcc7c2598f3e3dfb85979c355553278c1d493c9ea833677f36a23a77d5dc45c90625929073a617c209c7686ee9c34f362cfa78ed87db3a +9637669a145f876205fcb178cbc2d66d6f2f7eebd96d9fae85ae0935941724cd92545e329d2e0ec64a2c4acb43ce57648f585e55e286fcef7196948534534168 +b3c5ea8a91a52d760149ed82c98b2607fdbb2fadc070288d2d3838c1415aa8b9c831c031725c7862e885802fc91c0afa064222f406f29a9ac5aa161e0289514b +293e1981d06e86f8d27418c41c70a6a52d2b67f85d5eb75b0653256ababd44675f5a7b254f72678fbdc7cfe99a9f6427c386f4475eb6b81f8e19609c3c23f144 +e6151d1c3e06cceca2839830b564dc52338c9691b59c5787c6ac86f4ac5f53a1fc51e42309241029f03f2c2009e51122aaa89a52ce28b5c490e964b2a800a745 +da3b4dd6b53dc13ef8fc3b52d9b055eb569b998fa78f33734daf8fa9cd6d5af36e4bd3ce2ce8a7ce263505c1ca1a0b4ace133f168aa0381ca140b0ada5f577b3 +668d1f3b76f8b0a1d7fff4a79f7cb2018169b057e247419fdfe7f6246351cbd40953ea630bfa0ed706bcb396d2d5aaa6950314271211c9c9c193f5e69bef3cf4 +d77b51d5d4215870446494042804eb30b27a5a3532592dfdfea2453fbaf2d2742ece8826cea7d4846ee5727ae65f6fbef9932b2f43cf8ca9f4b145210bc6cab1 +b0309b2a5697a22d76cbc4a8f66af127d96dbff21730d2f8cb3b24cc2601087bc797a3ad8e3f0d5d7239b17eb0ece05828aba6eed414d1d2b02677170e4079c0 +21d8b2bd13fa975402a2af2ceba8d19d4c4c2b355cfa8e68995439ffadd5279cf7c3c6f67836de342414ab1e9d1bec5969b6bf565f05b21ac1d2f6c99cc5c20e +e5844484faa69693978dac26320e2595ab2cafdcb4e693d97fb86bdb96add964f695975e3614bd2c1082074e02e5cfe9616fd82d7abc4e941dc292686467354b +4de52cc5700b72105e5d4f0057f58cea97bdf6312417cbe7b294745bc0c339f95cc0c306dc9cc8e55c4e15c783aa02029bd9ba6995a5c5fc6e26147606ca64c6 +a35b2ee41368812a4fd28a1e75c284b9cf3fe20830d859b721851d9a332bf8d871c7363cf58f472c36e196549fdb40ebf798e79e73d2f2256f06bd8c966b0b79 +59ec9290c5251cf04cc22b9bb7ccbcfe85e71ee3cc24a3c7452eebf7f17e0fcb5b294389b844cd25e9012f27f2395d8968b94e098e47dd84f256dc0fcb0c5806 +b1ce6459ceded90c76142a472221cbe9a240dcb728bb836c41f005e225302d634f600b3ff3965b0c0612b3adcf11f9984f75a483394d7bfb0d946ff07dff12c4 +27bcbbf8fdcae1e3566f8198ad44776f28119ba60dd7f4ed6b4b07d5ef6e530d579d66852a4a4780fe239407be280e3d2ae4ffb03271a603457e44deb570e15b +43eb468c6ea8dfbc713be270ce38fd94575e7933e00d035b967eb4d2ef0bac5ef5310aa5cc7de48975eb3ea92aab865db5bcac1cfeb6c61dbb67cffecbe2773f +4c6772583f67f1e2a58fff6deed265cb870d1982b89d9250c9eee6ddcf3df3ecdc27e622297ef0e0214e87b0f0cdb7fffeecdf9b9a5a766cdd79e185df7be985 +1751a4e88187ff5a5357bbbba5fdb1b98f3735b78e193b0619c20b5e5b307aec84856f2e6c6c6ec5f9442a0b51a8a4b4e2a9794f8d19357af3a62d8ffced910f +3ef8102b853ffffc8bc94452965daaa2bebff883b7df7a7bf4e831f7fef9de175e7c311c0ca3c0d1230f3ebc6ac5ea8ab28af1938e5afee19227e63eb16ac5aa +d10da3bd6eefab0b5e5bb17cc5271b37959596a3a65720108cc7530e0efe41ba044a711ff00c58a2c10a26680e6bea3ca7227a811139d8dbe1976258ded405ce +dcb16cd9ee759f1e3be33fd7af59c77afca535358130c930817d110804f7140ce9046855586b9c96c26b7127d3c6ab8c474c4a6c87038e2d5edfb8815dfb41c7 +ba975a9a37c6ddc737f9bff761647293ebd80817d259d0729856e1656324453415de3450a587d40182138ac1698d696d4bcd99f3f885175d01927be9a5576ddf +96b8fcf26b712910705f70c1a5089578fbed0fcf3befe2f68eccb3cfcebfeffec7cacb4bb66e694f26e107f056540c714aa1fbee7b2416311e7ef8a940b01a8a +b42094706cb0bd5d4b25915d3978ead1675c7fddefd229f1c9275efddd6fefd334effbefad77b9aab0ecd33557dff2fb5b1f90d8d24bfee3cac7e6fcdd48ca77 +cc9af3ca738be22dc6b597ff0aedac9beeba75e63d3e47d5cd3ffbc3fa653bd72fdff9bb99f72819f9eebb1ed3144f457903f41dd3c02a9a81b2d2514f3cfef2 +7df7ce939d55b1285b5d39f698a967def8b3db1b1b53b1185b53353a95e03e7cf7e39933ef4cc4d9e5cbb65c73cdaf39ce33fbee47e7cf7f179ec8b28a5a960d +a6d358b133689ab269c24e50dc0fcb0c88ac4e5693640d89d5254e7782fe13fb10a830e277748931645e93e0ea6274090e03d6468fbc2c8454767005b4363290 +83a8924941c470b058391eca6426a75456d598c9e8d9a3cbffef6923effb9fffbce8bb67df34fbcd9be7b6def972e2b965d90ed32d7a5da83d05d483db004fb5 +652b03619bb0ab2473108a35979f29ad2c6d183f7ad498511b366fa8aa0b892e67755d4d22938c24cc5163ea11d421fb5c53a71d7bc75dbfb9eaa7d76edeba39 +95639c2ea7272057d6942c59b1fca1bf3dd416e9cc2899956b3e4e6553bffcf58d9178daed93549dafa81edcb8bbf3ae7b1ed8b1abad3d92deb071fb8dff7dcb +4faffff985175fd6d6918481b4ba76f8a5975df9df37fed42306a64d39fe7f66de74dcd127346d6b1519d7a821639cacbba66cf0a517fff89a2baefec6a9df6e +ddd5c91bcec913be06f1ebbd0f960d19d1f0adff73fed9e79c55525ef3e32baf69183b22ad18279ff68d6fcd385f90bcef2e5e72fb1fef5eb7f1d3b1e3274c3d +f6c463a69d78c1f7cefdf707cbc64d9a3aebb65fff65ce9cce68babd53e104f7f77ef8e36f9ef3cdce1823b844c5e45493d5e031840d8d25de94623bc01900a4 +51b326f080b3004d1631715ac465e580c51ee23ef913966b628b43f5045b03e8d20700f454172858e948228b03d53b0dafdbc3a8a633a94f19549ff974c34943 +99a9356d52fafd8a72be951dd5199aa1d57d375d727c12fee71208f3f06e195a2e4b54115ed5f88c29652dc9d01c49cead2252a733bb7bfa6947df78cbb5a16a +f70b0b5e9482e039097f85d313e43a334d592b9ad22343c754c5b58c256553666742ebe43c5a4b7cf7453fbae0d46f7d7dc9da7feb42ca706616af787bd4a4c1 +a3278f3304a3a923152877df75ef9fd76c5affcee2774e3aeb3453605be300bc38ef028bf138dc12248e949e1d3464101c12a5017f6d7939e62980008d740aea +73361187f32e138f8dafaf97100625cb3b3fdde293a4e6a65dedb1cc072b3e3278ebf8d34edcbebb837709193d07ab120b96c89b8152f1b1a7e7ca7ef75befbd +3dedc4af7724931aa7b5c65ae12d6451eb113e6c81496b6ab8ba440c383b3391f241e5828b49aa498c4765f192194c91c6a73547bad80e7c06743e65b119864d +c1e5c9b171b4bc95e489e20bbf6f86877bd74a101d01f77029934b1a5c660f0e4052c16e8b63f8bec4774f8a3838250d8671d896a041e8cce08ada74a2b3bc9a +dfba63f1f8e115915dbb42deba41b553962dd930fd6b2795b8cbc2be001c6d9a90d2b99cc919084585ef1ab926307dc0eb46ea45a711318d52d3cdb0a2368c1e +f1e4537393a9687dfdc81b7e76eded77dcbe75fbe67426897bb00c993d321301c9c04444a356955534348c7af8e1871e7ae80112c89c4ddf71e71f162f7e6ffa +f4e322918e70892799cad50daed9b173eb7df7ddbb68d15be06a679c71da9feefae3acdffee18f77dc861a8a704fc7629d887606226672e9782a928a6271da2c +aa5e0b129748c5dd3e278a636fd8b41653b0a3716b4959d8e16463f14e54b178f1a57fac5db77af88821a293f778e5bb67dff9e1d2d5ba812010359e5405915b +bf61cd9c07eefbf77bffc2c970896fcbe6f54f3df9c2c449633e5eb3f2d6dbeebce2bf2e1f5457e9f530996c7ce3c6f5ed915449a917d584e18a83146a592494 +0f0cb3d80e7c06e0e3d459940a4469411d538a504df868115c8f1dca29ad216d8046435de510f503b8845b2c4ff6f9db7f730bc28ee1a0420c8dc3f6e536b745 +7d582ffe8345394dad3cef8204c2693cee8419dd99dea44a4ab43d2a33b56b576b6a5bf4fc13467812db8e1b545be1f682d66611b2213b50a930a303a19c4e83 +5392393583fa6da24b74073ca1496327d755d50eaf1b595e5aa566f4d34e3e231dcbf8dc81cb7e70f9d886f10dc3c70caa1a5c5739381c280b79c2f523c7065c +fea6c6d6e18346b8657f369ebbf03fbf37ac7678ebee0e8811b5d575df3af32c04f6e08d42415fd0eb4ec4a33fbbf6dab2d2e088c1832b50e43d93f9e6d9df98 +327162fdc82140a4134f380e6eaa605960f4843181d260a83c14ae08fb4bfcf5e3eaab07d7d40ead1d3b691c8a4296d754e092bf2430e1a88965e5e1f92ffea3 +b2ace4f8af4f9b3076f4d8d1f508c01ed3306afc9886badaeae143ea06d55401b94ac3c1ef9c3763d4b02193468f6e6d6a449aff49274eaf1f31aca5b971e8e0 +41d75e7d2542b5070faa39e984e988cb329107a4666143835d82b3041e52a9096dadb80f740648f43febd078e25eb5854c0911c2bc8920650035e424d8c7b92d +cb576cff64dbf4ffb8f0a3b59f98fe6079756d304474623b7f8020841df70b4312c3bfb57cc548c53066df8fda23473f376f979a83655265632f2ef86b6d95db +adaa4b1637addc5a7ad25917315ad3f09ac0d7c78e8747b529d3c1791c7e8f3f9bb3e229c46a882ec670c1326f11dbbc0b350a7d7c34aa228213516b58a81825 +71699c04e8743aab3b9d0e14a6867e9c4ac0c2ca047c522aa3a3f02df125c17bc1b15e378b109e78345556e1c921d8d9c5c46359a0b928f0300043c7945c120c +ee48910103c3ea96b2d399c9aa6e196bfea5827e4f124e6e0bb99d6e44ff234cb5a4240c5d1d03c0b15d0ad21b89906278f0f4c18f6c9740850b45474d169014 +444e207f1f852410818e4b342f87e6e8949585e2f1b4dfefc6c02004795c6ee4ba610ca4e4911b15809d4d4dad48912309cd1dedf815bce889740a8e1197cb8b +c0ae7cedde5e2341fa1eaf5abcd38e2dd040c8595db0341ceb2cd4594156113fccc9d08eb534ef3416cdb9ffdd175fbdf999790f3ef5a4a3baf2a82953e97a64 +8895405833099721093158029531dfddb5bea62569dc703f9f55c62e98d3e4c8fa453983423d0cb37af56a2595aaabab57add08e9dcd1eb763f284b1326cae8c +099d06ca881d1140dcd4f0be11b6b38f179cb09f6e81a0345a81b6b4dc2211803022fc8950537032886a0ea2d2f4161106cff8417350087ed3df92ed88953fc9 +8749db4ef71e41d150aeba4f038d0db2efa3d333b0389962ac913d03441e8019882531cb85a41c24cf20c94548a6854c8ef10696ccb9fff5d75fbff9ff3df1d8 +d38f1ba1c09469c70f1d36c2c601c09c0d21b671020147fa9b3b568e4f3bd52be6b0a9ccd097ef48c9190e2b88e9080240791276cb96ad585c1eb74f9e3c71c4 +8851e033a857db5b5cb1dd656f5b010528e81770607f79025ff6920d074dc8ee75968a27fb350390f8e174045d41600ca574a08f38893f01bd240f4c0a6cfceb +43cf2f5cf0dfcf3e3aefd9c78da06fe2b1c70f1e3e8ae000c409fb7e42ca092e38f4852bde3b962f495efd802391297de216a6c6c364814d1293d3989c028703 +436ca6c80b43482a5c5824166d3f38b01f34a0f90085cc925e0f4800475788d9fecbf1f66b9a3eb79bf75f52fb731bd2ffce072369ab2bcd90c4a4db2b756143 +fc7cce603a125be63e3affdf6f5fffcc232fbef0345f593664c25175140774db92ba27789833977dbc64545668be614ee3ea75cc99139b652dd5dae9710ab027 +eab9340018c16978826df3c1da19029cbefbc7815e269bd2f51e4c8088065de749b64dd74670b3d71c9a2fcf573c4049ed2fcf4b7cf1474a12c760d681040e5f +81ca9bc87381c3976c59d52b4821a7bbada3bd4d4b5ef5b7fb9e7eee71aeac6ccc315fcfe300d21c89b88d4c1a227b13fd78e7b64da1f68cf6e85bbb56afdbec +ca657db0e321f9ca4ac6dba1e4a1a43992e501c3a8708e7c45d45940f8c07e22e07b9fb8ee3850c084eeb0de5d4920ace460c55dbee0dfa788039fc1076211f1 +69c0c243283f647ac44e030180069070100c6ae654d16239d9a979a48bafbbf2de17e6950e1b3666fcd4a1c30a7c007214c101523a021ce45f6fbf31d21daa72 +d532ee00e302d927349ae96c674a834c369dcfb4c239646391fc18a79d7bdeeb76b0e0f81e5290bd6e4dcf9df032ba94d897762bca429fcda723be6242338995 +81243bc26042ed0f0e02de24a81ec8916542eebb67ffc1555272cc94e9c386da3840a832c83ae40ddc8da018d64a696931a7caac97c920b75025211030376201 +98ac8a1ad220cb489227b9c14847c43a4876c6c97e7160ff76983d1ca08009c826c36020fd633060317675200c0f26cbcf660e8fd053baf3ba23f48862b78063 +3be8c7ae0c4252b1a0db921a0ea41a10ac942a2e415b304c518819cabd73ff5652517dec9463870fa57621e49b101c204c00da3309bc2690a8fa803d08b6c672 +8eac9956d2502c104593cb2252135148a8d7429469acf782da3a9a8aa0d47d2c7cc89d42d7a42599cebdb66490f655944941eb923d346712c57c0c24ee1b2814 +04f90c58b9df1e0edcff17e42a527bbe2023f9b2cfe4816009351658c823866d0ecadb9d898004c056498c3abe0259d1abb424198fcc79f451d12d9f78dc095d +fe0138ec894d89e000128073f039db59b1e40443fc6c0830224b3a3202b2637883045917201e98401471506bca736c8a87637a5e5333be802f9d4ccb6e19eb1a +2d5bb1ececb34e6f6d8f949584304ab0a91e9883bf7be4d39371b090bf14e00a8ebbff23d863e350dfff75bf9f1ed3b6d06f8f63a7e0ecb5775dd5e979c275ed +a37d47628f9cfcc398915fd78f51eeff7de001dcf789a4bc81ed49e9fe44dac7e17a2eedadc72ce18cec94f7fe26f60acfacc59300e67efc4312207e49e78af6 +405b4333686ff41fde94b678dfeef7d36378087887de116d45000182612451863401d0f739dd08e0274962c081ce4e41944c9ebde38f7faa1a3c68d2d4c98347 +901a5bf95c4a2a48117facddda95e258449ec2ba4a788aad13b8b0188b2460ed6a92ba886e1186a1c3458b9c29b85249b01e5a1ce33c3d837588b1f400b2dac0 +28562c5bfacff9f3d7affda4bc344485b47d5b486b3889b6c75552e0190b6583e9606154e42b430c23e8826f8c458df9beb730fe9294e5de5a2240224a842339 +782831842722d1797ff7179e8891d0d182e3613c64fcf6d8686ff65b90d264f4ccc0db429f3d7ac39c60e418155a7a4cdb813fb1f02e788bee4fa13380700f3a +9f7877ccc021bf23eda1d00fed0d2dbe38be059e4508ab4104100d691c2850d2f5050b77128903157a9259c9e95dbc748543f0e414d323057dee10c9ac179c24 +22da64849230e37227928ae890dcb0f5776d76ac042dd243f401481ef9ac3262fa614c587cc8006c2a4f4ca8645952301cb214b68a29c0c02ce4df900c77ea9c +2be48fc3e0ea92e4643a89332e448df18e487b5ba8140b22017cf35ce3401e52b016ca53f01fc9f4245c0ec13678a28629c312f616826cf83ef889f7d433c4e2 +de3dc649478b16a08a1648807721a88063844fe984b1ee5b09104fc74723ab5912498d1c93b1c153d26d3cb88a33b4edef38f7577b903e055749e1826eb35d98 +738c849ea7cfed9ecb3f907a86fbeb87cee79e2fbef7a8fafec4c27bf5986d0a5778173adbb4dd773ebbde9af5fa4bb76ef9745843fdca356b1a1aea77ef6c1c +525343e47ca8930870c18a76481446fc727bfad57fbc084965e23113281fc09726ac27bfe16eea98c2d3e821410f3bda80c61411fba91d79003e055e4508ab8e +d6967decda0edd6ae9a0642716e0c092a9301c218a86acf825cb58001e8b87d98fe87281ed4fa12b98530a77d20a287611146c74d9edbe6f7471037a7ff783bc +ce4dbd7204febbdc73fb1b210d2182c20eaba7bd51ddbd601ba0c6df827bfb70d976a99f64df57e8feacee3ef5c3f55cba0ac4be8fa66b2e162ef5b8a7efdfa5 +603beed1433e76a6ebd18517effd7ecb118fe54265158d1dadc1d212dc13f6bbd5680ed54c4841051ed6523b6f00606a30ab162f5bb974c9f493a70f1d4e7100 +5ede42080b2d0d420930450c5bc9b6eb46e9c88e213b412b5b4e074a401243ae0ccc47797a9aa70b3412068bdbed9908446474555c440d883ee180fd6df7f4b0 +0f4466519ea83f1b82e1ba23c01e734d77bb137de281ad9944ca20385040008a03b4c30214160e0e975d68df0e0b7156fb3e94d4b484e1ee706c8829eceeb5a4 +c73d3a1ec83b1660bd8fbe51bb66c33e9be59021f9b0a4226ed6406166074423270aa679650277120b3f2e519919c3cdf0d1e6e6b716bc7ed45193ba74627cbe +ae37ca5b32290ed09c63802bfeb4e32f6c05614fc457611614254f8ff7225160cb3624894e5264d796990d2ca10d95628f84d3b7782f9354c2435a289156badb +9fe8f9bec79cc1d1d7a7fb8970095046658c03de6f6301801f2dbd1ffca00b05f29ca0ef63ebcb9d94bb74a3bcf9d9b04d12f97928dc83f1407bead3fb1e6c0e +f1d56cd648ac81f80a36b3041d048e6165f5fc5cd944207f4c34a083f5b9d77784f9b0b7fee18da54b43a296b36d2a47e108f214da7f6124c446c96240bcae5a +b2d7adc249dc95a08d4a21c8128145d41039948a86260101c68b28eb4472d3daf5a2cb3d74b81d378a58096a16c246e906fe24b933b64280aad2e8315f171332 +8ffd05bac7f290fb2116f62263e4a52a304c1012ac5c44bdbd998c2acba4ecdc4145a1028e11d7858da57dffc9e1207f07eaa33bb728f00cd4edcd633bad5796 +6709fdc0d20363021e44e502f4dc1dce008bdd9f65c7da927b742493f40716f7f774401e857b8a03b4b571c0169649c60ac17f0aa3a008f46a5fb0baf02e051c +20560f02d379b194905ee22b021a90fe491d65fbedf6b91f3fb2eb4500016c8147d1544e55bd440340a929a4101839d455215f449550d95663b5786a4b7373ed48e21ff8ffc69d1f5f9273342a0000000049454e44ae426082}}{\nonshppict +{\pict\picscalex100\picscaley101\piccropl0\piccropr0\piccropt0\piccropb0\picw6802\pich1879\picwgoal3856\pichgoal1065\wmetafile8\bliptag1748430998\blipupi95{\*\blipuid 6836f096d239be683c305f9b4ee3f665} +010009000003586b000000002f6b000000000400000003010800050000000b0200000000050000000c0248000201030000001e00040000000701040004000000 +070104002f6b0000410b2000cc004700010100000000470001010000000028000000010100004700000001001800000000001cd6000000000000000000000000 +000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffff7f7fff7fffff7f7fff7f7fff7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7 +f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7fff7f7ff +f7f7f7f7f7fff7f7fff7fffffffffff7f7fffffffff7f7fffffffff7f7fffffff7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7 +f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7eff7fff7f7f7f7f7fff7fffff7f7ffffffffffffffffff +fffffffffffffffffffffffffff7f7fff7fff7f7f7f7f7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7ff +f7f7f7f7f7fff7f7fff7f7fff7fffff7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff7f7f7fff7f7f7f7f7fff7f7f7f7 +f7fff7fffff7fffffffffffffffffffffffffffffffffffffffffffffffff7fffff7fff7fffffffffffffffffffffffff7fffff7f7fff7f7fffff7f7ffefefff +ceced6ffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffff7fffff7fffff7fffff7fffffffffff7fffffffffff7fffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffff7fffffffffff7fffff7fffff7f7f7f7ffffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7 +ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7f7eff7f7 +f7fffff7f7f7f7ffffeff7f7f7f7ffeff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffef +f7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7eff7f7efefefeff7f7eff7f7f7f7ffeff7f7f7ff +fff7f7f7fffffff7fffffffffff7f7fff7fffff7f7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7f7ffffeff7f7f7f7ffeff7f7 +f7ffffeff7f7f7f7ffeff7f7f7f7f7eff7f7f7f7fff7f7f7fffffff7fffffffffff7fffffffffffffffffffffff7fffff7ffffeff7f7f7f7f7efeff7f7f7f7ef +f7f7f7f7f7eff7f7f7fffff7fffffffffff7fffffff7fffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffff7ffffefffffeffffff7f7f7 +f7c6c6ced6d6def7f7f7ffffff00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffff7fffff7ff +fffffffff7fffff7fffff7fffff7ffffeff7f7eff7ffeff7f7efffffeff7f7efffffeff7fff7fffff7fffffffffff7fffffffffff7fffff7fffff7fffff7ffff +f7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7ffffff +fffff7ffffffffffeff7f7eff7ffeff7f7efffffeff7f7eff7ffeff7f7efffffeff7f7eff7ffeff7f7efffffeff7f7eff7ffeff7f7effffff7fffff7fffff7ff +fff7fffff7fffff7fffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7fffffffffff7ffffffffff +f7fffffffffff7fffff7ffffeff7ffefffffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7f7eff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7ef +f7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffe7f7f7eff7f7eff7 +f7eff7ffeff7f7eff7ffeff7f7eff7ffe7eff7eff7f7e7eff7eff7f7e7eff7eff7f7e7eff7eff7f7eff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ff +eff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffe7efefe7eff7e7eff7eff7f7e7eff7ef +f7f7eff7f7eff7ffe7f7f7eff7f7eff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7 +f7eff7ffeff7f7eff7ffeff7f7eff7ffe7eff7eff7f7e7f7f7eff7ffeff7f7eff7ffeff7f7efffffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ffeff7f7eff7ff +e7efefeff7f7e7eff7eff7f7e7f7f7eff7ffeff7f7f7f7fffffffffffffffffffffffff7fffff7fff7f7f7f7f7fffffffff7fffffffffffff7ffffefffffe7ff +fff7e7e7e7a5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffff7f7fff7ffffeff7ffeff7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ffeff7ffdee7eff7f7fffffffff7fffff7ffffeff7ffffff +fff7f7fffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffff7ffff +fffffff7fffff7fffff7ffffeff7ffefefffeff7ffeff7ffeff7ffefefffeff7ffeff7ffeff7ffefefffeff7ffeff7ffeff7ffefefffeff7ffeff7ffeff7ffef +f7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7fffff7fffff7fffff7fffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffff7fffff7fffff7f7fff7f7ffeff7ffeff7ffe7eff7eff7ffeff7ffeff7ffeff7ffeff7ffefefffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ff +eff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffef +efffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffefefffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7 +ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffefefffeff7ffeff7ff +eff7ffeff7ffeff7ffeff7ffeff7ffe7eff7eff7ffefefffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffef +f7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffe7eff7eff7ffeff7ffeff7ffeff7ffeff7ffeff7ffeff7ffe7eff7eff7ffefefffeff7ffeff7ffeff7 +ffeff7ffeff7ffe7eff7eff7ffefefffeff7ffefefffeff7ffefefffefe7effff7ffefe7e7fff7f7fff7f7fffffffff7ffffffffffffffffffffffffffffffff +fffffffffff7d6cece9c9494ded6d6f7f7f7ffffff00fffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffff +fffffffffffffffffffffffff7f7fff7f7ffefeff7efeff7efeff7f7f7ffefeff7f7f7f7efeff7f7f7ffefeff7f7f7fff7ffffeff7f7efeff7ffffffffffffef +eff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffff7f7fff7f7ffefeff7eff7f7eff7fff7f7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ff +efeff7eff7f7efeff7eff7f7efeff7eff7ffefeff7eff7ffeff7fff7f7ffeff7fff7fffff7f7fffffffff7fffffffffff7f7fffffffff7f7fff7fffff7f7ffff +fffff7f7fff7fffff7f7fff7ffffeff7fff7f7ffefeff7eff7ffefeff7eff7f7efeff7f7f7ffefeff7eff7ffefeff7eff7ffefeff7eff7f7eff7fff7f7ffefef +f7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ff +efeff7eff7ffefeff7eff7ffefeff7f7f7ffefeff7f7f7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ffefeff7eff7ffeff7fff7f7ffefeff7f7f7ffeff7fff7 +f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffeff7 +fff7f7ffeff7fff7f7ffeff7fff7f7ffefeff7eff7f7efeff7eff7f7efeff7f7f7ffefeff7eff7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ff +eff7fff7f7ffefeff7f7f7ffeff7fff7f7ffefeff7f7f7ffefeff7eff7ffefeff7eff7f7efeff7eff7ffefeff7eff7f7eff7fff7f7ffefeff7eff7f7efeff7ef +f7f7e7eff7efeff7efeff7eff7ffefeff7eff7f7efeff7eff7f7efeff7eff7f7ffffffe7dee7000000deded6ffffffefe7e7fffffffffffffffffffffffffff7 +ffffffffffffffffeff7a594a5a59ca5d6ced6ffffffffffff00fffffffffffffffffffffffffffffffffff7fffffffffff7ffffefffffefffffffffffffffff +f7fffff7fffffffffffffffffffff7f7fffffffff7f7fff7f7f7f7effff7f7fff7effff7f7fff7effff7f7fff7f7fff7f7fff7f7efefeff7f7effffffffff7f7 +bdbdbd6b6b63dededeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffff7f7fffffff7f7f7fff7f7f7f7effffffffff7f7fffff7fff7f7fffffffff7f7fffff7fff7f7fffffffff7f7fffff7fff7f7fffffffff7f7ffff +f7fff7f7fff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7f7fff7f7f7f7f7fffffffff7f7fffffffff7f7fff7f7f7f7f7fff7f7f7f7ef +fff7f7f7f7f7fff7f7f7f7effff7f7f7f7f7fff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7f7fff7f7f7f7effff7f7f7f7effff7f7f7f7effffff7ff +f7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7 +f7f7f7f7fff7f7f7f7effff7f7f7f7effff7f7f7f7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7effff7f7f7f7effff7f7f7f7effffff7fff7f7fff7f7f7f7f7 +fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7ffffffff +f7f7fffff7f7f7f7fffff7f7f7f7fff7f7f7f7effffffffff7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7effffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7 +f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffff7fff7f7fff7f7f7f7f7fffffffff7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7effffff7fff7f7fff7f7f7f7ef +fff7f7f7f7effff7f7f7f7effffffffff7f7fff7f7f7f7f7fff7f7f7f7effff7f7f7f7efefefefe7efe7ced6c6f7ffeff7ffefffffffffffffffffffffffffff +f7fffffffffffffffffffff7e7f7948494ad9cadded6def7f7f7ffffff00fffffffffffffff7fffffffffffffffffffffffff7fffff7ffffeffffff7ffffffff +fffff7fff7fffff7f7f7effff7f7fff7effff7f7fff7effff7f7fff7f7fff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7f7fff7f7f7efeffffffff7f7 +efffffffefefe7bdbdb5fffffff7efefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fff7f7fffff7fff7f7fff7f7f7f7eff7f7eff7efeff7f7eff7efeff7f7eff7f7effff7f7f7efeff7f7eff7f7effff7f7f7efeff7f7eff7f7effff7f7f7efeff7 +f7eff7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7fff7f7fffff7f7f7effff7f7f7f7effff7f7f7efeff7f7eff7efeff7f7eff7f7effff7f7fff7 +f7fffff7f7f7effff7f7fff7f7fffff7f7efeff7f7eff7efeff7f7eff7efeff7f7eff7f7effff7f7f7efeff7f7eff7f7effff7f7f7efeff7f7eff7efeff7f7ef +f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7efff +f7f7f7f7effff7f7f7efeff7f7eff7f7effff7f7f7f7effff7f7f7f7effff7f7f7efeff7f7eff7efeff7f7eff7efeff7f7eff7efeff7f7eff7f7effff7f7f7f7 +effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7 +f7f7effff7f7f7f7effff7f7f7efeff7f7eff7efeff7f7effff7f7fffff7fff7f7fffff7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7efff +f7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7fff7f7fffff7fff7f7fffff7f7f7effff7f7f7f7effff7f7f7efeff7f7eff7ef +effff7f7f7f7effffff7fff7f7fffff7fff7f7fffff7fff7f7fffff7f7f7effff7f7f7f7eff7f7f7eff7f7deefe7f7ffffefffefefffeff7fff7eff7f7ffffff +fff7fffffffffffffff7effff7effffff7ff847b8ca59ca5d6ced6f7f7f7ffffff00fffffffffffffffffffffffffffffffffffffffffff7fff7fffffff7ffff +fffffff7fffff7fffff7fff7fffffff7f7f7f7f7f7f7eff7fff7f7f7efeffff7fffffffffffffffff7f7fffffffff7f7fffffffff7f7fffffffff7f7fffffff7 +f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7fffffffffffffffffffffffffffffffffff7f7f7fff7 +fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7 +f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7 +f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7 +fff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7 +fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7 +f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7ffff +fff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7 +fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7 +f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7efffffe7f7ffefffffeffff7efffffeff7f7efff +ffeff7ffeff7ffefeffff7f7ffefe7ffffffffefeff78c9494949494d6d6d6f7f7f7ffffff00fffffffffffffffffffffffffffffffffffff7fff7eff7fff7f7 +fff7efffefe7ffefefffe7efffe7efffdedeffefefffefe7ffe7e7ffe7e7ffefefffefe7ffe7e7ffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefff +e7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffeff7fff7f7ffefefffefefffe7efffe7 +efffdee7ffe7e7ffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7 +ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efff +e7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7 +efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7 +ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efff +e7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7 +efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7 +ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efff +e7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffe7e7ffe7efffdee7ffdeefffdee7ffdeefffdeefffe7 +efffdeefffe7efffe7e7ffe7e7ffe7e7ffe7e7ffe7e7ffe7e7f77b84849ca59cced6cef7f7f7ffffff00ffffffffffffffffffffffffffffffffffffbdc6c6ce +ceefefd6fff7deffffeffff7effff7efffded6fff7f7ffe7e7ffe7e7ffefeffff7f7ffe7e7ffefefffefefffefefffe7e7ffefefffe7e7ffefefffe7e7ffefef +ffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7efffefefffe7e7ff +e7efffe7e7ffe7e7ffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7 +e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefef +ffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ffefefffe7e7ff +efefffe7e7ffefefffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7 +e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7ef +ffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ff +efefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7 +e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7ef +ffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefefffe7e7ffe7efffe7e7ffefe7ffe7e7ffefe7ffe7e7ff +efefffefefffefefffefeffff7f7ffefeffff7f7fff7efffefeffff7ffff848c94949c9cd6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffff +d6dee7cecef78463ef5a39d65239ad5a52a55a52ad5a52b54242a55252ad5252ad5252ad4a4aad5252b5524aad4a4aa54a4aad5252ad4a4aad5252b54a4aad52 +52ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252b54a4aad5252b54a4aad5252ad4a4a +ad5252b54a4aad5252b54a4aad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b5 +5252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad52 +52b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252ad5252b55252 +ad5252b55252ad5252b55252ad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b5 +4a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad52 +52ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4a +ad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b5 +4a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad52 +52ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b54a4aad5252ad4a4aad5252b5524ab55a4ab5524a +b55a4ab5524aad5a52a55a529c5a5aa55a529c524aa5524aa55a52ad635aa5d6d6ff737b949ca5adceced6f7f7f7ffffff00ffffffffffffffffffffffffffff +ffffffffced6d6cec6f76339e73108c66b52c6635aa5524a9c524aa5635abd524ab55252b55252b55a52bd524ab55a52bd5a52bd524ab54a4ab55252b5524ab5 +524ab54a4ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524aad5252b5524ab5524ab5524ab55252b5524ab5524ab552 +4ab55252b5524ab55a52b5524ab55a52bd5252b55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52 +bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab5 +5a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b5524ab55a52bd524ab55a52b552 +4ab55a52bd524ab55a52b5524ab55a52bd524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252 +b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5 +524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab552 +4ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252 +b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5 +524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab5524ab5524ab55252b5524ab55a4ab55a +4ab5634abd5a4ab55a4ab55a4aad5a52a55a52a55a4aa55a4aad6b5ac65242ad4a399cdedeff848cad949cadd6d6def7f7f7ffffff00ffffffffffffffffffff +ffffffffffffffffbdcebdd6d6ff6339e76331efded6ffeff7ffe7e7fff7efffefe7ffe7deffe7deffefefffefe7ffefe7ffe7deffefe7ffe7deffefe7ffe7de +ffefe7ffe7deffefe7ffe7deffefe7ffe7deffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffe7deffefe7ffe7deffefe7ffefdeffefe7ffefdeffefe7ff +efe7ffefe7ffefdeffefe7ffefe7fff7e7ffefe7fff7e7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffef +e7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefde +ffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ff +efe7ffefe7ffefdeffefe7ffefe7ffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffef +e7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefde +ffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ff +efdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffef +e7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefde +ffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ff +efdefff7e7ffefdeffefe7ffefdeffefe7ffefe7ffefe7fff7e7fff7e7ffe7ceff8473de39318ce7deff8484ad9ca5b5ceced6f7f7f7ffffff00ffffffffffff +ffffffffffffffffffffffffcee7bdcedee76b42e75a31deefefffdef7f7deefffd6e7ffefefffefe7ffcec6de948ca59c94adcec6def7efffefeffff7efffef +e7ffefe7ffefe7fff7efffefe7ffefe7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7fff7e7 +ffefe7fff7e7ffefe7fff7e7ffefe7fff7e7ffefe7fff7e7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7ff +f7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffef +e7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7 +ffefe7fff7e7ffefe7ffefe7ffefe7fff7e7ffefe7ffefe7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7ff +f7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffef +e7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7 +ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7ff +f7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffef +e7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7ffefe7fff7efffefe7fff7e7 +ffefe7ffefefffe7e7f7efe7ffe7e7ffefefffe7e7ffe7efffe7e7ffe7deffe7d6fff7e7ff8c7bde42398ce7deff848cad8c94a5d6d6def7f7f7ffffff00ffff +ffffffffffffffffffffffffffffffffcee7b5c6ced65a39d65239dee7efffe7f7efeff7f7e7efefdeefe7b5bdbda5b5adced6d6decec6c6b5adc6bdb5f7efef +f7efeffff7f7f7f7effff7eff7f7effff7effff7effff7efffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7efffefeffff7efffefefff +f7efffefeffff7f7f7efefffefeff7efefffefeff7efefffefeff7efefffefeff7efefffefefffefeffff7eff7efefffefefffefeffff7eff7efeffff7eff7ef +effff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efefffefefffefeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7ef +f7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efeffff7eff7efefff +f7eff7efeffff7f7f7efefffeff7f7efeffff7f7f7efefffefeff7efeffff7f7ffefeffff7f7fff7effff7f7ffefeffff7effff7effff7f7ffefeffff7f7ffef +effff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7 +ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefefff +f7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffef +effff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7 +ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefefff +f7f7ffefeffff7eff7f7e7f7ffefeff7eff7f7f7eff7eff7f7eff7f7eff7f7ffe7deffe7deffefe7ff847bce393184e7e7ff8484a5949ca5ceced6f7f7f7ffff +ff00ffffffffffffffffffffffffffffffffffffd6d6bdcec6e7635ae74a42cef7efffe7e7e7fffff7eff7ef84b59c8cc6b58cc6c66b848c846b5adead9cc6ad +a5d6c6c6fff7fffff7ffefefefeff7e7ffffffffffeffff7e7ffefe7fff7effff7f7fff7fff7e7effff7f7fffffffff7f7f7e7e7ffefeffff7f7fffffffff7f7 +fffffffffffffff7f7fffff7ffefeffffffffff7f7ffefeffffffffff7f7fff7f7fff7f7fff7fffff7f7ffffffffffffffefeffffffffffffffffffffff7f7f7 +efeffffffff7f7f7fffffffffffffffffff7efeff7eff7fff7f7fffffffff7f7fffffff7efefffffffffefeffff7effffff7fff7efefefe7ffffffffffffffff +fffff7f7fff7effffffff7f7eff7efeffff7f7fffff7fffff7ffffffffffffffffffffffffffffffefe7e7fffffffffffff7f7f7f7f7f7fffffff7f7f7f7efef +fffffff7f7f7fffffff7f7f7fff7ffffffffffffffefefeff7eff7fff7f7ffffffefe7e7f7f7f7fffffff7efeffff7f7fffff7efefe7f7efeffff7effff7efff +efeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7 +efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefef +fff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efff +efeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7 +efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefeffff7efffefef +fff7efffefeffff7efffefeffff7e7f7f7e7f7f7efeff7e7f7ffe7f7f7e7fff7efffeff7fff7ffefe7fff7f7ff8484c639398cdedeff84849c9c9c9cd6d6d6f7 +f7f7ffffff00ffffffffffffffffffffffffffffffffffffe7d6ced6cef74a4ad64a4ad6f7effff7e7effff7efbdcec68cbdb57bc6c673cece9ccec69c7352bd +7b52dead8cceb59cefdedef7f7ffeff7efeff7eff7f7efffffeff7f7defff7e7fff7eff7efefefe7e7fff7f7fff7ffb5adadd6cecefffffff7efefc6bdb5cec6 +c6e7ded6948c8ca59c94bdb5adf7efefdeded6c6bdbdfff7f7efefe7ada5a5fff7f7ffffffefe7e7ded6d6a59c948c8484bdb5b5f7efefada5a5948c8c9c9494 +fff7f7fffff7f7efefe7dedea59c9c8c8c84c6bdbde7e7e7efefefadada58c8484bdb5adbdb5b5ffffffb5a5a5fff7f7fff7f7bdb5b5e7dedeffffffcec6cece +c6c6e7dedeb5adadf7efefbdb5b5e7dedeffffffcec6c6c6bdbde7dedebdb5b5e7e7de7b73738c8c84b5b5adfff7f7d6d6ce847b7bd6d6cecec6c6cec6c6ffff +f7efe7e7848484a5a59cadada5f7efefe7dee78c848cb5adb5fff7ffc6c6c6948c8c948c8cfff7f7efefe7b5adadefefe7efefe7f7f7e7f7f7effff7eff7f7ef +f7f7e7fff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefefff +f7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffef +e7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7ef +ffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefefff +f7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffefe7fff7efffefeffff7efffef +e7fff7efffefeffff7efffefe7fff7effff7e7fff7efeff7e7eff7e7f7f7defff7e7ffefefffeff7ffeff7f7efffe7efff7b8cc6313184dedeff7b7b9c9c9ca5 +d6ced6f7f7f7ffffff00fffff7fffff7ffffffffffffffffffffffffefe7d6d6cef74a4ade4a42deffeffff7eff7ffeff79ca5adadceef73bdd69cffff84c6ad +ad8442ffa552bd6b21ffd694d6d6b5e7f7e7fffffff7eff7f7eff7f7f7f7ffffefeff7defffffffffff7f7f7f7fffff7f7fff7424a429ca594f7f7efa5a59c52 +5a4aeff7e763635a636b5a8c8c84101008ced6c67b847b636b63fffff7b5bdad4a4a42f7f7efeff7efffffff848c84424a42949c8c293129e7efe78c8c847b84 +7b6b6b63ded6cefffff7fff7efadad9c524a4294948c211810e7e7d6bdb5ad4239318c847b4a42396b5a52fff7ef392121d6bdbdffffff4a394ac6b5bdffeff7 +84737b94848cefdee7291829fff7ff4a3142b5a5adffffff6b5a639c8c94efdeef312129f7efe7ada59c847b735a5a4af7efe75a5a4a635a52d6cec67373638c +8c7bffffef9c948c5a524a949484211810efe7de8c84846b5a5ab5a5adc6bdbd524a4a8c8484635a528c847bffffff313121bdb5a5f7efe7fffff7ffffeffff7 +e7f7f7e7ffffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7 +f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7 +f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ff +eff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7 +f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7 +f7e7f7ffeff7f7e7f7ffeff7f7e7f7ffeff7f7e7fff7f7f7f7efeff7f7eff7efefffeff7f7effff7f7ffeff7fff7ffe7efffe7f7ff7384c6293994d6deff7b84 +ad949cadd6d6def7f7f7ffffff00ffffeffffff7fffff7fffffffffff7ffffffd6cec6d6c6f74a42de4a42deefe7fff7eff7dececeb5b5bd9cadd6addeff7bde +d6a5e7bd846b29ce8c39de9c4ac69c5ab5c694d6e7d6ffffffffeffff7e7f7fff7f7f7f7efeff7e7efefe7fffffff7efeff7f7effffff75a5a52b5adadffffff +7b736bb5b5adffffef393931f7efe7ffffff313121deded684847b848c7bf7fff7bdbdb54a524af7f7efeff7e7efefe7525a52adb5a5f7fff7393931efefe7ff +ffffb5b5ad313121e7ded6f7efe7fffff7948c846b635afffff763524aefe7de736363b5ada5fffff7cebdb5634a4afffff7423129efd6d6ffffff5a4252ceb5 +c6ffffff846b7b947b8ce7cede422939ffe7ef52394adec6d6ffffff846b7b847384efd6de4a3942f7efe7fff7efb5a59c423129efe7de524a42d6cec6ffffff +736b63948c84ffffff9c94847b736bfffff75a524af7efe77b6b6b948484ffffff9c8c947b6b73ffffffffefef73635afff7ef423931ded6cefffffff7e7deff +fff7fff7e7f7efe7f7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7 +e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7ef +f7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7 +f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7 +e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7ef +f7f7e7f7f7eff7f7e7f7f7eff7f7e7f7f7eff7f7e7fff7eff7efeff7f7f7efeff7eff7f7eff7e7f7f7efffeff7ffeffff7efffefefffdeefff7b8cce292994d6 +d6ff7b7bad9c9cadceced6f7f7f7ffffff00fffff7fffff7fffffffffffffffffffffff7ded6c6cec6ef524ae73931d6f7f7ffffffefb5b59cded6c6a59494bd +bdc6bdefd69ccea56373427b734a84845a848463adc6adc6d6c6fff7ffffe7fffff7fffff7f7fffff7f7efe7ffefeffff7fffff7f7f7e7e7ffffff736363846b +73948484736363ffffffffefef4a3931efdedefff7ef524239efdede9c948c6b5a5affffffbdb5b56b5a5afff7f7ffefefffffffffffff736363392929423931 +ffefef4231319c8c8cffffffefdedeffffffffefeffff7f79c8c94423131211010efdede8c7b7bc6b5b5ffffffbdada55a4a4affefef523939ceb5b5ffffff4a +3139bda5adffffff7b6b739c848cffe7ef4a3942ffeff75a4a52bda5adfff7ff846b73ad949ce7ced6635252ffffff312121b5ada5ffefefffffff524239e7d6 +d6ffffff7b6b6b948484fff7eff7efe77b6b6b423131211010fff7f7846b73ad949cffffff9c8c8c8c7373fffffffff7ff5a424affefef524242cebdb5fffff7 +fff7effffff7fff7efffefeffff7effff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7ff +f7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7 +f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7ef +fff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7ff +f7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7 +f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7ffeff7fff7f7fff7efffffeffff7e7fff7f7ffeff7fff7fff7effff7f7ff8484ce +39319cded6ff8c7bb5a594b5d6d6def7f7f7ffffff00fffff7fffff7ffffffffffffffffffffffffded6d6d6c6ff4a42e73129d6f7effffffff794947befe7c6 +8c63429c7b5a5a633152734aa5dece94ded68ccec68cbdbd9cc6bdc6c6c6ffeff7ffeff7fff7fffffff7fff7efffefefffeff7fff7fffff7ffffe7efffeff75a +4a525239426b525a948484f7dee7f7dede291818f7e7e7ffffff210810efdede8c7b84312121846b735242428c737bffffffefdedeffefefefdedea59c9c9c84 +8c5a4a4affeff77363639c8c948c7b84f7e7effff7f7f7e7efe7dedeada5a5ada5ad312929f7eff7a59ca54239428c7b845a4a4a63525affffff291018635252 +9484843931317b6b6b8c8484392931b5adade7dede211818f7e7ef4231394a3942948c8c292121bdb5b5decece292121f7efef6b6363847b7bada5a5b5adad31 +29296b6363ded6d67b6b6b4a4242a59c9cefe7e7ada5a5b5adad423139d6cece4231396b5a63ad9ca5d6c6ce4a394284737b6b5a5a6b5a5afff7ff4a42426352 +52b5adadfffffffff7effff7effff7efffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7 +ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefefff +f7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffef +effff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7 +ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefefff +f7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7f7ffefeffff7effff7e7ffffe7ffefefffeff7ffeff7f7efffefef +ff8484ce312994e7d6ff8473ada594add6ced6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffe7d6e7debdff5242ff3929e7f7e7ffffffff9c9c +94efdebdb58c4ad6a552b5944a738c636bdede31c6ef63e7ff4ab5d694c6d6bdc6c6ffffffffefeffffff7ffffeffffff7f7efeffff7f7f7eff7fff7ffefe7ef +ffffff6b5a63b5adb5ffffff94848c847b7bffffffbdb5b5f7efeff7eff7c6bdbdf7e7efcec6ced6ced6948c8c9c9c9cefe7efefefeffffffffff7ffe7dee784 +7b848c848cbdb5b5ffffffcec6c68c848c8c8c8ce7e7efefefefffffffdee7e7949c9c637373b5bdc6effffff7ffffb5bdbd848c8c84848c525252ffffffb5ad +b5cecece848484d6ded6d6ded6848c84adb5adf7fff7f7fff7a5b5adffffffb5bdb5c6cec67b847badb5addee7deffffff9ca5a5f7ffffced6d6848c8c7b8484 +bdc6c6212929636b6bb5bdbdced6d6a5adad949c9ceff7ef848c8c6b7373c6cecedee7e742424a393942b5b5bdffffffc6c6ce84848c94949cefefefffffffad +b5b5a5a5a5adadadefefefefefefffffffefefeffff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7 +f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7 +fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7ff +f7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7 +f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7 +fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7fff7f7fff7f7fff7effff7f7f7f7efffffe7ffffe7ffffeffff7effff7f7ef +efffeff7ff7b8cc6313994dedeff8484ad9c94add6d6def7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6c6d6dec6ff4a39ff4239f7e7deffff +ffffa5a5a5e7debd9c7b31ffde7be7b563737b525ad6de5affff31bdef52b5de8cbdcebdcec6fffffffff7effff7e7ffffefffffeff7f7eff7f7f7f7f7f7fff7 +fffff7ffffffff5a525ab5adb5ffffff5252528c8c8cfff7f7fffffff7f7f7fffffff7f7f7fffffffffffffffffffffffffffffffffffff7f7f7e7efeff7f7f7 +f7ffffffffffffffffffffffe7efeffffffffffffffffffff7ffffffffffdeefefffffffeff7f7f7fffff7ffffefffffe7f7f7f7fffff7ffffbdc6c652525af7 +f7f7ffffffffffffffffffeffff7f7fffff7fffff7ffffe7f7eff7fffff7ffffeff7f7f7fffff7ffffffffffe7f7efffffffe7f7efffffffe7f7eff7fffff7ff +fff7fffff7ffff394242b5bdbdffffffdeefeffffffff7fffff7ffffefffffffffffeff7f7ffffff6b6b739c9ca5efeff7eff7f7eff7f7fffffffffffff7f7f7 +e7efeffffffff7fffffffffff7f7f7eff7efe7efefffffffeff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7 +f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7 +f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7 +eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7 +f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7 +f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7eff7f7f7f7f7ffeff7fff7f7f7f7f7f7fff7eff7e7f7ffe7fff7effff7f7 +f7f7f7eff7ffdef7ff7b8cc629318cdedeff7b7bad9c9cadceced6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffe7d6d6cebdf7524aef3939d6 +eff7fff7ffefbdceadced6a5d6c684a58439f7ce84948c5a73c6c663d6e76bb5d694c6de94ada5ced6cefffffffff7f7fff7effff7effffff7f7f7eff7f7f7ff +ffffffffffffffffefefef7b7b7b292929212121636363deded6fffffff7f7effffff7fffff7ffffffefefeff7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7ff +f7f7f7f7f7fff7eff7f7f7fff7f7f7f7f7fff7eff7f7f7fff7f7f7f7fffff7fff7f7fffff7f7f7f7fffff7f7f7f7f7fff7f7f7f7f7f7f7f7f7efefefefc6bdbd +8c8484fff7f7fffff7f7eff7f7eff7efeff7fffffff7f7fff7eff7f7f7ffc6c6ce52525af7eff7f7f7f7fffffff7eff7f7effff7f7ffc6bdc65a525afff7f7f7 +f7effffff7fff7f7fffff7f7f7effffff7fff7f7fffff7f7f7effffff7fff7f7fffff7f7f7effffff7fff7f7fff7f7fff7f7fff7fffff7f7fff7f7fff7f7fff7 +f7fff7f7fffff7fff7f7fffff7fff7f7fffff7fff7f7fffff7fff7f7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7 +f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7 +f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7ff +f7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7 +f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7 +f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7f7f7fff7ffffeffff7f7ffeff7fff7fff7f7f7effff7 +fffff7fff7f7ffe7f7ffefffff738cce293194d6d6ff8484ad9c9cadd6d6def7f7f7ffffff00ffffffffffffffffffffffffffffffffffffded6ced6c6ef4242 +ce3139c6eff7fffffff7c6d6b5c6d6a5ced6a5e7dea5cead73c6b58c9ccebd6ba5adadced69ca5adced6ceeff7e7fffffffffffffff7f7fffff7f7fff7f7f7f7 +f7f7f7ffffffefefefeff7effffffffffffff7f7f7fffffff7f7efffffffefefeff7f7efffffffeff7efefefe7fffff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7 +fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7fff7f7fffff7fff7f7fffff7fff7f7fffff7fff7f7fffff7fffffff7efefffff +ffffffffefe7e7ffffffffefeffffffffff7fff7eff7fff7fffffffff7eff7fffffffffffff7eff7ffefffffeffffff7fffff7fff7eff7fffffffffffff7eff7 +fff7f7fffff7fff7effffff7fff7f7fffff7fff7effffff7fff7f7fffff7fff7effffff7fff7f7fffff7fff7effff7f7fff7f7fff7f7fff7f7fff7f7fff7f7ff +f7f7fff7f7fff7f7fff7f7fffff7fff7f7fffff7fff7f7fffff7fff7f7fffff7f7f7f7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7 +f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7 +f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7 +fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7 +f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7 +f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7f7fff7f7fff7f7fff7f7f7f7fffff7fff7fffff7fff7f7fff7f7fff7f7f7f7 +fff7fff7fffff7fff7efffe7efffe7f7ff738cc629318cdedeff8484a5a5a5add6ced6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffe7decede +d6f74a52ce2939b5f7ffffeff7f7f7fff7efffe7e7f7e7eff7dec6bd94bdbd9cb5c6b5adbdbdf7f7f7f7efe7ffffefffffeffffffffff7fffffffffffffff7ff +ffe7efeff7f7fffffffffff7ffe7e7e7f7efeff7f7eff7f7f7efefeffffffffffff7f7f7f7fff7f7fffffff7f7effff7f7fff7f7fffff7f7f7f7fff7f7f7f7ef +fff7f7f7f7f7fff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7fff7f7fff7f7f7f7effff7f7f7f7f7fff7f7f7f7effffffff7 +efeffffffffffff7fff7f7fff7effff7f7fffffffffffff7eff7fffffffffffffffffff7eff7fff7fff7eff7fffffff7eff7fffffffffffffffffff7eff7fff7 +fff7eff7fff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7f7fff7f7fff7f7 +fff7f7fff7f7fff7f7fff7effff7f7fff7f7fff7f7fff7effff7f7fff7f7fff7f7fff7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7 +f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7 +f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7ef +fffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7 +f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7 +f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7f7fff7ffffeffff7f7ffeff7f7 +f7fff7f7f7f7fff7ffffefffffffffefeffff7ffff7b8cb5393984dedeff948c94ada59cded6d6f7f7f7ffffff00e7e7efe7efefe7efdee7f7e7e7efe7f7efef +cebdb5ded6ff4a52ce424ad6dedeffe7defff7f7ffeff7ffefffffe7efffefefffe7e7f7d6d6f7f7f7ffefe7fffffffffff7fffff7fff7effff7f7ffeff7fff7 +f7ffefefffefefffefe7ffefe7fff7e7fffff7fff7effffff7fffff7fffff7ffe7defffff7fffff7fff7efffe7defff7effffff7fff7effff7effffff7ffffef +fffff7ffffeffffff7ffffeffffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effff7f7fff7f7fff7f7fff7effff7f7fff7f7fff7f7ff +e7e7fff7f7fff7f7fff7effff7efffffeffffff7fff7effffff7fffffffff7f7fff7f7fff7effffff7ffefefffefe7fffff7fffff7fff7f7fffff7ffefeffff7 +f7fff7efffefe7fff7effff7f7fff7effffff7fff7effff7f7fff7effffff7fff7effff7f7fff7effffff7fff7effff7f7fff7effffff7fff7effff7f7fff7ef +fff7f7fff7effff7f7fff7effff7f7fff7effff7f7fff7effffff7fff7effff7f7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7ff +f7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7efffff +f7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7ef +fffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7ff +f7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7efffff +f7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7fff7effffff7ffffeffffff7fff7ef +fff7f7ffeff7fffff7ffffefffffefffffefffefefffe7efff7b8cce292984e7deff94849cb5a5a5d6cecef7f7f7ffffff00ffffffffffffffffffffffffffff +ffffffffe7cedee7ceff524ae74242f7c6adffd6b5ffefd6ffd6c6ffceceffceceffd6ceffd6ceffceceffb5bdffbdceffb5bdffd6c6ffd6bdffd6ceffceceff +c6ceffbdc6ffc6c6ffc6c6ffded6ffcebdffd6c6ffd6c6ffd6c6ffc6b5ffd6c6ffd6c6ffdeceffcebdffd6c6ffd6c6ffd6c6ffcec6ffd6ceffcec6ffdeceffd6 +c6ffdeceffd6c6ffdec6ffd6c6ffdeceffd6c6ffdec6ffd6c6ffdec6ffd6c6ffdec6ffd6c6ffdec6ffd6c6ffceceffc6c6ffceceffc6c6ffceceffc6c6ffcece +ffc6ceffc6c6ffc6c6ffc6bdffceceffd6ceffc6bdffcec6ffcec6ffc6c6ffced6ffced6ffbdc6ffc6ceffceceffd6d6ffced6ffc6ceffc6ceffced6ffc6c6ff +c6c6ffceceffd6deffceceffceceffcec6ffceceffcec6ffceceffcec6ffceceffcec6ffceceffcec6ffceceffcec6ffceceffcec6ffceceffcec6ffceceffc6 +c6ffceceffc6c6ffceceffc6c6ffceceffc6c6ffceceffc6c6ffceceffc6c6ffceceffc6c6ffceceffcec6ffd6ceffd6c6ffdeceffd6c6ffdeceffd6c6ffdece +ffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ff +deceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6 +c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdece +ffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ff +deceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6c6ffdeceffd6 +c6ffd6ceffceceffced6ffd6ceffdeceffdec6ffd6c6ffc6bdffb5bdff636be73129b5ded6ff8c7bbda594b5d6d6def7f7f7ffffff00ffffffffffffffffffff +ffffffffffffffffe7ceded6c6f73942c61010b53121b54221a539219c39299c3129943131a52918a52918a52929a518219c2139b52931b53121a54229ad3931 +a53139a529299c2929a52121a52929a521189c29219c29219c3931ad3129a53931ad3931ad3929a531299c29219c3129a54239ad3931ad4239ad31299c31299c +3129a53929a53129a53929a53929a53929a53129a53929a53121a53929a531219c3929a53121a53929a531219c3129a52929a53131a52929a52929a52929a529 +31a52129a52931a52931a53131ad10108c29219c21219c29299c29219c3931ad21219429319c29319c31399c31319c29319c21218c29299421219429319c2931 +9c3139a529319c29319421219429299429299c2929a529299c2929a529299c2929a529299c2929a529299c2929a529299c2929a529299c2929a529299c2929a5 +29299c2929a521299c2929a529299c2929a521299c2929a52929a52931a52929a52929a52929a52931a52929a53129a53129a53929a53129a53929a53129a539 +29a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129 +a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a5 +3129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a539 +29a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129 +a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a53129a53929a5 +39299c39319c29299c29319c29319c31319c39299c4229a539299c4239ad2931a52129a52929add6ceff8473ada59cb5d6ced6f7f7f7ffffff00fff7fffff7ff +ffffffffffffffffffffffffe7d6c6d6d6d6adc6ff8ca5ffadadffada5efb5ade7b5b5e7bdbdefada5e7c6adffad9cf7b5b5efadbdef9cb5e7adadefc6a5f7c6 +adefb5bdefadb5e7bdade7bdade7b5adefa5adefadb5f7adadefadadefa5a5e7a5ade7a5ade7a5ade7a5a5e7adb5efadb5e7b5bdf7a5addea5ade7adb5e7a5ad +e7adade7adb5efadade7adadefadade7adb5efadade7adadefadade7adadefadade7adadefadade7adadefadade7adadefadade7b5adefadade7adadefadade7 +adb5efadade7adadefa5ade7adb5efb5b5efb5b5efadade7b5adefbdbdf7bdb5efada5e7c6bdf7adade7adade7adade7b5b5efadade7b5b5efb5b5efbdbdf7b5 +adefadade7adade7b5b5efadade7b5adefb5b5efb5ade7adade7b5ade7adade7b5ade7adade7b5ade7adade7b5ade7adade7b5ade7adade7b5ade7adade7b5ad +e7adade7adade7adade7adade7adade7adb5e7adade7adadefadade7adb5efadade7adadefadade7adadefadade7adadefadade7adb5efadade7adadefadade7 +adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efad +ade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adad +efadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7 +adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efad +ade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adadefadade7adb5efadade7adad +efadade7b5b5efadade7a5adef9cadefa5b5efa5adefb5adf7b5a5f7b5b5f7adb5ef9cade794a5efadadffe7e7ff8c8c8ca5a58cdeded6f7f7f7ffffff00fff7 +fffff7ffffffffffffffffffffffffffded6bdced6c6def7ffd6efffeff7fff7f7ffeff7ffeff7fffff7fffff7ffffe7ffffeffffff7fffffffff7ffffffffff +ffe7fffff7ffeff7ffeff7ffffeffffff7fffff7fff7fffff7fffff7ffffeff7fff7ffffefffffeff7ffe7efffefffffeff7ffe7efffe7f7ffeff7ffeffffff7 +ffffe7effff7ffffeff7fff7ffffeff7fff7ffffeff7fff7f7ffeff7fff7ffffeff7fff7f7ffeff7fff7f7ffeff7fff7f7ffeff7fff7f7fff7f7fffffffff7f7 +fff7fffff7f7fff7f7fff7f7fff7fffff7ffffeff7fff7f7fff7f7fff7effffffffffff7fffff7fff7effff7effff7f7fffff7ffefeffff7f7fff7f7ffffffff +efeffff7f7fff7f7fff7f7fff7effff7f7fff7effffffffff7f7fffff7fff7f7fffff7fff7f7fff7f7fff7f7fffff7fff7f7fff7f7fff7f7fffff7fff7f7ffff +f7fff7f7fffff7fff7f7fff7f7fff7f7fff7f7fff7f7fff7f7fff7f7fff7f7fff7f7fff7fffff7f7fff7fffff7f7fff7f7fff7f7fff7ffffeff7fff7ffffeff7 +fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffff +eff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7 +ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7 +fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffff +eff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7ffffeff7fff7 +ffffeff7fff7ffffeff7fff7f7ffe7f7ffeff7ffe7f7ffeff7fff7effff7f7fff7f7fff7ffffe7f7ffefffffeff7ffe7efff8c8c73adad8cd6d6c6f7f7f7ffff +ff00ffffffffffffffffffffffffffffffffffffdeded6c6c6c6ffffffe7efffeff7ffeff7fff7f7fff7f7f7f7f7fff7effffff7fff7effff7f7ffeff7f7f7ff +fff7f7fffff7fff7effff7f7fff7f7f7fff7fff7effff7f7ffefefffefeffff7f7ffffffffe7e7efe7eff7f7fffff7f7ffefeff7f7ffffe7e7eff7f7ffefeff7 +e7e7efe7eff7ffffffefeff7efeff7efeff7f7f7fff7f7fff7f7fff7efffffffffeff7f7f7f7ffe7efeffffffff7f7fff7f7fff7f7ffefefffefeffffffffff7 +efeffff7fff7eff7fffffff7f7ffe7e7f7f7f7fff7f7ffeff7f7efefefffffffffffffe7e7efffffffefe7f7f7f7ffefeff7f7ffffe7e7efefeff7efeff7f7f7 +fff7f7fff7eff7efeff7f7f7fff7f7ffefeff7dee7efffffffe7eff7f7f7fff7f7f7fff7fff7f7fff7f7ffdee7e7f7fffff7ffffeff7f7f7f7fff7ffffefeff7 +efeff7efeff7ffffffefefeff7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7fff7f7ffefeff7f7f7ffeff7fff7f7ffef +f7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7 +ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7 +f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffef +f7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7 +ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7f7f7ffeff7f7 +f7f7ffeff7f7f7f7ffeff7fff7f7ffefeff7eff7ffeff7ffeff7ffefeffff7f7fff7effff7f7ffeff7fff7f7ffefeffff7ffffe7efef94948ca5a59cd6d6d6f7 +f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6cececefffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ffffffefefefffffffffffffefefefe7e7e7e7e7efefefeff7f7f7efef +f7ffffff737373737373efefefefefeffffffffff7f7f7eff7efefefefe7effff7ffefefefefefeff7f7eff7f7efe7efe7f7fff7f7f7ffefeff7efeffff7efff +fff7f7f7f7e7f7f7f7f7eff7ded6e7efeff7ffffffe7e7eff7f7f7dee7deeff7eff7ffeff7efeffffffff7efeffff7f7eff7f7ffffffe7e7e7eff7eff7f7f7ff +fffff7efefefefe7efefefefe7e7efefeff7f7f7efefefffffffdee7defffffff7f7f7f7f7f7f7f7f7efefefe7e7e7ffffffdee7e7eff7f7e7efefe7efefeff7 +f7fffffff7f7f7f7f7f7efe7e7fffffff7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7 +f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7 +f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efef +eff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7 +f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7 +f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efef +eff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5 +d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededec6c6c6fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefeffffffff7eff7ffffffffffffffffffffffffffffffffffffff +ffffdededeffffffadadad737373fffffffffffff7f7f7e7e7e7fffffffffffffffffffff7fffffffffffffff7f7f7fffffffffffff7f7f7efeff7efeff7f7f7 +fff7f7fffffffff7f7e7fffffff7eff7ffffffffffffefeff7f7efffeff7f7fffffffffffff7fff7fff7f7f7f7effffffff7efefefefefeff7f7ffffffffffff +efefefefefefffffffffffffffffffffffffffffffffffffffffffffffffeff7f7f7f7f7fffffffff7f7fffffffffffff7f7f7e7efe7fffffff7ffffffffffff +fffff7f7f7f7f7f7f7f7f7fffffffffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ffffffefefef8c8c +8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6c6c6c6fffffff7f7f7efefeff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffff7f7f7efefeffffffff7f7f7c6c6ce292929 +181818737373f7f7f7ffffff7b7b84424242212129292929ded6d6ffffffded6d61810181008104a424ae7e7e7424242dededef7f7ef52524ac6c6c6f7ffffef +eff7dedeef524a5ae7dedef7f7ef525252efefef847b8494949cf7f7ffbdb5c61821211818185a5a52f7f7efefefe7ffffff524a52efe7eff7ffff525a5a1018 +18424a4affffffffffffe7dede2929291818187b7b7befe7ef4a4a4a312931525252d6dedee7e7e73939395a5a5acecece525252fffffff7ffff212121181818 +737373e7e7e7d6dede525252fff7f7ffffff4a4242efefeff7efeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7 +f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7 +f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7 +efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7 +f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7 +f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7 +efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ef +efef8c8c8ca5a5a5cececef7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededec6c6c6fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffff7eff7f7f7f7ffffffefeff7cecece3939 +39efeff7ffffff737373636363ffffff848484525252efeff7adadad5a5a52f7efef393939bdb5bdffffffffffffdedee7313131f7f7efffffff393931d6ded6 +f7ffffe7deefdedeef42424af7f7efffffff393131e7e7e78c848c73737befeff7101018e7efefffffffffffffffffffe7e7deffffff423939ded6de73737b63 +6b6bffffff949494635a63ffffff524a52c6bdbdfffffff7f7f7c6c6c64a4a4affffff424242b5b5bd9ca5a5636363fff7f7e7dede4a4242ffffff313131adad +adf7ffff6b6b6ba5a5a5e7e7e7393939efe7e7f7efef423939dededef7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffff7f7f7 +ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6cececefffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7eff7fffffffffffff7f7f7f7f7ff5a +5a5aa5a5a5efefeff7f7f7ffffff313131dedede84848484848cefefefdedede6b6b6bf7efef3131317b737b7b7b849c949cdedede313131d6d6d6ffffff2129 +21dee7def7f7f7f7f7ffe7e7f7212129ded6d6ffffff393131efefef7b737b9c94a5c6c6ce393942848484848484adada5efefe7f7f7efffffff292129f7eff7 +63636badadb5f7f7ffe7e7ef73737bd6ced6211821f7f7f7efe7eff7eff7d6d6d64a4a4a737373424242ceced6adadad635a5aefe7e7e7dee7393131e7e7e731 +3131e7dedef7f7f7adadad8c8c8cd6d6d6393131fff7f7ffffff312929e7dedef7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efef +eff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7 +f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7 +f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efef +eff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7 +f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7 +f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7efefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededec6c6c6fffffff7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefefffffffefefefefefef +ffffff524a52dededef7f7f7efefeff7f7f76b6b6bc6c6c69c9c9c212121ffffffc6c6c6525252efe7e72118189c949c8c8494393942efefef212121cececeef +f7ef292921dededef7f7ffe7e7efffffff393942fffff7ffffff312929efefef948c94736b7befe7f7080810a5a5a5848c8c52524afffffff7f7eff7f7f73129 +31e7dede6b6b7384848cffffffa5a5ad423942ffffff393139d6ced6ffffffffffffffffffefefef9c9c9c292931c6c6ceb5b5bd6b6363ffffffe7dede423939 +ffffff393939cececeffffff8c8c8c848484efe7e7212121dededeefe7e7292121f7efeffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6c6c6c6fffffff7f7f7efefeff7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efe7effffffff7f7 +f7ffffffefefef4a4a4ac6bdc6ffffffefefefffffff636363cecece8c8c8c636363313131212121ada5a5ffffffc6c6c6292931313139adadb5e7e7e7292929 +5a5a521818105a5a5aefefefeff7f7ffffff424252080810393939efefe7393939efe7ef736b737b7b84efefff9c9ca5292929292929d6d6cef7f7efe7e7e7ff +ffff393131e7e7e7e7e7ef313139101018313139bdbdc6e7e7efded6de3129311810188c848cefe7ef63636b313131313139bdc6c62929290800085a5252e7de +de313131f7f7f7d6cece181818292121424242dededecec6c6313131524a52211818847373fff7f7f7efeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7 +efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7 +f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7 +f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7 +efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7 +f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7 +f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5cececef7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededec6c6c6fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ff +fffff7f7f7f7f7f7ffffff8c8c8c6b6b6bffffffffffffded6de313131fff7fffff7fffffffffffffffffffffff7f7f7f7f7fff7fff7f7f7fff7fff7f7f7fff7 +fff7f7f7fffff7fffff7ffffffeff7eff7f7f7efeff7e7e7f7313142fffff7fff7efffffffffffff84848c7b7384fffffff7f7fffffffff7f7f7fffffffffff7 +fff7f7ffffff312929dededef7f7ffeff7f7f7f7fff7f7fffff7fff7f7f7fff7fffff7fffff7fff7eff7f7f7f7f7f7fff7f7ffffffffffffffa5a5a5635a5aff +f7f7fffffffffffffff7f7f7f7f7fffffffffffffff7f7f7f7f7fff7fffff7f7fff7f7fff7f7fff7fffff7f7fff7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6cececeffff +fff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +efefefffffffefeff7efefefefefefefeff75a525a5a5a5a848484636363a5a5adf7f7f7dededeffffffdedee7efefeff7f7f7fff7f7f7f7f7f7f7fff7efffff +f7fff7f7f7f7f7f7f7f7eff7f7eff7f7eff7fff7f7ffffefeff7cecede4a4a526b6363deded6847b7bffffff7b737b73737bf7f7ffefeff7efeff7eff7eff7f7 +eff7f7efefefe7fff7f7423939cec6cef7f7f7f7f7ffefeff7f7f7fff7f7f7fff7fff7eff7fff7fff7eff7f7f7f7efe7eff7f7ffefeff7f7f7fff7f7ffc6c6ce +a59c9cfff7f7d6cecea59c9cefefefefe7e7efefeff7efeff7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7ffeff7fff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededec6 +c6c6fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7ffffffe7e7e7efefeffffffff7f7f7dedee7949494948c94a5a5a5ffffffefefefffffffefeff7f7f7f7efefeffffff7fff7f7fff7fff7f7f7 +fff7fff7f7fff7f7fff7f7f7fffff7efefe7f7fff7efefeff7f7fff7f7ffffffffd6d6de94948cefe7dea5a5a5fff7f7ceced6c6bdcef7f7ffefeff7f7ffffef +f7f7fffff7eff7effffff7ffffffa59c9cefe7eff7f7fff7f7f7f7f7fff7f7f7fff7fff7f7f7fff7fff7f7f7fff7f7f7eff7f7eff7f7f7fff7f7ffefefefffff +ffefefefefefefffffffefe7e7ada5a5fffffff7f7f7fffffffffffffffffff7f7f7fff7f7f7f7f7fff7fffff7f7fff7f7fff7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffff +d6d6d6cececefffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7efefeffffffffffffff7f7f7efefefffffffffffffefefeffffffff7f7f7f7eff7ffffffe7e7e7fffffff7f7f7f7f7f7fff7f7f7f7 +f7fff7fff7eff7f7f7fff7f7f7f7f7f7efefe7efefe7f7fff7f7f7efefeff7efeff7f7f7fffffffffffffffffff7fff7f7f7efefffffffffffffe7e7eff7f7ff +efefefefefefeff7efefefe7f7f7f7ffffffe7e7e7ffffffeff7f7f7f7f7f7f7f7f7f7f7f7eff7f7f7f7f7f7f7fff7f7f7eff7fff7ffefeff7f7f7fff7f7f7ef +efeff7f7f7fffffffff7f7fff7f7fffffffff7f7f7f7f7fffffffffffff7fffff7f7f7f7f7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5cececef7f7f7ffffff00ffffffffffffffffffffffffffff +ffffffffdededec6c6c6fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffff7f7f7ffffffefefeffff7fff7f7f7f7f7f7ffffffe7e7e7f7f7f7ffffffe7e7efefeff7fffffff7f7f7fff7f7f7 +f7f7fff7fff7f7f7fff7fff7f7f7fff7fff7f7f7f7f7f7efefeffffff7efefeffffffff7f7ffefefffe7e7effffffffffff7f7f7f7f7f7f7f7effffff7fff7f7 +fff7f7fff7f7f7eff7effffff7eff7effffffff7efeffffffff7eff7f7f7ffeff7f7f7fffff7f7f7fff7fff7f7f7fff7fffff7f7fff7f7fffffffff7fff7f7f7 +fffffff7f7f7f7fffff7f7f7ffffffefe7e7ffffffffffffefefeffffffff7f7f7e7efeff7f7f7f7f7f7f7fffff7f7f7fff7f7f7f7f7fff7fffff7f7fff7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffff +ffffffffffffffffd6d6d6cececefffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefefffffffe7e7e7f7f7f7efefefefefeffffffff7f7f7e7e7e7efefeffffffffffffff7f7f7fff7ffefeff7ffffff +f7f7f7fff7f7f7f7f7f7f7fff7effffff7fff7f7f7f7f7f7f7fff7fffff7eff7efe7efe7efeff7fffffff7f7ffffffffefefe7f7f7eff7efefffffffefe7eff7 +f7fff7f7fffff7fff7f7f7fffffff7f7f7f7f7effff7f7f7efeffffffff7eff7f7f7f7f7f7f7eff7f7f7f7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7fffffff7f7 +f7f7f7f7f7f7f7f7ffffefefefefefeff7f7f7f7f7f7f7f7f7efefeffffffff7ffffe7efeff7f7f7eff7f7f7f7f7eff7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffff +ffffffffffffffffffffffffdededec6c6c6fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefefffffffefefefffffffefeff7e7e7e7f7f7f7f7eff7fffffff7f7ffe7e7efffffffefefeff7f7 +f7f7f7f7fffff7fff7f7fff7fff7f7f7fff7fff7f7fff7f7fff7f7f7f7f7effffff7fffffff7fff7f7f7ffefeff7efefffefeff7ffffffefefdeffffffefefef +fffffff7f7ffefefffefeff7f7f7f7eff7efeff7efeff7e7fff7f7f7efeffffffffff7f7f7fffff7f7f7f7fff7f7f7f7fffff7f7f7f7fff7f7f7f7f7fffffff7 +f7f7fffff7f7f7f7f7f7f7f7f7f7eff7f7eff7f7f7f7f7f7f7f7ffffffe7e7e7ffffffe7efefeff7f7eff7eff7ffffeff7f7f7fff7f7f7f7fffffff7f7f7fff7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffff +ffffffffffffffffffffffffffffffffd6d6d6cececefffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefefffffffe7e7e7ffffffefefefffffffefeff7efeff7f7f7f7f7efeffff7fffff7fff7efefff +fffff7f7f7f7f7f7f7f7f7fff7f7f7f7f7fff7fff7eff7f7f7fff7f7f7f7f7f7f7f7f7fffff7efefeff7fffff7f7f7f7f7fff7effff7eff7efefe7fffff7f7ef +effff7fff7eff7f7f7ffefeff7f7f7ffe7efefeff7f7f7f7effffffffffff7ffffffefefeffff7f7eff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fff7f7 +fff7f7efefeff7f7eff7f7f7efefe7efefefefefeffffffff7f7f7f7f7f7ffffffffffffefefefefefefffffffe7efe7eff7f7f7f7f7eff7f7f7f7f7f7f7f7f7 +f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5cececef7f7f7ffff +ff00ffffffffffffffffffffffffffffffffffffdededecec6c6fffffff7f7f7fff7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7eff7ffffffefeff7efeff7f7f7efe7efefffffffefeff7fffff7fff7eff7eff7ffefff +fff7f7efefe7ffffffefeff7ffffffefe7e7fffffff7efeffffffff7f7f7ffffffe7e7e7ffffffefefeffffffff7f7f7f7f7f7efeff7fffffff7eff7fff7f7f7 +f7f7fffffff7f7f7f7f7fff7f7f7fff7fff7f7f7f7f7f7f7f7f7fffff7f7f7f7f7fff7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7fff7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7fffff7f7f7f7f7f7f7f7f7f7fffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7ffffffefefef8c8c8ca5a5a5d6d6d6f7 +f7f7ffffff00ffffffffffffffffffffffffffffffffffffded6d6cececefffffffff7f7f7f7f7fff7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffffff7fff7eff7ffffffe7efe7f7fff7e7e7f7efeffff7f7efefe7e7ffff +fff7e7f7f7f7f7ffffffdededefffffff7f7f7ffffffd6d6d6ffffffefefefefefefe7e7e7ffffffe7e7e7fffffff7efefefefeffff7f7fffffff7efefffffff +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5 +d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededecec6cefffffff7f7f7fff7f7f7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efe7e7fffffffff7ffe7e7efe7efeff7fffff7f7fffffffff7f7efff +fffff7eff7ffffff9c9c9c737b7bf7f7f7f7f7f7f7f7f7fffffffff7f7efe7e7fffffff7f7f7ffffffefe7e7fffffff7f7f7ffffffffffffefefeff7efefffff +ffe7e7e7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ffffffefefef8c8c +8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6cec6cefffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ffffffefe7eff7f7ffefffffd6e7e7efeff7f7eff7 +f7f7f7f7fffffff7ffffffff4a4a52293131f7f7ffffffffffffffe7dedef7f7f7ffffffefe7e7f7f7f7f7f7f7ffffffefefeffffffff7f7f7f7f7f7ffffffff +fffff7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ef +efef8c8c8ca5a5a5cececef7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededec6c6c6fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffffff7f7fffffff7f7ffdeefefefffffffff +ffded6d6bdceced6dee7ffffffffeff752525a102121c6cecec6ceceefefefffffffffffffd6d6d6d6cecec6c6c6f7f7f7efefefdededeefefefe7e7e7d6d6d6 +f7f7f7e7e7e7efefeff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6cececefffffff7f7f7eff7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7eff7f7fffffffff7f7efefefe7ffffde +eff7524a4a4a4242293942182931bdadadffffff636b730810182939391018187b7b7bfff7f78c8c8c080808424242313131a5a5a58c8c8c000000e7dede9c9c +9c100808bdbdbdffffffefefefefefeff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7efefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffdededec6cec6fffffff7f7f7f7f7f7f7f7f7f7ff +fff7f7f7f7fffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7eff7f7e7efeffffffffff7eff7f7f7 +deffff426363393129f7efefefffff73848c312121efd6d6636b73102929eff7f79ca5a5181818b5adad101010848484fffffffffffff7f7f79c9c9c181818de +dedea5a5a5181010d6d6d6f7f7f7efefeffffffffffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7ffffffefefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffffffffffffffffffffffffffffffffffffd6d6d6cecec6fffffff7f7f7eff7f7f7 +f7f7eff7f7f7fff7eff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efeff7fffffff7ef +eff7ffffbddee70018217b7b73fff7efe7f7ffadbdc6100000cebdb5526363213139efffffb5b5b5000000adadad0800002118181818182121216b6b6b949494 +080000dededea5a5a5080808c6bdbdfffffffffffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5cececef7f7f7ffffff00fffffffffff7ffffffffffffffffffffffffc6bdbdd6d6cefffffff7f7f7 +f7f7f7eff7f7f7fffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fff7 +f7fffffff7efefefd6f7ff082129948c84fff7efefffffc6d6de211010b59c9c5a636b000008b5bdbd525252292929e7e7e75a5a5a525252d6d6d63131315a5a +5a9494940808089c9c9c7b7b7b212121ded6d6f7f7f7f7f7f7f7efeff7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7f7f7f7efefefffffffefefef949494a5a5a5d6d6d6f7f7f7ffffff00e7d6c6efdecee7decee7decee7d6cee7ded6bdbdb5d6d6ceffff +fff7f7f7eff7eff7f7f7eff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +e7efeffffffff7efefffffffbddee7102931847b73fff7efd6eff7c6dee7100000b5a59c8c949463737b5a6363424a4ab5b5b5ffffffe7dede6363633939397b +7b7befefefb5b5b56b6b6b636363524a4a8c8484fffffff7f7f7efefeffffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefefefefeff7f7f7efefef8c8c8ca5a5a5cececef7f7f7ffffff00e7cebddecebde7d6bddecebdded6c6d6cebdcec6bdd6 +d6cefffffff7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7fffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7fffffffffffff7efefefefefefffff6b8484181010efe7e7f7ffff424a52312121ffefeffffffff7fffff7fffffffffffffffff7efeffffffffff7f7 +fffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7f7ffffffefefeffffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7efefefffffffefefef8c8c8c9c9c9cd6d6d6f7f7f7ffffff00ffe7d6ffefd6ffe7d6ffefdef7efdef7efde +c6bdb5ded6cefffff7fffff7efefeff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7fffffffffffffff7f7f7f7f7d6e7e7e7f7f7847b7b21181810212139424adececefff7f7e7efefeff7f7e7efefefefefefefefefefefefe7 +e7f7f7f7f7efeff7f7f7ffffffffffffefefefffffffefefeff7f7f7efefeff7f7f7fffffffffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7efefef8c8c8ca5a5a5d6d6d6f7f7f7ffffff00ffdec6f7dec6f7decef7decef7de +ceefdececec6b5ded6c6fffffffff7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7fffffffff7f7f7eff7f7f7f7e7f7f7effffffffffffff7ffefefeff7f7f7fff7fff7e7eff7f7f7eff7f7efefefffffffefefeff7 +f7f7fffffffffffff7f7f7e7e7e7fffffff7f7f7f7f7f7f7efefefefeff7efeffffffff7f7f7f7efefefefeff7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7ffff +fff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7 +f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7 +f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7efefef9c9c9cb5b5b5dededef7f7f7ffffff00ffdec6ffe7ceffdec6ff +e7cef7decef7e7d6cebdadded6cefffff7fff7f7f7efeffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fffffffff7fffff7ffefefefeff7f7ffffffe7deeff7effffffffff7efeff7e7f7ffffffefefefefefeff7f7f7dedee7 +e7e7e7fffffffff7f7e7e7e7ffffffffffffefefeffff7f7f7efeff7f7f7fffffffff7f7f7efeff7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefeff7f7f7f7f7f7f7f7f7f7f7f7efefef9c9c9cd6d6d6e7e7e7ffffffffffff00ffe7ceffe7ce +ffe7ceffe7ceffe7d6f7e7d6d6c6b5ded6c6fffffffff7f7fff7f7fff7f7fff7fff7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefeffffffff7f7fffffffff7fff7dee7e7ffffffe7e7f7fff7effff7effff7fff7e7f7ffffffefefe7f7f7 +fff7f7ffffffffefe7e7fffffffffffff7f7f7f7efeff7f7f7f7f7f7ffffffefe7e7efefeff7f7f7ffffffefefeff7f7f7f7efeffffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7 +fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7 +f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7 +f7f7f7f7fffffff7f7f7f7f7f7f7f7f7fffffff7f7f7f7f7f7f7f7f7f7f7f7efefeffffffff7f7f7ffffffe7e7e7adadadf7f7f7ffffffffffffffffff00ffe7 +ceffe7ceffe7ceffe7d6ffe7d6ffe7d6cebdade7d6cefffff7fff7f7f7efeffff7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7eff7f7f7f7f7f7f7f7f7f7efefefefeff7eff7f7f7ffefefe7f7fff7f7f7fffffffff7f7effffff7fff7fffffffff7efefff +ffffe7e7e7fffffff7f7f7f7f7f7efefe7f7f7f7fffffff7efeffff7f7f7efefefe7e7fff7f7f7efeff7efeffffff7ffffffefefe7fffffff7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7fff7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7 +f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7efefefefefeff7f7f7fffffff7f7f7e7e7e7b5b5b5ffffffffffffffffffffff +ff00ffefd6ffe7ceffefd6ffe7d6ffefd6f7e7d6cebdb5decec6fffffffff7f7f7efeff7efeff7efeffff7fffff7f7f7f7effffff7f7f7f7fff7f7f7f7efffff +f7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effffff7f7f7f7fff7f7f7f7effff7f7f7f7f7fffff7f7f7effff7f7f7f7f7fff7f7f7f7effff7f7f7f7f7 +fff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7 +f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7 +f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7ef +fff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7 +f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7 +f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7ef +fff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7f7effff7f7f7 +f7effff7f7f7f7effff7f7fff7effff7f7fff7f7f7efeff7f7f7fffffff7f7f7ffffffefefeff7f7f7efefeffffffff7f7f7fffffff7f7f7fffffff7f7f7ffff +fff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7 +fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7fffffff7f7f7f7f7f7efefefffffffefefefb5b5b5ffffffffffffff +ffffffffff00ffe7ceffe7ceffe7ceffefd6f7e7d6f7e7d6decec6e7decefffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffff7fffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7f7ffffffffffffffffffffffffe7e7e7b5b5b5ffffff +ffffffffffffffffff00ffefd6ffe7d6ffefd6ffefd6ffefdeffe7d6d6c6b5bdb5a5cebdb5c6b5adc6b5adc6b5b5bdb5adbdadadc6bdb5bdb5adbdbdb5bdb5ad +c6bdb5bdb5adbdbdb5bdb5adc6bdb5bdb5adbdbdb5bdb5adc6bdb5bdb5adbdbdb5bdb5adc6bdb5bdb5adbdbdb5bdb5adc6bdb5bdb5adbdbdb5bdb5adbdbdb5bd +b5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbd +b5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5ad +bdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bd +b5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbd +b5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5ad +bdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bd +b5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbdb5bdb5adbdbd +b5bdb5adbdbdb5bdb5adc6bdb5bdb5adc6bdb5c6b5adc6b5b5bdb5adc6b5b5bdb5adc6b5b5c6bdbdadada5bdbdb5c6c6c6bdbdbdb5b5b5b5b5b5b5b5b5b5b5b5 +b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5 +b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5adadadb5b5b5adadadbdbd +bdf7f7f7ffffffffffffffffff00ffe7d6ffefd6ffe7d6ffefd6ffe7d6ffefdeffefdeefdeceefdeceefded6e7d6cee7ded6e7d6ceefded6e7d6cee7ded6e7d6 +cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6e7d6cee7ded6 +ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7 +decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6 +cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6 +ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7 +decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6 +cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6 +ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7decee7d6cee7ded6ded6cee7 +decee7d6cee7ded6ded6cee7decee7d6cee7decee7d6cee7decee7d6cee7ded6e7d6ceded6ceded6ceefe7e7b5adade7e7deefefe7efefefe7efe7efefefefef +efefefefe7e7e7efefefefefefefefefe7e7e7efefefefefefefefefe7e7e7efefefefefefefefefe7e7e7efefefefefefefefefe7e7e7efefefefefefefefef +e7e7e7efefefefefefefefefe7e7e7efefefefefefefefefe7e7e7efefefefefefefefefe7e7e7efefefefefefefefefefefefefefefe7e7e7efefefefefeff7 +f7f7ffffffffffffffffffffffffffffff00ffefdeffefdeffefdeffefd6ffefdeffefdeffefdeefdeceffefdeffefe7ffefe7ffe7deffefe7ffefdeffefe7ff +efdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffefe7ffefdeffef +e7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefde +ffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7 +efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffef +e7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefde +ffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7 +efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffef +e7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7ffefde +ffefe7f7efdeffefe7ffefdeffefe7f7efdeffefe7f7efdeffefe7f7efdeffefe7ffefdeffefe7f7e7def7e7defffff7c6bdadfffff7ffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffff7f7f7ffffffffffffffffff00ffe7d6ffefdeffefdeffefdeffe7d6ffefdeffefdeffefdeffefdefff7e7ffe7deffe7deffe7d6ffefde +ffefdefff7e7ffefdeffefe7ffefdefff7e7ffefdeffefe7ffefdefff7e7ffefdeffefe7ffefdefff7e7ffefdeffefe7ffefdefff7e7ffefdeffefe7ffefdeff +f7e7ffefdeffefe7ffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffef +deffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefde +ffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeff +efe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffef +deffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefde +ffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeff +efe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffefdeffefdeffefdeffefe7ffef +deffefdeffefdeffefe7ffefdeffefdeffefdefff7deffefdeffefdeffefdefff7e7ffefdeffefe7ffefe7ffefe7f7e7defffff7bdada5fffff7fffff7ffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffff00ffefdeffefdefff7e7ffefdeffefe7ffefdefff7e7ffefdeffefdeffefdeffe7d6ffefdeffff +effff7e7ffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefde +ffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeff +efdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffef +deffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefde +ffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeff +efdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffef +deffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefde +ffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeff +efdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdeffefdefff7e7ffefdeffefe7ffffefbdada5fffff7ffff +f7fffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffefdeffefe7ffefdeffefdeffefdeffefdeffe7d6ffefdeffefdeffefdeffe7d6ff +efdefff7e7ffefdeffefd6ffefdeffefdeffefdeffefd6ffefdeffefdeffefdeffefd6ffefdeffefdeffefdeffefd6ffefdeffefdeffefdeffefd6ffefdeffef +deffefdeffefd6ffefdeffefdeffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefde +ffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ff +efdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffef +d6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefde +ffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ff +efdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffef +d6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefde +ffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefd6ffefdeffefdeffefdeffefdeffefe7ffefe7ffffefbdad9cff +fff7ffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7f7ffffffffffffffffffffffffffffffffffffffffff00040000002701ffff030000000000}}}{\rtlch\fcs1 \af37 +\ltrch\fcs0 \f37\insrsid13645585 +\par \hich\af37\dbch\af31505\loch\f37 \hich\f37 Alternatively you can achieve the same using the \'93\loch\f37 \hich\f37 Properties\'94\loch\f37 \hich\f37 Windows menu of the executable (\'93\loch\f37 \hich\f37 Privilege level\'94\loch\f37 \hich\f37 + setting in the \'93\loch\f37 \hich\f37 Compatibilty\'94\loch\f37 tab): Right mouse click -> Properties -> Compatibility -> Privilege level -> S\hich\af37\dbch\af31505\loch\f37 \hich\f37 et \'93\loch\f37 \hich\f37 Run this program as an administrator\'94 +.}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid13645585\charrsid4744522 +\par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 Graphical Perfmon front end: +\par \hich\af37\dbch\af31505\loch\f37 1.\tab Compile the windows MSR driver (msr.sys) with Windows* DDK Kit (see the sources in the WinMSRDriver directory). For Windows 7 you have to sign the msr.sys driver additionally (}{\field{\*\fldinst {\rtlch\fcs1 \af37 +\ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 HYPER\hich\af37\dbch\af31505\loch\f37 LINK "http://msdn.microsoft.com/en-us/library/ms537361(VS.85).aspx"}{\rtlch\fcs1 \af37 \ltrch\fcs0 +\f37\lang7\langfe2057\langnp7\insrsid16320623 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b9200000068007400740070003a002f002f006d00730064006e002e006d006900630072006f0073006f00660074002e0063006f006d002f0065006e002d00750073002f006c006900620072006100720079002f006d00 +73003500330037003300360031002800560053002e003800350029002e0061007300700078000000795881f43b1d7f48af2c825dc485276300000000a5ab0000005c0000ff340042}}}{\fldrslt {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\ul\cf2\insrsid10518488\charrsid16320623 +\hich\af37\dbch\af31505\loch\f37 http://msdn.microsoft.com/en-us/library/ms537361(VS.8\hich\af37\dbch\af31505\loch\f37 5).aspx}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 +\hich\af37\dbch\af31505\loch\f37 ). +\par \hich\af37\dbch\af31505\loch\f37 2.\tab Copy msr.sys into the c:\\windows\\system32 directory +\par \hich\af37\dbch\af31505\loch\f37 3.\tab Build intelpcm.dll in the intelpcm.dll directory using Microsoft* Visual Studio +\par \hich\af37\dbch\af31505\loch\f37 4.\tab Build 'PCM-Service.exe' in the PCM-Service_Win directory using Microsoft* Visual Studio +\par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid2696726 {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 5.\tab Copy PCM-Se +\hich\af37\dbch\af31505\loch\f37 rvice.exe, PCM-Service.exe.con}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid2696726 \hich\af37\dbch\af31505\loch\f37 fig, and intelpcm.dll.dll files }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 +\hich\af37\dbch\af31505\loch\f37 into a single directory}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid2696726 +\par }\pard \ltrpar\ql \li720\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0\pararsid2696726 {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid2696726 \hich\af37\dbch\af31505\loch\f37 +The config file enables support for legacy security policy. Without this configuration switch, you will get an exception like this: +\par }{\rtlch\fcs1 \af0 \ltrch\fcs0 \f0\cf17\insrsid2696726\charrsid2696726 \hich\af0\dbch\af31505\loch\f0 Unhandled Exception: System.NotSupport\hich\af0\dbch\af31505\loch\f0 +edException: This method implicitly uses CAS policy, which has been obsoleted by the .NET Framework. \line +\par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 6.\tab +With administrator rights execute '"PCM-Service.exe" -Install' from this directory +\par \hich\af37\dbch\af31505\loch\f37 7.\tab With administrator rights execute 'net start pcmservice' +\par \hich\af37\dbch\af31505\loch\f37 8.\tab Star\hich\af37\dbch\af31505\loch\f37 t per}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid13713190 \hich\af37\dbch\af31505\loch\f37 fmon and find new PCM* counters}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid10518488 + +\par }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 If you do not want or cannot compile the msr.sys driver you might use a third-party }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid13713190 +\hich\af37\dbch\af31505\loch\f37 open source }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid4335690 \hich\af37\dbch\af31505\loch\f37 W}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 in}{\rtlch\fcs1 \af37 \ltrch\fcs0 +\f37\insrsid4335690 \hich\af37\dbch\af31505\loch\f37 R}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 ing0 driver instead. Instructions: +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0\afs22 \ltrch\fcs0 \f37\fs22\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 1.\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1 +\nowidctlpar\wrapdefault\faauto\ls1\rin0\lin720\itap0\pararsid16320623 {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 Download the free RealTemp utility }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid8218016 +\hich\af37\dbch\af31505\loch\f37 package }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 from }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16320623 \hich\af31506\dbch\af31505\loch\f31506 + HYPERLINK "http://www.techpowerup.com/realtemp/" }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13713190 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6200000068007400740070003a002f002f007700770077002e00740065006300680070006f00770065007200750070002e0063006f006d002f007200650061006c00740065006d0070002f000000795881f43b1d7f48 +af2c825dc485276300000000a5ab000000007600ff0000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs15\ul\cf2\insrsid16320623 \hich\af31506\dbch\af31505\loch\f31506 http://www.techpowerup.com/realtemp/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 +\af0 \ltrch\fcs0 \insrsid13713190 \hich\af31506\dbch\af31505\loch\f31506 or any other free utility that uses the open-source }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid4335690 \hich\af31506\dbch\af31505\loch\f31506 W}{\rtlch\fcs1 \af0 \ltrch\fcs0 +\insrsid13713190 \hich\af31506\dbch\af31505\loch\f31506 in}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid4335690 \hich\af31506\dbch\af31505\loch\f31506 R}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13713190 \hich\af31506\dbch\af31505\loch\f31506 +ing0 driver (like OpenHardwareMonitor }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13713190 \hich\af31506\dbch\af31505\loch\f31506 HYPERLINK "http://code.google.com/p/open-hardware-monitor/downloads/list" }{\rtlch\fcs1 \af0 +\ltrch\fcs0 \insrsid1847683 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b9400000068007400740070003a002f002f0063006f00640065002e0067006f006f0067006c0065002e0063006f006d002f0070002f006f00700065006e002d00680061007200640077006100720065002d006d006f00 +6e00690074006f0072002f0064006f0077006e006c006f006100640073002f006c006900730074000000795881f43b1d7f48af2c825dc485276300000000a5ab000000ffff000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs15\ul\cf2\insrsid13713190 \hich\af31506\dbch\af31505\loch\f31506 +http://\hich\af31506\dbch\af31505\loch\f31506 code.google.com/p/open-hardware-monitor/downloads/list}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13713190 \hich\af31506\dbch\af31505\loch\f31506 ).}{\rtlch\fcs1 \af0 +\ltrch\fcs0 \insrsid16320623 +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0\afs22 \ltrch\fcs0 \f31506\fs22\insrsid16320623 \hich\af31506\dbch\af31505\loch\f31506 2.\tab}\hich\af31506\dbch\af31505\loch\f31506 Copy WinRing0.dll, WinRing0.sys, WinRing0x64.dll, WinRing0x64.sys files}{ +\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid7106661 \hich\af31506\dbch\af31505\loch\f31506 from there}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16320623 \hich\af31506\dbch\af31505\loch\f31506 into the PCM.exe binary location, into the PCM-Service.exe location and} +{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 into }{\rtlch\fcs1 \af37 \ltrch\fcs0 +\f37\insrsid16320623\charrsid16320623 \hich\af37\dbch\af31505\loch\f37 c:\\windows\\system32}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16320623\charrsid16320623 +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0\afs22 \ltrch\fcs0 \f37\fs22\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 3.\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\ls1\rin0\lin720\itap0\pararsid5335986 +{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 \hich\af37\dbch\af31505\loch\f37 Run the PCM}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid3891247 \hich\af37\dbch\af31505\loch\f37 .exe}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid16320623 +\hich\af37\dbch\af31505\loch\f37 tool and/or go to step 6 (perfmon utility).}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16320623\charrsid9256949 +\par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid9256949 {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\insrsid9256949 +\par \hich\af37\dbch\af31505\loch\f37 Known limitations: +\par }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9256949\charrsid9256949 \hich\af31506\dbch\af31505\loch\f31506 Running PCM.exe under Cygwin shell is possible, but due to incompatibilities of signal}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9256949 +\hich\af31506\dbch\af31505\loch\f31506 s}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9256949\charrsid9256949 \hich\af31506\dbch\af31505\loch\f31506 /event}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9256949 \hich\af31506\dbch\af31505\loch\f31506 s}{\rtlch\fcs1 \af0 +\ltrch\fcs0 \insrsid9256949\charrsid9256949 \hich\af31506\dbch\af31505\loch\f31506 handling between Windows and Cygwin, the PCM may not cleanup PMU configuration after Ctrl+C or Ctrl+Break pressed. The subsequent run of PCM will require to do PMU +\hich\af31506\dbch\af31505\loch\f31506 configuration reset, so adding -r command line option to PCM will be required.}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9256949\charrsid5335986 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210030dd4329a8060000a41b0000160000007468656d652f7468656d652f +7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87 +615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad +79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b +5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab +999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9 +699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586 +8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6 +0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f +9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be +15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979 +3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d +32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a +f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86 +e877f0034e16bafb0e258ebb4faf06b769e888340b103d331115bebc4eb813bf83291b63624a0d1475a756c734f9bbc2cd28546ecbe1e20a3794ca175f3fae90 +fb6d2dd99bb07b55e5ccf68942bd0877b23c77b908e8db5f9db7f024d9239010f35bd4bbe2fcae387bfff9e2bc289f2fbe24cfaa301468dd8bd846dbb4ddf1c2 +ae7b4c191ba8292337a469bc25ec3d411f06f53a73e224c5292c8de0516732307070a1c0660d125c7d44553488700a4d7bddd3444299910e254ab984c3a219ae +a4adf1d0f82b7bd46cea4388ad1c12ab5d1ed8e1153d9c9f350a3246aad01c6873462b9ac05999ad5cc988826eafc3acae853a33b7ba11cd1445875ba1b236b1 +399483c90bd560b0b0263435085a21b0f22a9cf9356b38ec6046026d77eba3dc2dc60b17e92219e180643ed27acffba86e9c94c7ca9c225a0f1b0cfae0788ad5 +4adc5a9aec1b703b8b93caec1a0bd8e5de7b132fe5113cf312503b998e2c2927274bd051db6b35979b1ef271daf6c6704e86c73805af4bdd476216c26593af84 +0dfb5393d964f9cc9bad5c313709ea70f561ed3ea7b053075221d51696910d0d339585004b34272bff7213cc7a510a5454a3b349b1b206c1f0af490176745d4b +c663e2abb2b34b23da76f6352ba57ca2881844c1111ab189d8c7e07e1daaa04f40255c77988aa05fe06e4e5bdb4cb9c5394bbaf28d98c1d971ccd20867e556a7 +689ec9166e0a522183792b8907ba55ca6e943bbf2a26e52f48957218ffcf54d1fb09dc3eac04da033e5c0d0b8c74a6b43d2e54c4a10aa511f5fb021a07533b20 +5ae07e17a621a8e082dafc17e450ffb739676998b48643a4daa7211214f623150942f6a02c99e83b85583ddbbb2c4996113211551257a656ec1139246ca86be0 +aadedb3d1441a89b6a929501833b197fee7b9641a3503739e57c732a59b1f7da1cf8a73b1f9bcca0945b874d4393dbbf10b1680f66bbaa5d6f96e77b6f59113d +316bb31a795600b3d256d0cad2fe354538e7566b2bd69cc6cbcd5c38f0e2bcc63058344429dc2121fd07f63f2a7c66bf76e80d75c8f7a1b622f878a18941d840 +545fb28d07d205d20e8ea071b283369834296bdaac75d256cb37eb0bee740bbe278cad253b8bbfcf69eca23973d939b97891c6ce2cecd8da8e2d343578f6648a +c2d0383fc818c798cf64e52f597c740f1cbd05df0c264c49134cf09d4a60e8a107260f20f92d47b374e32f000000ffff0300504b030414000600080000002100 +0dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f7 +8277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89 +d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd500 +1996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0f +bfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6 +a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a +0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021 +0030dd4329a8060000a41b00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d001400060008 +00000021000dd1909fb60000001b0100002700000000000000000000000000b20900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ad0a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000070de +0f0fe865cf01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/WinMSRDriver/Win7/makefile b/WinMSRDriver/Win7/makefile new file mode 100644 index 0000000..05a507b --- /dev/null +++ b/WinMSRDriver/Win7/makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def \ No newline at end of file diff --git a/WinMSRDriver/Win7/msr.h b/WinMSRDriver/Win7/msr.h new file mode 100644 index 0000000..20a9315 --- /dev/null +++ b/WinMSRDriver/Win7/msr.h @@ -0,0 +1,28 @@ +#ifndef MSR_INCLUDED +#define MSR_INCLUDED + +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* + written by Roman Dementiev +*/ + + +#include +#include +#include +#include +#include "msrstruct.h" + + +#endif diff --git a/WinMSRDriver/Win7/msrmain.c b/WinMSRDriver/Win7/msrmain.c new file mode 100644 index 0000000..d0a8e4f --- /dev/null +++ b/WinMSRDriver/Win7/msrmain.c @@ -0,0 +1,256 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + + WARNING: This driver code is only for testing purposes, not for production use +*/ + +#include "msr.h" +#include "ntdef.h" +#include + + +/*! \file msrmain.cpp + \brief Test Windows 7 Model Specific Driver implementation +*/ + +#define NT_DEVICE_NAME L"\\Driver\\RDMSR" +#define DOS_DEVICE_NAME L"\\DosDevices\\RDMSR" + + +DRIVER_INITIALIZE DriverEntry; + +__drv_dispatchType(IRP_MJ_CREATE) +__drv_dispatchType(IRP_MJ_CLOSE) +DRIVER_DISPATCH dummyFunction; + +__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH deviceControl; + +DRIVER_UNLOAD MSRUnload; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#pragma alloc_text(PAGE,MSRUnload) +#pragma alloc_text(PAGE,dummyFunction) +#pragma alloc_text(PAGE,deviceControl) +#endif + + +NTSTATUS +DriverEntry( + __in PDRIVER_OBJECT DriverObject, + __in PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING UnicodeString; + UNICODE_STRING dosDeviceName, RegName; + UNICODE_STRING RegValueName; + PDEVICE_OBJECT MSRSystemDeviceObject = NULL; + OBJECT_ATTRIBUTES RegKeyAttributes; + HANDLE RegKey; + UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 1024]; + ULONG resultLength; + PWCHAR domainStr; + ULONG domainStrL; + int i = 0; + + UNREFERENCED_PARAMETER(RegistryPath); + + RtlInitUnicodeString(&UnicodeString, NT_DEVICE_NAME); + RtlInitUnicodeString(&dosDeviceName, DOS_DEVICE_NAME); + + status = IoCreateDevice(DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &MSRSystemDeviceObject + ); + + if (!NT_SUCCESS(status)) + return status; + + DriverObject->DriverUnload = MSRUnload; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = dummyFunction; + DriverObject->MajorFunction[IRP_MJ_CREATE] = dummyFunction; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = deviceControl; + + + IoCreateSymbolicLink(&dosDeviceName, &UnicodeString); + + return status; +} + + +NTSTATUS dummyFunction(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE(); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + +VOID MSRUnload(PDRIVER_OBJECT DriverObject) +{ + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + UNICODE_STRING nameString; + + PAGED_CODE(); + + RtlInitUnicodeString(&nameString, DOS_DEVICE_NAME); + + IoDeleteSymbolicLink(&nameString); + + if (deviceObject != NULL) + { + IoDeleteDevice(deviceObject); + } +} + + +NTSTATUS deviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpStackLocation = NULL; + struct MSR_Request * input_msr_req = NULL; + struct PCICFG_Request * input_pcicfg_req = NULL; + ULONG64 * output = NULL; + GROUP_AFFINITY old_affinity, new_affinity; + ULONG currentGroupSize; + ULONG inputSize = 0; + PCI_SLOT_NUMBER slot; + unsigned size = 0; + + PAGED_CODE(); + + IrpStackLocation = IoGetCurrentIrpStackLocation(Irp); + + if (IrpStackLocation) + { + inputSize = IrpStackLocation->Parameters.DeviceIoControl.InputBufferLength; + + if (IrpStackLocation->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(ULONG64)) + { + input_msr_req = (struct MSR_Request *)Irp->AssociatedIrp.SystemBuffer; + input_pcicfg_req = (struct PCICFG_Request *)Irp->AssociatedIrp.SystemBuffer; + output = (ULONG64 *)Irp->AssociatedIrp.SystemBuffer; + + switch (IrpStackLocation->Parameters.DeviceIoControl.IoControlCode) + { + case IO_CTL_MSR_WRITE: + if (inputSize < sizeof(struct MSR_Request)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + memset(&new_affinity, 0, sizeof(GROUP_AFFINITY)); + memset(&old_affinity, 0, sizeof(GROUP_AFFINITY)); + new_affinity.Group = 0; + while ((ULONG)input_msr_req->core_id >= (currentGroupSize = KeQueryMaximumProcessorCountEx(new_affinity.Group))) + { + input_msr_req->core_id -= currentGroupSize; + ++new_affinity.Group; + } + new_affinity.Mask = 1ULL << (input_msr_req->core_id); + KeSetSystemGroupAffinityThread(&new_affinity, &old_affinity); + __writemsr(input_msr_req->msr_address, input_msr_req->write_value); + KeRevertToUserGroupAffinityThread(&old_affinity); + Irp->IoStatus.Information = 0; // result size + break; + case IO_CTL_MSR_READ: + if (inputSize < sizeof(struct MSR_Request)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + memset(&new_affinity, 0, sizeof(GROUP_AFFINITY)); + memset(&old_affinity, 0, sizeof(GROUP_AFFINITY)); + new_affinity.Group = 0; + while ((ULONG)input_msr_req->core_id >= (currentGroupSize = KeQueryMaximumProcessorCountEx(new_affinity.Group))) + { + input_msr_req->core_id -= currentGroupSize; + ++new_affinity.Group; + } + new_affinity.Mask = 1ULL << (input_msr_req->core_id); + KeSetSystemGroupAffinityThread(&new_affinity, &old_affinity); + *output = __readmsr(input_msr_req->msr_address); + KeRevertToUserGroupAffinityThread(&old_affinity); + Irp->IoStatus.Information = sizeof(ULONG64); // result size + break; + case IO_CTL_PCICFG_WRITE: + if (inputSize < sizeof(struct PCICFG_Request) || (input_pcicfg_req->bytes != 4 && input_pcicfg_req->bytes != 8)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + slot.u.AsULONG = 0; + slot.u.bits.DeviceNumber = input_pcicfg_req->dev; + slot.u.bits.FunctionNumber = input_pcicfg_req->func; + size = HalSetBusDataByOffset(PCIConfiguration, input_pcicfg_req->bus, slot.u.AsULONG, + &(input_pcicfg_req->write_value), input_pcicfg_req->reg, input_pcicfg_req->bytes); + if (size != input_pcicfg_req->bytes) + { + status = STATUS_INVALID_PARAMETER; + break; + } + Irp->IoStatus.Information = 0; // result size + break; + case IO_CTL_PCICFG_READ: + if (inputSize < sizeof(struct PCICFG_Request) || (input_pcicfg_req->bytes != 4 && input_pcicfg_req->bytes != 8)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + slot.u.AsULONG = 0; + slot.u.bits.DeviceNumber = input_pcicfg_req->dev; + slot.u.bits.FunctionNumber = input_pcicfg_req->func; + size = HalGetBusDataByOffset(PCIConfiguration, input_pcicfg_req->bus, slot.u.AsULONG, + output, input_pcicfg_req->reg, input_pcicfg_req->bytes); + if (size != input_pcicfg_req->bytes) + { + status = STATUS_INVALID_PARAMETER; + break; + } + Irp->IoStatus.Information = sizeof(ULONG64); // result size + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + else + status = STATUS_INVALID_PARAMETER; + } + else + status = STATUS_INVALID_DEVICE_REQUEST; + + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} diff --git a/WinMSRDriver/Win7/msrstruct.h b/WinMSRDriver/Win7/msrstruct.h new file mode 100644 index 0000000..ceeb262 --- /dev/null +++ b/WinMSRDriver/Win7/msrstruct.h @@ -0,0 +1,50 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + written by Roman Dementiev +*/ + +#ifndef MSR_STRUCT_HEADER +#define MSR_STRUCT_HEADER + + +#ifndef CTL_CODE +#include +#endif + +#define MSR_DEV_TYPE 50000 + +#define IO_CTL_MSR_READ CTL_CODE(MSR_DEV_TYPE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IO_CTL_MSR_WRITE CTL_CODE(MSR_DEV_TYPE, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IO_CTL_PCICFG_READ CTL_CODE(MSR_DEV_TYPE, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IO_CTL_PCICFG_WRITE CTL_CODE(MSR_DEV_TYPE, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) + +struct MSR_Request +{ + int core_id; + ULONG64 msr_address; + ULONG64 write_value; /* value to write if write requet + ignored if read request */ +}; + +struct PCICFG_Request +{ + ULONG bus, dev, func, reg, bytes; + // "bytes" can be only 4 or 8 + /* value to write if write request ignored if read request */ + ULONG64 write_value; +}; + + +#endif diff --git a/WinMSRDriver/Win7/mymake.bat b/WinMSRDriver/Win7/mymake.bat new file mode 100644 index 0000000..06f1767 --- /dev/null +++ b/WinMSRDriver/Win7/mymake.bat @@ -0,0 +1,9 @@ +set BUILD_PATH= + +rmdir /S /Q objfre_win7_amd64 +nmake + +rem chdir objfre_win7_amd64\amd64 +rem copy msr.sys c:\ +rem chdir .. +rem chdir .. \ No newline at end of file diff --git a/WinMSRDriver/Win7/sources b/WinMSRDriver/Win7/sources new file mode 100644 index 0000000..4f097a9 --- /dev/null +++ b/WinMSRDriver/Win7/sources @@ -0,0 +1,24 @@ +TARGETNAME=msr +TARGETTYPE=DRIVER +NTDDI_VERSION=NTDDI_WIN7 + +MSC_WARNING_LEVEL=/W3 /WX + + +INCLUDES=\ + $(DDK_INC_PATH); + +TARGETLIBS=\ + $(DDK_LIB_PATH)\ntoskrnl.lib + + +SOURCES=msrmain.c + +PRECOMPILED_INCLUDE=msr.h + + + + + + + diff --git a/WinMSRDriver/WinXP/makefile b/WinMSRDriver/WinXP/makefile new file mode 100644 index 0000000..05a507b --- /dev/null +++ b/WinMSRDriver/WinXP/makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def \ No newline at end of file diff --git a/WinMSRDriver/WinXP/msr.h b/WinMSRDriver/WinXP/msr.h new file mode 100644 index 0000000..0d50a9f --- /dev/null +++ b/WinMSRDriver/WinXP/msr.h @@ -0,0 +1,27 @@ +#ifndef MSR_INCLUDED +#define MSR_INCLUDED +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* + written by Roman Dementiev +*/ + + +#include +#include +#include +#include +#include "msrstruct.h" + + +#endif diff --git a/WinMSRDriver/WinXP/msrmain.c b/WinMSRDriver/WinXP/msrmain.c new file mode 100644 index 0000000..5ce3f30 --- /dev/null +++ b/WinMSRDriver/WinXP/msrmain.c @@ -0,0 +1,191 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* + written by Roman Dementiev + + WARNING: This driver code is only for testing purposes, not for production use +*/ + +#include "msr.h" +#include "ntdef.h" + + +/*! \file msrmain.cpp + \brief Test Windows 7 Model Specific Driver implementation +*/ + +#define NT_DEVICE_NAME L"\\Driver\\RDMSR" +#define DOS_DEVICE_NAME L"\\DosDevices\\RDMSR" + + +DRIVER_INITIALIZE DriverEntry; + + +__drv_dispatchType(IRP_MJ_CREATE) +__drv_dispatchType(IRP_MJ_CLOSE) +DRIVER_DISPATCH dummyFunction; + + +__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH deviceControl; + + +DRIVER_UNLOAD MSRUnload; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#pragma alloc_text(PAGE,MSRUnload) +#pragma alloc_text(PAGE,dummyFunction) +#pragma alloc_text(PAGE,deviceControl) +#endif + +NTSTATUS +DriverEntry( + __in PDRIVER_OBJECT DriverObject, + __in PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING UnicodeString; + UNICODE_STRING dosDeviceName, RegName; + UNICODE_STRING RegValueName; + PDEVICE_OBJECT MSRSystemDeviceObject = NULL; + OBJECT_ATTRIBUTES RegKeyAttributes; + HANDLE RegKey; + UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 1024]; + ULONG resultLength; + PWCHAR domainStr; + ULONG domainStrL; + int i = 0; + + UNREFERENCED_PARAMETER(RegistryPath); + + RtlInitUnicodeString(&UnicodeString, NT_DEVICE_NAME); + RtlInitUnicodeString(&dosDeviceName, DOS_DEVICE_NAME); + + status = IoCreateDevice(DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &MSRSystemDeviceObject + ); + + if (!NT_SUCCESS(status)) + return status; + + + DriverObject->DriverUnload = MSRUnload; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = dummyFunction; + DriverObject->MajorFunction[IRP_MJ_CREATE] = dummyFunction; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = deviceControl; + + + IoCreateSymbolicLink(&dosDeviceName, &UnicodeString); + + + return status; +} + + +NTSTATUS dummyFunction(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE(); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + +VOID MSRUnload(PDRIVER_OBJECT DriverObject) +{ + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + UNICODE_STRING nameString; + + PAGED_CODE(); + + RtlInitUnicodeString(&nameString, DOS_DEVICE_NAME); + + IoDeleteSymbolicLink(&nameString); + + if (deviceObject != NULL) + { + IoDeleteDevice(deviceObject); + } +} + + +NTSTATUS deviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpStackLocation = NULL; + struct MSR_Request * input = NULL; + ULONG64 * output = NULL; + KAFFINITY old_affinity, new_affinity; + + PAGED_CODE(); + + IrpStackLocation = IoGetCurrentIrpStackLocation(Irp); + + if (IrpStackLocation) + { + if (IrpStackLocation->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(struct MSR_Request) + && IrpStackLocation->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(ULONG64)) + { + input = (struct MSR_Request *)Irp->AssociatedIrp.SystemBuffer; + output = (ULONG64 *)input; + + switch (IrpStackLocation->Parameters.DeviceIoControl.IoControlCode) + { + case IO_CTL_MSR_WRITE: + new_affinity = 1ULL << (input->core_id); + KeSetSystemAffinityThread(new_affinity); + __writemsr(input->msr_address, input->write_value); + KeRevertToUserAffinityThread(); + Irp->IoStatus.Information = 0; // result size + break; + case IO_CTL_MSR_READ: + new_affinity = 1ULL << (input->core_id); + KeSetSystemAffinityThread(new_affinity); + *output = __readmsr(input->msr_address); + KeRevertToUserAffinityThread(); + Irp->IoStatus.Information = sizeof(ULONG64); // result size + break; + default: + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + else + status = STATUS_INVALID_DEVICE_REQUEST; + } + else + status = STATUS_INVALID_DEVICE_REQUEST; + + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} diff --git a/WinMSRDriver/WinXP/msrstruct.h b/WinMSRDriver/WinXP/msrstruct.h new file mode 100644 index 0000000..160c928 --- /dev/null +++ b/WinMSRDriver/WinXP/msrstruct.h @@ -0,0 +1,40 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + written by Roman Dementiev +*/ + +#ifndef MSR_STRUCT_HEADER +#define MSR_STRUCT_HEADER + + +#ifndef CTL_CODE +#include +#endif + +#define MSR_DEV_TYPE 50000 + +#define IO_CTL_MSR_READ CTL_CODE(MSR_DEV_TYPE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IO_CTL_MSR_WRITE CTL_CODE(MSR_DEV_TYPE, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) + +struct MSR_Request +{ + int core_id; + ULONG64 msr_address; + ULONG64 write_value; /* value to write if write requet + ignored if read request */ +}; + + +#endif diff --git a/WinMSRDriver/WinXP/mymake.bat b/WinMSRDriver/WinXP/mymake.bat new file mode 100644 index 0000000..1f7d078 --- /dev/null +++ b/WinMSRDriver/WinXP/mymake.bat @@ -0,0 +1,4 @@ +set BUILD_PATH= + +nmake + diff --git a/WinMSRDriver/WinXP/sources b/WinMSRDriver/WinXP/sources new file mode 100644 index 0000000..35f5704 --- /dev/null +++ b/WinMSRDriver/WinXP/sources @@ -0,0 +1,16 @@ +TARGETNAME=msr +TARGETTYPE=DRIVER + +MSC_WARNING_LEVEL=/W3 /WX + + +SOURCES=msrmain.c + +PRECOMPILED_INCLUDE=msr.h + + + + + + + diff --git a/build_all.bat b/build_all.bat new file mode 100644 index 0000000..28c6f5d --- /dev/null +++ b/build_all.bat @@ -0,0 +1,35 @@ + +REM change path to your VCVARS.BAT +CALL "c:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\vcvars32.bat" + +for %%p in (PCM) do ( + @echo Building %%p + chdir %%p_Win + vcupgrade -overwrite %%p.vcproj + msbuild %%p.vcxproj + chdir .. +) + + @echo Building Intelpcm.dll + chdir Intelpcm.dll + vcupgrade -overwrite Intelpcm.dll.vcproj + msbuild Intelpcm.dll.vcxproj + chdir .. + + @echo Building PCM-Service + chdir PCM-Service_Win + vcupgrade -overwrite PCMService.vcproj + msbuild PCMService.vcxproj + chdir .. + + +for %%p in (PCM-MSR PCM-TSX PCM-Memory PCM-NUMA PCM-PCIE PCM-Power) do ( + @echo Building %%p + chdir %%p_Win + vcupgrade -overwrite %%p-win.vcproj + msbuild %%p-win.vcxproj + chdir .. +) + + + diff --git a/check_win_build.sh b/check_win_build.sh new file mode 100644 index 0000000..a9086ab --- /dev/null +++ b/check_win_build.sh @@ -0,0 +1,6 @@ + +git log | head -1 + + +cmd /k build_all.bat | egrep 'Error' + diff --git a/client_bw.cpp b/client_bw.cpp new file mode 100644 index 0000000..d5f351e --- /dev/null +++ b/client_bw.cpp @@ -0,0 +1,232 @@ +/* +Copyright (c) 2009-2013, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev, +// Patrick Konsor +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pci.h" +#include "client_bw.h" + +#ifndef _MSC_VER +#include +#include +#endif + +#ifdef _MSC_VER + +#include + +class PCMPmem: public WinPmem { + protected: + virtual int load_driver_() + { + SYSTEM_INFO sys_info; + ZeroMemory(&sys_info, sizeof(sys_info)); + + GetCurrentDirectory(MAX_PATH - 10, driver_filename); + + GetNativeSystemInfo(&sys_info); + switch(sys_info.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_AMD64: + wcscat_s(driver_filename, MAX_PATH, L"\\winpmem_64.sys"); + if(GetFileAttributes(driver_filename) == INVALID_FILE_ATTRIBUTES) + { + std::cout << "ERROR: winpmem_64.sys not found in current directory. Download it from https://volatility.googlecode.com/svn-history/r2813/branches/scudette/tools/windows/winpmem/binaries/winpmem_64.sys ." << std::endl; + std::cout << "ERROR: Memory bandwidth statistics will not be available." << std::endl; + } + break; + case PROCESSOR_ARCHITECTURE_INTEL: + wcscat_s(driver_filename, MAX_PATH, L"\\winpmem_32.sys"); + if(GetFileAttributes(driver_filename) == INVALID_FILE_ATTRIBUTES) + { + std::cout << "ERROR: winpmem_32.sys not found in current directory. Download it from https://volatility.googlecode.com/svn-history/r2813/branches/scudette/tools/windows/winpmem/binaries/winpmem_32.sys ." << std::endl; + std::cout << "ERROR: Memory bandwidth statistics will not be available." << std::endl; + } + break; + default: + return -1; + } + return 1; + } + virtual int write_crashdump_header_(struct PmemMemoryInfo *info) + { + return -1; + } +}; + +ClientBW::ClientBW() : pmem(new PCMPmem()) +{ + pmem->install_driver(false); + pmem->set_acquisition_mode(PMEM_MODE_IOSPACE); + + PciHandleM imcHandle(0,0,0,0); // memory controller device coordinates: domain 0, bus 0, device 0, function 0 + uint64 imcbar = 0; + imcHandle.read64(PCM_CLIENT_IMC_BAR_OFFSET, &imcbar); + // std::cout << "DEBUG: imcbar="<read32(startAddr + PCM_CLIENT_IMC_DRAM_DATA_READS); + ReleaseMutex(Mutex); + return res; +} + +uint64 ClientBW::getImcWrites() +{ + WaitForSingleObject(Mutex,INFINITE); + uint32 res = pmem->read32(startAddr + PCM_CLIENT_IMC_DRAM_DATA_WRITES); + ReleaseMutex(Mutex); + return res; +} + +uint64 ClientBW::getIoRequests() +{ + WaitForSingleObject(Mutex,INFINITE); + uint32 res = pmem->read32(startAddr + PCM_CLIENT_IMC_DRAM_IO_REQESTS); + ReleaseMutex(Mutex); + return res; +} + +ClientBW::~ClientBW() +{ + pmem->uninstall_driver(); + delete pmem; + CloseHandle(Mutex); +} + + +#elif __APPLE__ + +#include "PCIDriverInterface.h" + +#define CLIENT_BUS 0 +#define CLIENT_DEV 0 +#define CLIENT_FUNC 0 +#define CLIENT_BAR_MASK 0x0007FFFFF8000LL +#define CLIENT_EVENT_BASE 0x5000 + +ClientBW::ClientBW() +{ + uint64_t bar = 0; + uint32_t pci_address = FORM_PCI_ADDR(CLIENT_BUS, CLIENT_DEV, CLIENT_FUNC, PCM_CLIENT_IMC_BAR_OFFSET); + PCIDriver_read64(pci_address, &bar); + uint64_t physical_address = (bar & CLIENT_BAR_MASK) + CLIENT_EVENT_BASE;//bar & (~(4096-1)); + mmapAddr = NULL; + if (physical_address) { + PCIDriver_mapMemory((uint32_t)physical_address, (uint8_t**)&mmapAddr); + } +} + +uint64 ClientBW::getImcReads() +{ + uint32_t val = 0; + PCIDriver_readMemory32((uint8_t*)mmapAddr + PCM_CLIENT_IMC_DRAM_DATA_READS - CLIENT_EVENT_BASE, &val); + return (uint64_t)val; +} + +uint64 ClientBW::getImcWrites() +{ + uint32_t val = 0; + PCIDriver_readMemory32((uint8_t*)mmapAddr + PCM_CLIENT_IMC_DRAM_DATA_WRITES - CLIENT_EVENT_BASE, &val); + return (uint64_t)val; +} + +uint64 ClientBW::getIoRequests() +{ + uint32_t val = 0; + PCIDriver_readMemory32((uint8_t*)mmapAddr + PCM_CLIENT_IMC_DRAM_IO_REQESTS - CLIENT_EVENT_BASE, &val); + return (uint64_t)val; +} + +ClientBW::~ClientBW() { + PCIDriver_unmapMemory((uint8_t*)mmapAddr); +} + +#else + +#if defined(__linux__) || defined(__FreeBSD__) +// Linux implementation + +ClientBW::ClientBW() : + fd(-1), + mmapAddr(NULL) +{ + int handle = ::open("/dev/mem", O_RDONLY); + if (handle < 0) throw std::exception(); + fd = handle; + + PciHandleM imcHandle(0,0,0,0); // memory controller device coordinates: domain 0, bus 0, device 0, function 0 + uint64 imcbar = 0; + imcHandle.read64(PCM_CLIENT_IMC_BAR_OFFSET, &imcbar); + // std::cout << "DEBUG: imcbar="<= 0) ::close(fd); +} + +#endif + +#endif diff --git a/client_bw.h b/client_bw.h new file mode 100644 index 0000000..d433361 --- /dev/null +++ b/client_bw.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2012-2013, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// + +#ifndef CPUCounters_CLIENTBW_H +#define CPUCounters_CLIENTBW_H + +/*! \file client_bw.h + \brief Interface to access client bandwidth counters + +*/ + +#include "types.h" + +#ifdef _MSC_VER +#include "windows.h" +#include "winpmem\winpmem.h" +#else +#include +#endif + +#define PCM_CLIENT_IMC_BAR_OFFSET (0x0048) +#define PCM_CLIENT_IMC_DRAM_IO_REQESTS (0x5048) +#define PCM_CLIENT_IMC_DRAM_DATA_READS (0x5050) +#define PCM_CLIENT_IMC_DRAM_DATA_WRITES (0x5054) +#define PCM_CLIENT_IMC_MMAP_SIZE (0x6000) + + +class ClientBW +{ +#if defined(__linux__) || defined(__FreeBSD__) + int32 fd; + char * mmapAddr; +#endif +#ifdef __APPLE__ + char * mmapAddr; +#endif +#ifdef _MSC_VER + WinPmem * pmem; + uint64 startAddr; + HANDLE Mutex; +#endif + +public: + ClientBW(); + + uint64 getImcReads(); + uint64 getImcWrites(); + uint64 getIoRequests(); + + ~ClientBW(); +}; + + +#endif diff --git a/cpuasynchcounter.h b/cpuasynchcounter.h new file mode 100644 index 0000000..29e77d2 --- /dev/null +++ b/cpuasynchcounter.h @@ -0,0 +1,198 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// +// asynchonous CPU conters +// +// contact: Thomas Willhalm + +#ifndef CPUASYNCHCOUNTER_HEADER +#define CPUASYNCHCOUNTER_HEADER + + +/*! \file cpuasynchcounter.h + \brief Implementation of a POSIX thread that periodically saves the current state of counters and exposes them to other threads +*/ + +#include +#include +#include "cpucounters.h" + +#define DELAY 1 // in seconds + +using namespace std; + + +void * UpdateCounters(void *); + +class AsynchronCounterState { + PCM * m; + + CoreCounterState * cstates1, * cstates2; + SocketCounterState * skstates1, * skstates2; + SystemCounterState sstate1, sstate2; + + pthread_t UpdateThread; + pthread_mutex_t CounterMutex; + + friend void * UpdateCounters(void *); + +// AsynchronCounterState(const& AsynchronCounterState); //unimplemeted +// const& AsynchronCounterState operator=(const& AsynchronCounterState); //unimplemented + +public: + AsynchronCounterState() + { + m = PCM::getInstance(); + PCM::ErrorCode status = m->program(); + if(status != PCM::Success) + { + cout << "\nCan not access CPU counters. Try to run pcm.x 1 to check the PMU access status.\n" << endl; + exit(-1); + } + + cstates1 = new CoreCounterState[m->getNumCores()]; + cstates2 = new CoreCounterState[m->getNumCores()]; + skstates1 = new SocketCounterState[m->getNumSockets()]; + skstates2 = new SocketCounterState[m->getNumSockets()]; + + for (uint32 i = 0; i < m->getNumCores(); ++i) { + cstates1[i] = getCoreCounterState(i); + cstates2[i] = getCoreCounterState(i); + } + + for (uint32 i = 0; i < m->getNumSockets(); ++i) { + skstates1[i] = getSocketCounterState(i); + skstates2[i] = getSocketCounterState(i); + } + + pthread_mutex_init(&CounterMutex, NULL); + pthread_create(&UpdateThread, NULL, UpdateCounters, this); + } + ~AsynchronCounterState() + { + pthread_cancel(UpdateThread); + pthread_mutex_destroy(&CounterMutex); + m->cleanup(); + delete[] cstates1; + delete[] cstates2; + delete[] skstates1; + delete[] skstates2; + } + + uint32 getNumCores() + { return m->getNumCores(); } + + uint32 getNumSockets() + { return m->getNumSockets(); } + + uint32 getQPILinksPerSocket() + { + return m->getQPILinksPerSocket(); + } + + uint32 getSocketId(uint32 c) + { + return m->getSocketId(c); + } + + template + T get(uint32 core) + { + pthread_mutex_lock(&CounterMutex); + T value = func(cstates1[core], cstates2[core]); + pthread_mutex_unlock(&CounterMutex); + return value; + } + + template + T get(int param, uint32 core) + { + pthread_mutex_lock(&CounterMutex); + T value = func(param, cstates1[core], cstates2[core]); + pthread_mutex_unlock(&CounterMutex); + return value; + } + + template + T getSocket(uint32 socket) + { + pthread_mutex_lock(&CounterMutex); + T value = func(skstates1[socket], skstates2[socket]); + pthread_mutex_unlock(&CounterMutex); + return value; + } + + template + T getSocket(int param, uint32 socket) + { + pthread_mutex_lock(&CounterMutex); + T value = func(param, skstates1[socket], skstates2[socket]); + pthread_mutex_unlock(&CounterMutex); + return value; + } + + template + T getSocket(uint32 socket, uint32 param) + { + pthread_mutex_lock(&CounterMutex); + T value = func(socket, param, sstate1, sstate2); + pthread_mutex_unlock(&CounterMutex); + return value; + } + + template + T getSystem() + { + pthread_mutex_lock(&CounterMutex); + T value = func(sstate1, sstate2); + pthread_mutex_unlock(&CounterMutex); + return value; + } + + template + T getSystem(int param) + { + pthread_mutex_lock(&CounterMutex); + T value = func(param, sstate1, sstate2); + pthread_mutex_unlock(&CounterMutex); + return value; + } + +}; + +void * UpdateCounters(void * state) +{ + AsynchronCounterState * s = (AsynchronCounterState *)state; + + while (true) { + pthread_mutex_lock(&(s->CounterMutex)); + for (uint32 core = 0; core < s->m->getNumCores(); ++core) { + s->cstates1[core] = s->cstates2[core]; + s->cstates2[core] = s->m->getCoreCounterState(core); + } + + for (uint32 socket = 0; socket < s->m->getNumSockets(); ++socket) { + s->skstates1[socket] = s->skstates2[socket]; + s->skstates2[socket] = s->m->getSocketCounterState(socket); + } + + s->sstate1 = s->sstate2; + s->sstate2 = s->m->getSystemCounterState(); + + pthread_mutex_unlock(&(s->CounterMutex)); + sleep(1); + } + return NULL; +} + +#endif diff --git a/cpucounters.cpp b/cpucounters.cpp new file mode 100644 index 0000000..bbb5e4b --- /dev/null +++ b/cpucounters.cpp @@ -0,0 +1,4406 @@ +/* +Copyright (c) 2009-2014, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// Otto Bruggeman +// Thomas Willhalm +// Pat Fay +// Austen Ott +// Jim Harris (FreeBSD) + +/*! \file cpucounters.cpp + \brief The bulk of Intel PCM implementation + */ + +//#define PCM_TEST_FALLBACK_TO_ATOM + +#include +#include +#include +#ifdef INTELPCM_EXPORTS +// Intelpcm.h includes cpucounters.h +#include "Intelpcm.dll\Intelpcm.h" +#else +#include "cpucounters.h" +#endif +#include "msr.h" +#include "pci.h" +#include "types.h" +#include "utils.h" + +#ifdef _MSC_VER +#include +#include +#include +#include +#include "winring0/OlsApiInit.h" +#include "PCM_Win/windriver.h" +#else +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include + +// convertUnknownToInt is used in the safe sysctl call to convert an unkown size to an int +int convertUnknownToInt(size_t size, char* value); + +#endif + +#if defined (__FreeBSD__) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#undef PCM_UNCORE_PMON_BOX_CHECK_STATUS // debug only + +// FreeBSD is much more restrictive about names for semaphores +#if defined (__FreeBSD__) +#define PCM_INSTANCE_LOCK_SEMAPHORE_NAME "/Intel_PCM_inst_lock" +#define PCM_NUM_INSTANCES_SEMAPHORE_NAME "/Intel_num_PCM_inst" +#else +#define PCM_INSTANCE_LOCK_SEMAPHORE_NAME "Intel(r) PCM inst lock" +#define PCM_NUM_INSTANCES_SEMAPHORE_NAME "Num Intel(r) PCM insts" +#endif + +#ifdef _MSC_VER + +HMODULE hOpenLibSys = NULL; + +bool PCM::initWinRing0Lib() +{ + const BOOL result = InitOpenLibSys(&hOpenLibSys); + + if(result == FALSE) hOpenLibSys = NULL; + + return result==TRUE; +} + +class InstanceLock +{ + HANDLE Mutex; + + InstanceLock(); +public: + InstanceLock(const bool global) + { + Mutex = CreateMutex(NULL, FALSE, + global?(L"Global\\Intel(r) Performance Counter Monitor instance create/destroy lock"):(L"Local\\Intel(r) Performance Counter Monitor instance create/destroy lock")); + // lock + WaitForSingleObject(Mutex, INFINITE); + } + ~InstanceLock() + { + // unlock + ReleaseMutex(Mutex); + } +}; +#else // Linux or Apple + +pthread_mutex_t processIntanceMutex = PTHREAD_MUTEX_INITIALIZER; + +class InstanceLock +{ + const char * globalSemaphoreName; + sem_t * globalSemaphore; + bool global; + + InstanceLock(); +public: + InstanceLock(const bool global_) : globalSemaphoreName(PCM_INSTANCE_LOCK_SEMAPHORE_NAME), globalSemaphore(NULL), global(global_) + { + if(!global) + { + pthread_mutex_lock(&processIntanceMutex); + return; + } + umask(0); + while (1) + { + //sem_unlink(globalSemaphoreName); // temporary + globalSemaphore = sem_open(globalSemaphoreName, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 1); + if (SEM_FAILED == globalSemaphore) + { + if (EACCES == errno) + { + std::cerr << "PCM Error, do not have permissions to open semaphores in /dev/shm/. Waiting one second and retrying..." << std::endl; + sleep(1); + } + } + else + { + /* + if (sem_post(globalSemaphore)) { + perror("sem_post error"); + } + */ + break; // success + } + } + if (sem_wait(globalSemaphore)) { + perror("sem_wait error"); + } + } + ~InstanceLock() + { + if(!global) + { + pthread_mutex_unlock(&processIntanceMutex); + return; + } + if (sem_post(globalSemaphore)) { + perror("sem_post error"); + } + } +}; +#endif // end of _MSC_VER else + +PCM * PCM::instance = NULL; + +int bitCount(uint64 n) +{ + int count = 0; + while (n) + { + count += (int)(n & 0x00000001); + n >>= 1; + } + return count; +} + +PCM * PCM::getInstance() +{ + // no lock here + if (instance) return instance; + + InstanceLock lock(false); + if (instance) return instance; + + return instance = new PCM(); +} + +uint32 build_bit_ui(int beg, int end) +{ + uint32 myll = 0; + if (end == 31) + { + myll = (uint32)(-1); + } + else + { + myll = (1 << (end + 1)) - 1; + } + myll = myll >> beg; + return myll; +} + +uint32 extract_bits_ui(uint32 myin, uint32 beg, uint32 end) +{ + uint32 myll = 0; + uint32 beg1, end1; + + // Let the user reverse the order of beg & end. + if (beg <= end) + { + beg1 = beg; + end1 = end; + } + else + { + beg1 = end; + end1 = beg; + } + myll = myin >> beg1; + myll = myll & build_bit_ui(beg1, end1); + return myll; +} + +uint64 build_bit(uint32 beg, uint32 end) +{ + uint64 myll = 0; + if (end == 63) + { + myll = (uint64)(-1); + } + else + { + myll = (1LL << (end + 1)) - 1; + } + myll = myll >> beg; + return myll; +} + +uint64 extract_bits(uint64 myin, uint32 beg, uint32 end) +{ + uint64 myll = 0; + uint32 beg1, end1; + + // Let the user reverse the order of beg & end. + if (beg <= end) + { + beg1 = beg; + end1 = end; + } + else + { + beg1 = end; + end1 = beg; + } + myll = myin >> beg1; + myll = myll & build_bit(beg1, end1); + return myll; +} + +uint64 PCM::extractCoreGenCounterValue(uint64 val) +{ + if(core_gen_counter_width) + return extract_bits(val, 0, core_gen_counter_width-1); + + return val; +} + +uint64 PCM::extractCoreFixedCounterValue(uint64 val) +{ + if(core_fixed_counter_width) + return extract_bits(val, 0, core_fixed_counter_width-1); + + return val; +} + +uint64 PCM::extractUncoreGenCounterValue(uint64 val) +{ + if(uncore_gen_counter_width) + return extract_bits(val, 0, uncore_gen_counter_width-1); + + return val; +} + +uint64 PCM::extractUncoreFixedCounterValue(uint64 val) +{ + if(uncore_fixed_counter_width) + return extract_bits(val, 0, uncore_fixed_counter_width-1); + + return val; +} + +uint64 PCM::extractL3CacheOccupancy(uint64 val) +{ + //Check if any of the error bit(63) or Unavailable bit(62) of the IA32_QM_CTR MSR are 1 + + if(val & (3ULL<<62)) + { + // invalid reading + return PCM_INVALID_L3_CACHE_OCCUPANCY; + } + + // valid reading + return extract_bits(val,0,61); +} +int32 extractThermalHeadroom(uint64 val) +{ + if(val & (1ULL<<31ULL)) + { // valid reading + return (int32)extract_bits(val,16,22); + } + + // invalid reading + return PCM_INVALID_THERMAL_HEADROOM; +} + + +uint64 get_frequency_from_cpuid(); + +union PCM_CPUID_INFO +{ + int array[4]; + struct { int eax,ebx,ecx,edx; } reg ; +}; + +void pcm_cpuid(int leaf, PCM_CPUID_INFO & info) +{ + #ifdef _MSC_VER + // version for Windows + __cpuid(info.array, leaf); + #else + __asm__ __volatile__ ("cpuid" : \ + "=a" (info.reg.eax), "=b" (info.reg.ebx), "=c" (info.reg.ecx), "=d" (info.reg.edx) : "a" (leaf)); + #endif +} + +/* Adding the new version of cpuid with leaf and subleaf as an input */ +void pcm_cpuid(const unsigned leaf, const unsigned subleaf, PCM_CPUID_INFO & info) +{ + #ifdef _MSC_VER + __cpuidex(info.array, leaf, subleaf); + #else + __asm__ __volatile__ ("cpuid" : \ + "=a" (info.reg.eax), "=b" (info.reg.ebx), "=c" (info.reg.ecx), "=d" (info.reg.edx) : "a" (leaf), "c" (subleaf)); + #endif +} + +bool PCM::detectModel() +{ + + char buffer[1024]; + union { + char cbuf[16]; + int ibuf[16/sizeof(int)]; + } buf; + PCM_CPUID_INFO cpuinfo; + int max_cpuid; + pcm_cpuid(0, cpuinfo); + memset(buffer, 0, 1024); + memset(buf.cbuf, 0, 16); + buf.ibuf[0] = cpuinfo.array[1]; + buf.ibuf[1] = cpuinfo.array[3]; + buf.ibuf[2] = cpuinfo.array[2]; + if (strncmp(buf.cbuf, "GenuineIntel", 4 * 3) != 0) + { + std::cerr << getUnsupportedMessage() << std::endl; + return false; + } + max_cpuid = cpuinfo.array[0]; + + pcm_cpuid(1, cpuinfo); + cpu_family = (((cpuinfo.array[0]) >> 8) & 0xf) | ((cpuinfo.array[0] & 0xf00000) >> 16); + cpu_model = original_cpu_model = (((cpuinfo.array[0]) & 0xf0) >> 4) | ((cpuinfo.array[0] & 0xf0000) >> 12); + + if (max_cpuid >= 0xa) + { + // get counter related info + pcm_cpuid(0xa, cpuinfo); + perfmon_version = extract_bits_ui(cpuinfo.array[0], 0, 7); + core_gen_counter_num_max = extract_bits_ui(cpuinfo.array[0], 8, 15); + core_gen_counter_width = extract_bits_ui(cpuinfo.array[0], 16, 23); + if (perfmon_version > 1) + { + core_fixed_counter_num_max = extract_bits_ui(cpuinfo.array[3], 0, 4); + core_fixed_counter_width = extract_bits_ui(cpuinfo.array[3], 5, 12); + } + } + + if (cpu_family != 6) + { + std::cerr << getUnsupportedMessage() << " CPU Family: " << cpu_family << std::endl; + return false; + } + + return true; +} + +bool PCM::L3CacheOccupancyMetricAvailable() +{ + PCM_CPUID_INFO cpuinfo; + pcm_cpuid(0x7,0,cpuinfo); + return (cpuinfo.reg.ebx & (1<<12))?true:false; +} + +unsigned PCM::getMaxRMID() const +{ + unsigned maxRMID = 0; + PCM_CPUID_INFO cpuinfo; + pcm_cpuid(0xf,0,cpuinfo); + maxRMID = (unsigned)cpuinfo.reg.ebx + 1; + return maxRMID; +} + +void PCM::initL3CacheOccupancyMonitoring() +{ + /* Check if the core has CacheMonitoring support */ + if(!L3CacheOccupancyMetricAvailable()) + { + return; + } + + unsigned maxRMID; + + const uint64 event = 1; //L3 Occupancy monitoring + + /* Calculate maximum number of RMID supported by socket */ + maxRMID = getMaxRMID(); + // std::cout << "Maximum RMIDs per socket in the system : " << maxRMID << "\n"; + + std::vector rmid(num_sockets); + + for(int i = 0; i < num_sockets; i ++) + rmid[i] = maxRMID - 1; + + /* Associate each core with 1 RMID */ + + for(int32 core = 0; core < num_cores; core ++ ) + { + if(!isCoreOnline(core)) continue; + uint64 msr_pqr_assoc = 0 ; + uint64 msr_qm_evtsel = 0 ; + + //Read 0xC8F MSR for each core + MSR[core]->read(IA32_PQR_ASSOC, &msr_pqr_assoc); + + //std::cout << "Socket Id : " << topology[core].socket; + msr_pqr_assoc &= 0xffffffff00000000ULL; + msr_pqr_assoc |= (uint64)(rmid[topology[core].socket] & ((1ULL<<10)-1ULL)); + //Write 0xC8F MSR with new RMID for each core + MSR[core]->write(IA32_PQR_ASSOC,msr_pqr_assoc); + + //Write MSR 0xC8D , the event id and rmid for each core + msr_qm_evtsel = rmid[topology[core].socket] & ((1ULL<<10)-1ULL) ; + msr_qm_evtsel <<= 32 ; + msr_qm_evtsel |= event & ((1ULL<<8)-1ULL); + MSR[core]->write(IA32_QM_EVTSEL,msr_qm_evtsel); + + // std::cout << "Assigning RMID " << rmid[topology[core].socket] << " to core " << core << " on socket " << topology[core].socket << "\n"; + rmid[topology[core].socket] --; + } + + /* Get The scaling factor by running CPUID.0xF.0x1 instruction */ + L3ScalingFactor = getL3ScalingFactor(); +} + +void PCM::initCStateSupportTables() +{ +#define PCM_PARAM_PROTECT(...) __VA_ARGS__ +#define PCM_CSTATE_ARRAY(array_ , val ) \ + { \ + static uint64 tmp[] = val; \ + PCM_COMPILE_ASSERT( sizeof(tmp)/sizeof(uint64) == MAX_C_STATE + 1); \ + array_ = tmp; \ + break; \ + } + + // fill package C state array + switch(original_cpu_model) + { + case ATOM: + case ATOM_2: + case ATOM_CENTERTON: + case ATOM_AVOTON: + case ATOM_BAYTRAIL: + PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x3F8, 0, 0x3F9, 0, 0x3FA, 0, 0, 0, 0 }) ); + case NEHALEM_EP: + case NEHALEM: + case CLARKDALE: + case WESTMERE_EP: + case NEHALEM_EX: + case WESTMERE_EX: + PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0, 0, 0}) ); + case SANDY_BRIDGE: + case JAKETOWN: + case IVY_BRIDGE: + case IVYTOWN: + PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x60D, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0, 0, 0}) ); + case HASWELL: + case HASWELL_2: + case HASWELLX: + PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x60D, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0, 0, 0}) ); + case HASWELL_ULT: + case BROADWELL: + PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x60D, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0x630, 0x631, 0x632}) ); + + default: + std::cerr << "PCM error: package C-states support array is not initialized. Package C-states metrics will not be shown." << std::endl; + PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) ); + }; + + // fill core C state array + switch(original_cpu_model) + { + case ATOM: + case ATOM_2: + case ATOM_CENTERTON: + PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) ); + case NEHALEM_EP: + case NEHALEM: + case CLARKDALE: + case WESTMERE_EP: + case NEHALEM_EX: + case WESTMERE_EX: + PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0x3FC, 0, 0, 0x3FD, 0, 0, 0, 0}) ); + case SANDY_BRIDGE: + case JAKETOWN: + case IVY_BRIDGE: + case IVYTOWN: + case HASWELL: + case HASWELL_2: + case HASWELL_ULT: + case HASWELLX: + case BROADWELL: + case ATOM_BAYTRAIL: + case ATOM_AVOTON: + PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0x3FC, 0, 0, 0x3FD, 0x3FE, 0, 0, 0}) ); + default: + std::cerr << "PCM error: core C-states support array is not initialized. Core C-states metrics will not be shown." << std::endl; + PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) ); + }; +} + +bool PCM::discoverSystemTopology() +{ + typedef std::map socketIdMap_type; + socketIdMap_type socketIdMap; + +#ifdef _MSC_VER +// version for Windows + +#ifdef COMPILE_FOR_WINDOWS_7 + DWORD GroupStart[5]; // at most 4 groups on Windows 7 + GroupStart[0] = 0; + GroupStart[1] = GetActiveProcessorCount(0); + GroupStart[2] = GroupStart[1] + GetActiveProcessorCount(1); + GroupStart[3] = GroupStart[2] + GetActiveProcessorCount(2); + GroupStart[4] = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + if (GroupStart[3] + GetActiveProcessorCount(3) != GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)) + { + std::cerr << "Error in processor group size counting (1)" << std::endl; + std::cerr << "Make sure your binary is compiled for 64-bit: using 'x64' platform configuration." << std::endl; + return false; + } + char * slpi = new char[sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)]; + DWORD len = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX); + DWORD res = GetLogicalProcessorInformationEx(RelationAll, (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)slpi, &len); + + while (res == FALSE) + { + delete[] slpi; + + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + slpi = new char[len]; + res = GetLogicalProcessorInformationEx(RelationAll, (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)slpi, &len); + } + else + { + _com_error error(GetLastError()); + std::wcerr << "Error in Windows function 'GetLogicalProcessorInformationEx': " << + GetLastError() << " " << error.ErrorMessage() << std::endl; + return false; + } + } + + char * base_slpi = slpi; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pi = NULL; + + for ( ; slpi < base_slpi + len; slpi += pi->Size) + { + pi = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)slpi; + if (pi->Relationship == RelationProcessorCore) + { + threads_per_core = (pi->Processor.Flags == LTP_PC_SMT) ? 2 : 1; + // std::cout << "thr per core: "<< threads_per_core << std::endl; + num_cores += threads_per_core; + } + } + + + if (num_cores != GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)) + { + std::cerr << "Error in processor group size counting: " << num_cores << "!=" << GetActiveProcessorCount(ALL_PROCESSOR_GROUPS) << std::endl; + std::cerr << "Make sure your binary is compiled for 64-bit: using 'x64' platform configuration." << std::endl; + return false; + } + +#undef PCM_WIN7_USE_CPUID_FOR_TOPOLOGY_ENUMERATION +#ifdef PCM_WIN7_USE_CPUID_FOR_TOPOLOGY_ENUMERATION + PCM_CPUID_INFO cpuid_args; + pcm_cpuid(1, cpuid_args); + + int apic_ids_per_package = (cpuid_args.array[1] & 0x00FF0000) >> 16, apic_ids_per_core; + + if (apic_ids_per_package == 0) + { + std::cout << "apic_ids_per_package == 0" << std::endl; + return false; + } + + pcm_cpuid(0xb, 0x0, cpuid_args); + + if ((cpuid_args.array[2] & 0xFF00) == 0x100) + apic_ids_per_core = cpuid_args.array[1] & 0xFFFF; + else + apic_ids_per_core = 1; + + if (apic_ids_per_core == 0) + { + std::cout << "apic_ids_per_core == 0" << std::endl; + return false; + } + + for (int i = 0; i < num_cores; i++) + { + ThreadGroupTempAffinity affinity(i); + + pcm_cpuid(0xb, cpuid_args); + + int apic_id = cpuid_args.array[3]; + + TopologyEntry entry; + entry.os_id = i; + entry.socket = apic_id / apic_ids_per_package; + entry.core_id = (apic_id % apic_ids_per_package) / apic_ids_per_core; + + topology.push_back(entry); + socketIdMap[entry.socket] = 0; + } +#else + + topology.resize(num_cores); + + slpi = base_slpi; + pi = NULL; + + for ( ; slpi < base_slpi + len; slpi += pi->Size) + { + pi = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)slpi; + if (pi->Relationship == RelationNumaNode) + { + ++num_sockets; + for (unsigned int c = 0; c < (unsigned int)num_cores; ++c) + { + // std::cout << "c:"<NumaNode.GroupMask.Group]: "<NumaNode.GroupMask.Group]<NumaNode.GroupMask.Group] || c >= GroupStart[(pi->NumaNode.GroupMask.Group) + 1]) + { + //std::cout <<"core "<NumaNode.GroupMask.Group << std::endl; + continue; + } + if ((1LL << (c - GroupStart[pi->NumaNode.GroupMask.Group])) & pi->NumaNode.GroupMask.Mask) + { + topology[c].core_id = c; + topology[c].os_id = c; + topology[c].socket = pi->NumaNode.NodeNumber; + // std::cout << "Core "<< c <<" is in NUMA node "<< topology[c].socket << " and belongs to processor group " << slpi->NumaNode.GroupMask.Group <= 0) + { + topology[entry.os_id] = entry; + ++num_online_cores; + if (entry.socket == 0 && entry.core_id == 0) ++threads_per_core; + } + sscanf(buffer, "processor\t: %d", &entry.os_id); + //std::cout << "os_core_id: "<= 0) + { + topology[entry.os_id] = entry; + ++num_online_cores; + if (entry.socket == 0 && entry.core_id == 0) ++threads_per_core; + } + fclose(f_cpuinfo); + +#elif defined(__FreeBSD__) + + size_t size = sizeof(num_cores); + cpuctl_cpuid_args_t cpuid_args; + int fd, apic_ids_per_package, apic_ids_per_core; + + if(0 != sysctlbyname("kern.smp.cpus", &num_cores, &size, NULL, 0)) + { + std::cerr << "Unable to get kern.smp.cpus from sysctl." << std::endl; + return false; + } + + if (modfind("cpuctl") == -1) + { + std::cout << "cpuctl(4) not loaded." << std::endl; + return false; + } + + do_cpuid(1, cpuid_args.data); + + apic_ids_per_package = (cpuid_args.data[1] & 0x00FF0000) >> 16; + + if (apic_ids_per_package == 0) + { + std::cout << "apic_ids_per_package == 0" << std::endl; + return false; + } + + cpuid_count(0xb, 0x0, cpuid_args.data); + + if ((cpuid_args.data[2] & 0xFF00) == 0x100) + apic_ids_per_core = cpuid_args.data[1] & 0xFFFF; + else + apic_ids_per_core = 1; + + if (apic_ids_per_core == 0) + { + std::cout << "apic_ids_per_core == 0" << std::endl; + return false; + } + + for (int i = 0; i < num_cores; i++) + { + char cpuctl_name[64]; + int apic_id; + + sprintf(cpuctl_name, "/dev/cpuctl%d", i); + fd = ::open(cpuctl_name, O_RDWR); + + cpuid_args.level = 0xb; + + ::ioctl(fd, CPUCTL_CPUID, &cpuid_args); + + apic_id = cpuid_args.data[3]; + + entry.os_id = i; + entry.socket = apic_id / apic_ids_per_package; + entry.core_id = (apic_id % apic_ids_per_package) / apic_ids_per_core; + + if (entry.socket == 0 && entry.core_id == 0) ++threads_per_core; + + topology.push_back(entry); + socketIdMap[entry.socket] = 0; + } + +#else // Getting processor info for Mac OS +#define SAFE_SYSCTLBYNAME(message, ret_value) \ + { \ + size_t size; \ + char *pParam; \ + if(0 != sysctlbyname(message, NULL, &size, NULL, 0)) \ + { \ + std::cerr << "Unable to determine size of " << message << " sysctl return type." << std::endl; \ + return false; \ + } \ + if(NULL == (pParam = (char *)malloc(size))) \ + { \ + std::cerr << "Unable to allocate memory for " << message << std::endl; \ + return false; \ + } \ + if(0 != sysctlbyname(message, (void*)pParam, &size, NULL, 0)) \ + { \ + std::cerr << "Unable to get " << message << " from sysctl." << std::endl; \ + return false; \ + } \ + ret_value = convertUnknownToInt(size, pParam); \ + free(pParam); \ + } +// End SAFE_SYSCTLBYNAME + + // Using OSXs sysctl to get the number of CPUs right away + SAFE_SYSCTLBYNAME("hw.logicalcpu", num_cores) + +#undef SAFE_SYSCTLBYNAME + + // The OSX version needs the MSR handle earlier so that it can build the CPU topology. + // This topology functionality should potentially go into a different KEXT + MSR = new SafeMsrHandle *[num_cores]; + for(int i = 0; i < num_cores; i++) + { + MSR[i] = new SafeMsrHandle(i); + } + + TopologyEntry *entries = new TopologyEntry[num_cores]; + MSR[0]->buildTopology(num_cores, entries); + for(int i = 0; i < num_cores; i++){ + socketIdMap[entries[i].socket] = 0; + if(entries[i].os_id >= 0) + { + if(entries[i].core_id == 0 && entries[i].socket == 0) ++threads_per_core; + topology.push_back(entries[i]); + } + } + delete entries; +// End of OSX specific code +#endif // end of ifndef __APPLE__ + +#endif //end of ifdef _MSC_VER + + if(num_cores == 0) { + num_cores = topology.size(); + } + if(num_sockets == 0) { + num_sockets = (std::max)(socketIdMap.size(), (size_t)1); + } + + socketIdMap_type::iterator s = socketIdMap.begin(); + for (uint32 sid = 0; s != socketIdMap.end(); ++s) + { + s->second = sid++; + } + + for (int i = 0; (i < num_cores) && (!socketIdMap.empty()); ++i) + { + if(isCoreOnline(i)) + topology[i].socket = socketIdMap[topology[i].socket]; + } + +#if 0 + std::cerr << "Number of socket ids: " << socketIdMap.size() << "\n"; + std::cerr << "Topology:\nsocket os_id core_id\n"; + for (int i = 0; i < num_cores; ++i) + { + std::cerr << topology[i].socket << " " << topology[i].os_id << " " << topology[i].core_id << std::endl; + } +#endif + + if(threads_per_core == 0) + { + for (int i = 0; i < num_cores; ++i) + { + if(topology[i].socket == topology[0].socket && topology[i].core_id == topology[0].core_id) + ++threads_per_core; + } + } + if(num_phys_cores_per_socket == 0) num_phys_cores_per_socket = num_cores / num_sockets / threads_per_core; + if(num_online_cores == 0) num_online_cores = num_cores; + + int32 i = 0; + + socketRefCore.resize(num_sockets); + for(i = 0; i < num_cores; ++i) + { + if(isCoreOnline(i)) + { + socketRefCore[topology[i].socket] = i; + } + } + +#if 0 + for(int32 i=0; i< num_sockets;++i) + { + std::cout << "socketRefCore["<< i << "]=" << socketRefCore[i] << std::endl; + } +#endif + + return true; +} + +void PCM::printSystemTopology() const +{ + if(num_cores == num_online_cores) + { + std::cerr << "Number of physical cores: " << (num_cores/threads_per_core) << std::endl; + } + + std::cerr << "Number of logical cores: " << num_cores << std::endl; + std::cerr << "Number of online logical cores: " << num_online_cores << std::endl; + + if(num_cores == num_online_cores) + { + std::cerr << "Threads (logical cores) per physical core: " << threads_per_core << std::endl; + } + else + { + std::cerr << "Offlined cores: "; + for (int i = 0; i < num_cores; ++i) + if(isCoreOnline(i) == false) + std::cerr << i << " "; + std::cerr << std::endl; + } + std::cerr << "Num sockets: " << num_sockets << std::endl; + std::cerr << "Physical cores per socket: " << num_phys_cores_per_socket << std::endl; + std::cerr << "Core PMU (perfmon) version: " << perfmon_version << std::endl; + std::cerr << "Number of core PMU generic (programmable) counters: " << core_gen_counter_num_max << std::endl; + std::cerr << "Width of generic (programmable) counters: " << core_gen_counter_width << " bits" << std::endl; + if (perfmon_version > 1) + { + std::cerr << "Number of core PMU fixed counters: " << core_fixed_counter_num_max << std::endl; + std::cerr << "Width of fixed counters: " << core_fixed_counter_width << " bits" << std::endl; + } +} + +void PCM::initMSR() +{ + int32 i = 0; + +#ifndef __APPLE__ + MSR = new SafeMsrHandle *[num_cores]; + try + { + for (i = 0; i < num_cores; ++i) + { + MSR[i] = isCoreOnline(i) ? (new SafeMsrHandle(i)):(new SafeMsrHandle()); // the core is offlined, assign an invalid MSR handle + } + } + catch (...) + { + // failed + for (int j = 0; j < i; j++) + { + if(MSR[j]) + delete MSR[j]; + } + delete[] MSR; + MSR = NULL; + + std::cerr << "Can not access CPUs Model Specific Registers (MSRs)." << std::endl; +#ifdef _MSC_VER + std::cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program." << std::endl; +#elif defined(__linux__) + std::cerr << "Try to execute 'modprobe msr' as root user and then" << std::endl; + std::cerr << "you also must have read and write permissions for /dev/cpu/*/msr devices (/dev/msr* for Android). The 'chown' command can help." << std::endl; +#elif defined(__FreeBSD__) + std::cerr << "Ensure cpuctl module is loaded and that you have read and write" << std::endl; + std::cerr << "permissions for /dev/cpuctl* devices (the 'chown' command can help)." << std::endl; +#endif + + } +#endif +} + +bool PCM::detectNominalFrequency() +{ + if (MSR) + { + uint64 freq = 0; + MSR[socketRefCore[0]]->read(PLATFORM_INFO_ADDR, &freq); + const uint64 bus_freq = ( + cpu_model == SANDY_BRIDGE + || cpu_model == JAKETOWN + || cpu_model == IVYTOWN + || cpu_model == HASWELLX + || cpu_model == IVY_BRIDGE + || cpu_model == HASWELL + || cpu_model == BROADWELL + || original_cpu_model == ATOM_AVOTON + ) ? (100000000ULL) : (133333333ULL); + + nominal_frequency = ((freq >> 8) & 255) * bus_freq; + + if(!nominal_frequency) + nominal_frequency = get_frequency_from_cpuid(); + + if(!nominal_frequency) + { + std::cerr << "Error: Can not detect core frequency." << std::endl; + destroyMSR(); + return false; + } + + std::cerr << "Nominal core frequency: " << nominal_frequency << " Hz" << std::endl; + } + + return true; +} + +void PCM::initEnergyMonitoring() +{ + if(packageEnergyMetricsAvailable() && MSR) + { + uint64 rapl_power_unit = 0; + MSR[socketRefCore[0]]->read(MSR_RAPL_POWER_UNIT,&rapl_power_unit); + uint64 energy_status_unit = extract_bits(rapl_power_unit,8,12); + joulesPerEnergyUnit = 1./double(1ULL<read(MSR_PKG_POWER_INFO,&package_power_info); + pkgThermalSpecPower = (uint32) (double(extract_bits(package_power_info, 0, 14))*wattsPerPowerUnit); + pkgMinimumPower = (uint32) (double(extract_bits(package_power_info, 16, 30))*wattsPerPowerUnit); + pkgMaximumPower = (uint32) (double(extract_bits(package_power_info, 32, 46))*wattsPerPowerUnit); + + std::cerr << "Package thermal spec power: "<< pkgThermalSpecPower << " Watt; "; + std::cerr << "Package minimum power: "<< pkgMinimumPower << " Watt; "; + std::cerr << "Package maximum power: "<< pkgMaximumPower << " Watt; " << std::endl; + + int i = 0; + + if(snb_energy_status.empty()) + for (i = 0; i < num_sockets; ++i) + snb_energy_status.push_back(new CounterWidthExtender(new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[i]],MSR_PKG_ENERGY_STATUS)) ); + + if(dramEnergyMetricsAvailable() && jkt_dram_energy_status.empty()) + for (i = 0; i < num_sockets; ++i) + jkt_dram_energy_status.push_back(new CounterWidthExtender(new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[i]],MSR_DRAM_ENERGY_STATUS))); + } +} + +void PCM::initUncoreObjects() +{ + if (hasPCICFGUncore() && MSR != NULL) + { + server_pcicfg_uncore = new ServerPCICFGUncore *[num_sockets]; + int i = 0; + + try + { + for (i = 0; i < num_sockets; ++i) + { + server_pcicfg_uncore[i] = new ServerPCICFGUncore(i, this); + } + } + catch (...) + { + // failed + for (int j = 0; j < i; j++) + delete server_pcicfg_uncore[j]; + delete[] server_pcicfg_uncore; + + server_pcicfg_uncore = NULL; + + std::cerr << "Can not access Jaketown/Ivytown PCI configuration space. Access to uncore counters (memory and QPI bandwidth) is disabled." << std::endl; + #ifdef _MSC_VER + std::cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program." << std::endl; + #else + //std::cerr << "you must have read and write permissions for /proc/bus/pci/7f/10.* and /proc/bus/pci/ff/10.* devices (the 'chown' command can help)." << std::endl; + //std::cerr << "you must have read and write permissions for /dev/mem device (the 'chown' command can help)."<< std::endl; + //std::cerr << "you must have read permission for /sys/firmware/acpi/tables/MCFG device (the 'chmod' command can help)."<< std::endl; + std::cerr << "You must be root to access these Jaketown/Ivytown counters in PCM. " << std::endl; + #endif + } + } else if((cpu_model == SANDY_BRIDGE || cpu_model == IVY_BRIDGE || cpu_model == HASWELL || cpu_model == BROADWELL) && MSR != NULL) + { + // initialize memory bandwidth counting + try + { + clientBW = new ClientBW(); + clientImcReads = new CounterWidthExtender(new CounterWidthExtender::ClientImcReadsCounter(clientBW)); + clientImcWrites = new CounterWidthExtender(new CounterWidthExtender::ClientImcWritesCounter(clientBW)); + clientIoRequests = new CounterWidthExtender(new CounterWidthExtender::ClientIoRequestsCounter(clientBW)); + + } catch(...) + { + std::cerr << "Can not read memory controller counter information from PCI configuration space. Access to memory bandwidth counters is not possible." << std::endl; + #ifdef _MSC_VER + // TODO: add message here + #endif + #ifdef __linux__ + std::cerr << "You must be root to access these SandyBridge/IvyBridge/Haswell counters in PCM. " << std::endl; + #endif + } + } + + switch(cpu_model) + { + case IVYTOWN: + case JAKETOWN: + PCU_MSR_PMON_BOX_CTL_ADDR = JKTIVT_PCU_MSR_PMON_BOX_CTL_ADDR; + PCU_MSR_PMON_CTRX_ADDR[0] = JKTIVT_PCU_MSR_PMON_CTR0_ADDR; + PCU_MSR_PMON_CTRX_ADDR[1] = JKTIVT_PCU_MSR_PMON_CTR1_ADDR; + PCU_MSR_PMON_CTRX_ADDR[2] = JKTIVT_PCU_MSR_PMON_CTR2_ADDR; + PCU_MSR_PMON_CTRX_ADDR[3] = JKTIVT_PCU_MSR_PMON_CTR3_ADDR; + break; + case HASWELLX: + PCU_MSR_PMON_BOX_CTL_ADDR = HSX_PCU_MSR_PMON_BOX_CTL_ADDR; + PCU_MSR_PMON_CTRX_ADDR[0] = HSX_PCU_MSR_PMON_CTR0_ADDR; + PCU_MSR_PMON_CTRX_ADDR[1] = HSX_PCU_MSR_PMON_CTR1_ADDR; + PCU_MSR_PMON_CTRX_ADDR[2] = HSX_PCU_MSR_PMON_CTR2_ADDR; + PCU_MSR_PMON_CTRX_ADDR[3] = HSX_PCU_MSR_PMON_CTR3_ADDR; + break; + default: + PCU_MSR_PMON_BOX_CTL_ADDR = 0; + PCU_MSR_PMON_CTRX_ADDR[0] = 0; + PCU_MSR_PMON_CTRX_ADDR[1] = 0; + PCU_MSR_PMON_CTRX_ADDR[2] = 0; + PCU_MSR_PMON_CTRX_ADDR[3] = 0; + } + + +} + +#ifdef __linux__ + +bool isNMIWatchdogEnabled() +{ + FILE * f = fopen("/proc/sys/kernel/nmi_watchdog", "r"); + if (!f) + { + return false; + } + + char buffer[1024]; + if(NULL == fgets(buffer, 1024, f)) + { + std::cerr << "Can not read /proc/sys/kernel/nmi_watchdog ." << std::endl; + fclose(f); + return true; + } + int enabled = 1; + sscanf(buffer, "%d", &enabled); + fclose(f); + + if(enabled == 1) + { + std::cerr << "Error: NMI watchdog is enabled. This consumes one hw-PMU counter" << std::endl; + std::cerr << " to disable NMI watchdog please run under root: echo 0 > /proc/sys/kernel/nmi_watchdog"<< std::endl; + std::cerr << " or to disable it permanently: echo 'kernel.nmi_watchdog=0' >> /etc/sysctl.conf "<< std::endl; + } + + return (enabled == 1); +} + +#endif + +PCM::PCM() : + cpu_family(-1), + cpu_model(-1), + original_cpu_model(-1), + threads_per_core(0), + num_cores(0), + num_sockets(0), + num_phys_cores_per_socket(0), + num_online_cores(0), + core_gen_counter_num_max(0), + core_gen_counter_num_used(0), // 0 means no core gen counters used + core_gen_counter_width(0), + core_fixed_counter_num_max(0), + core_fixed_counter_num_used(0), + core_fixed_counter_width(0), + uncore_gen_counter_num_max(8), + uncore_gen_counter_num_used(0), + uncore_gen_counter_width(48), + uncore_fixed_counter_num_max(1), + uncore_fixed_counter_num_used(0), + uncore_fixed_counter_width(48), + perfmon_version(0), + perfmon_config_anythread(1), + nominal_frequency(0), + max_qpi_speed(0), + pkgThermalSpecPower(-1), + pkgMinimumPower(-1), + pkgMaximumPower(-1), + allow_multiple_instances(false), + programmed_pmu(false), + MSR(NULL), + server_pcicfg_uncore(NULL), + clientBW(NULL), + clientImcReads(NULL), + clientImcWrites(NULL), + clientIoRequests(NULL), + disable_JKT_workaround(false), + blocked(false), + coreCStateMsr(NULL), + pkgCStateMsr(NULL), + mode(INVALID_MODE), + numInstancesSemaphore(NULL), + canUsePerf(false), + outfile(NULL), + backup_ofile(NULL), + run_state(1) +{ + +#ifdef _MSC_VER + TCHAR driverPath[1040]; // length for current directory + "\\msr.sys" + GetCurrentDirectory(1024, driverPath); + wcscat_s(driverPath, 1040, L"\\msr.sys"); + // WARNING: This driver code (msr.sys) is only for testing purposes, not for production use + Driver drv; + // drv.stop(); // restart driver (usually not needed) + if (!drv.start(driverPath)) + { + std::cerr << "Cannot access CPU counters" << std::endl; + std::cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program" << std::endl; + return; + } +#endif + +#ifdef __linux__ + if(isNMIWatchdogEnabled()) return; +#endif + + if(!detectModel()) return; + + if(!checkModel()) return; + + initCStateSupportTables(); + + if(!discoverSystemTopology()) return; + + printSystemTopology(); + + initMSR(); + + if(!detectNominalFrequency()) return; + + initEnergyMonitoring(); + + initUncoreObjects(); + + // Initialize L3 Cache Occupancy Monitoring + initL3CacheOccupancyMonitoring(); + +#ifdef PCM_USE_PERF + canUsePerf = true; + std::vector dummy(PERF_MAX_COUNTERS, -1); + perfEventHandle.resize(num_cores, dummy); +#endif +} + +void PCM::enableJKTWorkaround(bool enable) +{ + if(disable_JKT_workaround) return; + std::cerr << "Using PCM on your system might have a performance impact as per http://software.intel.com/en-us/articles/performance-impact-when-sampling-certain-llc-events-on-snb-ep-with-vtune" << std::endl; + std::cerr << "You can avoid the performance impact by using the option --noJKTWA, however the cache metrics might be wrong then." << std::endl; + if(MSR) + { + for(int32 i = 0; i < num_cores; ++i) + { + uint64 val64 = 0; + MSR[i]->read(0x39C, &val64); + if(enable) + val64 |= 1ULL; + else + val64 &= (~1ULL); + MSR[i]->write(0x39C, val64); + } + } + if(server_pcicfg_uncore) + { + for (int32 i = 0; i < num_sockets; ++i) + { + if(server_pcicfg_uncore[i]) server_pcicfg_uncore[i]->enableJKTWorkaround(enable); + } + } +} + +bool PCM::isCoreOnline(int32 os_core_id) const +{ + return (topology[os_core_id].os_id != -1) && (topology[os_core_id].core_id != -1) && (topology[os_core_id].socket != -1); +} + +bool PCM::isCPUModelSupported(int model_) +{ + return ( model_ == NEHALEM_EP + || model_ == NEHALEM_EX + || model_ == WESTMERE_EP + || model_ == WESTMERE_EX + || model_ == ATOM + || model_ == CLARKDALE + || model_ == SANDY_BRIDGE + || model_ == JAKETOWN + || model_ == IVY_BRIDGE + || model_ == HASWELL + || model_ == IVYTOWN + || model_ == HASWELLX + || model_ == BROADWELL + ); +} + +bool PCM::checkModel() +{ + if (cpu_model == NEHALEM) cpu_model = NEHALEM_EP; + if (cpu_model == ATOM_2 || cpu_model == ATOM_CENTERTON || cpu_model == ATOM_BAYTRAIL || cpu_model == ATOM_AVOTON) cpu_model = ATOM; + if (cpu_model == HASWELL_ULT || cpu_model == HASWELL_2) cpu_model = HASWELL; + + if(!isCPUModelSupported(cpu_model)) + { + std::cerr << getUnsupportedMessage() << " CPU model number: " << cpu_model << " Brand: \"" << getCPUBrandString().c_str() <<"\""<< std::endl; +/* FOR TESTING PURPOSES ONLY */ +#ifdef PCM_TEST_FALLBACK_TO_ATOM + std::cerr << "Fall back to ATOM functionality." << std::endl; + cpu_model = ATOM; + return true; +#endif + return false; + } + return true; +} + +void PCM::destroyMSR() +{ + if (MSR) + { + for (int i = 0; i < num_cores; ++i) + if (MSR[i]) delete MSR[i]; + delete[] MSR; + MSR = NULL; + } +} + +PCM::~PCM() +{ + InstanceLock lock(allow_multiple_instances); + if (instance) + { + destroyMSR(); + + if (server_pcicfg_uncore) + { + for (int i = 0; i < num_sockets; ++i) + if (server_pcicfg_uncore[i]) delete server_pcicfg_uncore[i]; + delete[] server_pcicfg_uncore; + } + for (uint32 i = 0; i < snb_energy_status.size(); ++i) + { + delete snb_energy_status[i]; + } + for (uint32 i = 0; i < jkt_dram_energy_status.size(); ++i) + delete jkt_dram_energy_status[i]; + + instance = NULL; + + if(clientImcReads) delete clientImcReads; + clientImcReads = NULL; + if(clientImcWrites) delete clientImcWrites; + clientImcWrites = NULL; + if(clientIoRequests) delete clientIoRequests; + clientIoRequests = NULL; + if(clientBW) delete clientBW; + clientBW = NULL; + } +} + +bool PCM::good() +{ + return MSR != NULL; +} + +class TemporalThreadAffinity // speedup trick for Linux +{ +#ifdef __linux__ + cpu_set_t old_affinity; + TemporalThreadAffinity(); // forbiden + +public: + TemporalThreadAffinity(uint32 core_id) + { + pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &old_affinity); + + cpu_set_t new_affinity; + CPU_ZERO(&new_affinity); + CPU_SET(core_id, &new_affinity); + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &new_affinity); + } + ~TemporalThreadAffinity() + { + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &old_affinity); + } +#else // not implemented for windows or os x + TemporalThreadAffinity(); // forbiden + +public: + TemporalThreadAffinity(uint32) { } +#endif +}; + +#ifdef PCM_USE_PERF +perf_event_attr PCM_init_perf_event_attr() +{ + perf_event_attr e; + bzero(&e,sizeof(perf_event_attr)); + e.type = -1; // must be set up later + e.size = sizeof(e); + e.config = -1; // must be set up later + e.sample_period = 0; + e.sample_type = 0; + e.read_format = PERF_FORMAT_GROUP; /* PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | + PERF_FORMAT_ID | PERF_FORMAT_GROUP ; */ + e.disabled = 0; + e.inherit = 0; + e.pinned = 1; + e.exclusive = 0; + e.exclude_user = 0; + e.exclude_kernel = 0; + e.exclude_hv = 0; + e.exclude_idle = 0; + e.mmap = 0; + e.comm = 0; + e.freq = 0; + e.inherit_stat = 0; + e.enable_on_exec = 0; + e.task = 0; + e.watermark = 0; + e.wakeup_events = 0; + return e; +} +#endif + +PCM::ErrorCode PCM::program(const PCM::ProgramMode mode_, const void * parameter_) +{ + if(allow_multiple_instances && (EXT_CUSTOM_CORE_EVENTS == mode_ || CUSTOM_CORE_EVENTS == mode_)) + { + allow_multiple_instances = false; + std::cerr << "Warning: multiple PCM instance mode is not allowed with custom events." << std::endl; + } + + InstanceLock lock(allow_multiple_instances); + if (!MSR) return PCM::MSRAccessDenied; + + ExtendedCustomCoreEventDescription * pExtDesc = (ExtendedCustomCoreEventDescription *)parameter_; + +#ifdef PCM_USE_PERF + std::cerr << "Trying to use Linux perf events..." << std::endl; + if(PERF_COUNT_HW_MAX <= PCM_PERF_COUNT_HW_REF_CPU_CYCLES) + { + canUsePerf = false; + std::cerr << "Can not use Linux perf because your Linux kernel does not support PERF_COUNT_HW_REF_CPU_CYCLES event. Falling-back to direct PMU programming." << std::endl; + } + if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->fixedCfg) + { + canUsePerf = false; + std::cerr << "Can not use Linux perf because non-standard fixed counter configuration requested. Falling-back to direct PMU programming." << std::endl; + } + if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && (pExtDesc->OffcoreResponseMsrValue[0] || pExtDesc->OffcoreResponseMsrValue[1])) + { + canUsePerf = false; + std::cerr << "Can not use Linux perf because OffcoreResponse counter usage requested. Falling-back to direct PMU programming." << std::endl; + } +#endif + + if(allow_multiple_instances) + { + //std::cerr << "Checking for other instances of PCM..." << std::endl; + #ifdef _MSC_VER + + numInstancesSemaphore = CreateSemaphore(NULL, 0, 1 << 20, L"Global\\Number of running Intel Processor Counter Monitor instances"); + if (!numInstancesSemaphore) + { + _com_error error(GetLastError()); + std::wcerr << "Error in Windows function 'CreateSemaphore': " << GetLastError() << " " << error.ErrorMessage() << std::endl; + return PCM::UnknownError; + } + LONG prevValue = 0; + if (!ReleaseSemaphore(numInstancesSemaphore, 1, &prevValue)) + { + _com_error error(GetLastError()); + std::wcerr << "Error in Windows function 'ReleaseSemaphore': " << GetLastError() << " " << error.ErrorMessage() << std::endl; + return PCM::UnknownError; + } + if (prevValue > 0) // already programmed since another instance exists + { + std::cerr << "Number of PCM instances: " << (prevValue + 1) << std::endl; + if (hasPCICFGUncore() && server_pcicfg_uncore && server_pcicfg_uncore[0] && max_qpi_speed==0) + for (int i = 0; i < num_sockets; ++i) + max_qpi_speed = (std::max)(server_pcicfg_uncore[i]->computeQPISpeed(socketRefCore[i],cpu_model), max_qpi_speed); // parenthesis to avoid macro expansion on Windows + + reportQPISpeed(); + return PCM::Success; + } + + #else // if linux or apple + numInstancesSemaphore = sem_open(PCM_NUM_INSTANCES_SEMAPHORE_NAME, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 0); + if (SEM_FAILED == numInstancesSemaphore) + { + if (EACCES == errno) + std::cerr << "PCM Error, do not have permissions to open semaphores in /dev/shm/. Clean up them." << std::endl; + return PCM::UnknownError; + } + #ifndef __APPLE__ + sem_post(numInstancesSemaphore); + int curValue = 0; + sem_getvalue(numInstancesSemaphore, &curValue); + #else //if it is apple + uint32 curValue = PCM::incrementNumInstances(); + sem_post(numInstancesSemaphore); + #endif // end ifndef __APPLE__ + + if (curValue > 1) // already programmed since another instance exists + { + std::cerr << "Number of PCM instances: " << curValue << std::endl; + if (hasPCICFGUncore() && server_pcicfg_uncore && server_pcicfg_uncore[0] && max_qpi_speed==0) + for (int i = 0; i < num_sockets; ++i) { + max_qpi_speed = std::max(server_pcicfg_uncore[i]->computeQPISpeed(socketRefCore[i],cpu_model), max_qpi_speed); + reportQPISpeed(); + } + if(!canUsePerf) return PCM::Success; + } + + #endif // end ifdef _MSC_VER + + #ifdef PCM_USE_PERF + /* + numInst>1 && canUsePerf==false -> not reachable, already PMU programmed in another PCM instance + numInst>1 && canUsePerf==true -> perf programmed in different PCM, is not allowed + numInst<=1 && canUsePerf==false -> we are first, perf cannot be used, *check* if PMU busy + numInst<=1 && canUsePerf==true -> we are first, perf will be used, *dont check*, this is now perf business + */ + if(curValue > 1 && (canUsePerf == true)) + { + std::cerr << "Running several clients using the same counters is not posible with Linux perf. Recompile PCM without Linux Perf support to allow such usage. " << std::endl; + decrementInstanceSemaphore(); + return PCM::UnknownError; + } + + if((curValue <= 1) && (canUsePerf == false) && PMUinUse()) + { + decrementInstanceSemaphore(); + return PCM::PMUBusy; + } + #else + if (PMUinUse()) + { + decrementInstanceSemaphore(); + return PCM::PMUBusy; + } + #endif + } + else + { + if((canUsePerf == false) && PMUinUse()) + { + return PCM::PMUBusy; + } + } + + mode = mode_; + + // copy custom event descriptions + if (mode == CUSTOM_CORE_EVENTS) + { + if (!parameter_) + { + std::cerr << "PCM Internal Error: data structure for custom event not initialized" << std::endl; + return PCM::UnknownError; + } + CustomCoreEventDescription * pDesc = (CustomCoreEventDescription *)parameter_; + coreEventDesc[0] = pDesc[0]; + coreEventDesc[1] = pDesc[1]; + if (cpu_model != ATOM) + { + coreEventDesc[2] = pDesc[2]; + coreEventDesc[3] = pDesc[3]; + core_gen_counter_num_used = 4; + } + else + core_gen_counter_num_used = 2; + } + else + { + if (cpu_model == ATOM) + { + coreEventDesc[0].event_number = ARCH_LLC_MISS_EVTNR; + coreEventDesc[0].umask_value = ARCH_LLC_MISS_UMASK; + coreEventDesc[1].event_number = ARCH_LLC_REFERENCE_EVTNR; + coreEventDesc[1].umask_value = ARCH_LLC_REFERENCE_UMASK; + core_gen_counter_num_used = 2; + } + else if ( + SANDY_BRIDGE == cpu_model + || JAKETOWN == cpu_model + || IVYTOWN == cpu_model + || IVY_BRIDGE == cpu_model + || HASWELL == cpu_model + || HASWELLX == cpu_model + || BROADWELL == cpu_model + ) + { + coreEventDesc[0].event_number = ARCH_LLC_MISS_EVTNR; + coreEventDesc[0].umask_value = ARCH_LLC_MISS_UMASK; + coreEventDesc[1].event_number = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_EVTNR; + coreEventDesc[1].umask_value = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_UMASK; + coreEventDesc[2].event_number = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_EVTNR; + coreEventDesc[2].umask_value = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_UMASK; + coreEventDesc[3].event_number = MEM_LOAD_UOPS_RETIRED_L2_HIT_EVTNR; + coreEventDesc[3].umask_value = MEM_LOAD_UOPS_RETIRED_L2_HIT_UMASK; + core_gen_counter_num_used = 4; + } + else + { // Nehalem or Westmere + if( + NEHALEM_EP == cpu_model + || WESTMERE_EP == cpu_model + || CLARKDALE == cpu_model + ) + { + coreEventDesc[0].event_number = MEM_LOAD_RETIRED_L3_MISS_EVTNR; + coreEventDesc[0].umask_value = MEM_LOAD_RETIRED_L3_MISS_UMASK; + } + else + { + coreEventDesc[0].event_number = ARCH_LLC_MISS_EVTNR; + coreEventDesc[0].umask_value = ARCH_LLC_MISS_UMASK; + } + coreEventDesc[1].event_number = MEM_LOAD_RETIRED_L3_UNSHAREDHIT_EVTNR; + coreEventDesc[1].umask_value = MEM_LOAD_RETIRED_L3_UNSHAREDHIT_UMASK; + coreEventDesc[2].event_number = MEM_LOAD_RETIRED_L2_HITM_EVTNR; + coreEventDesc[2].umask_value = MEM_LOAD_RETIRED_L2_HITM_UMASK; + coreEventDesc[3].event_number = MEM_LOAD_RETIRED_L2_HIT_EVTNR; + coreEventDesc[3].umask_value = MEM_LOAD_RETIRED_L2_HIT_UMASK; + core_gen_counter_num_used = 4; + } + } + + core_fixed_counter_num_used = 3; + + if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->gpCounterCfg) + { + core_gen_counter_num_used = (std::min)(core_gen_counter_num_used,pExtDesc->nGPCounters); + } + + if(cpu_model == JAKETOWN) + { + bool enableWA = false; + for(uint32 i = 0; i< core_gen_counter_num_used; ++i) + { + if(coreEventDesc[i].event_number == MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_EVTNR) + enableWA = true; + } + enableJKTWorkaround(enableWA); // this has a performance penalty on memory access + } + + programmed_pmu = true; + + // Version for linux/windows + for (int i = 0; i < num_cores; ++i) + { + // program core counters + + TemporalThreadAffinity tempThreadAffinity(i); // speedup trick for Linux + + FixedEventControlRegister ctrl_reg; +#ifdef PCM_USE_PERF + int leader_counter = -1; + perf_event_attr e = PCM_init_perf_event_attr(); + if(canUsePerf) + { + e.type = PERF_TYPE_HARDWARE; + e.config = (((long long unsigned)PERF_TYPE_HARDWARE)<<(64ULL-8ULL)) + PERF_COUNT_HW_INSTRUCTIONS; + if((perfEventHandle[i][PERF_INST_RETIRED_ANY_POS] = syscall(SYS_perf_event_open, &e, -1, + i /* core id */, leader_counter /* group leader */ ,0 )) <= 0) + { + std::cerr <<"Linux Perf: Error on programming INST_RETIRED_ANY: "<write(IA32_CR_PERF_GLOBAL_CTRL, 0); + MSR[i]->read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); + + + if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->fixedCfg) + { + ctrl_reg = *(pExtDesc->fixedCfg); + } + else + { + ctrl_reg.fields.os0 = 1; + ctrl_reg.fields.usr0 = 1; + ctrl_reg.fields.any_thread0 = 0; + ctrl_reg.fields.enable_pmi0 = 0; + + ctrl_reg.fields.os1 = 1; + ctrl_reg.fields.usr1 = 1; + ctrl_reg.fields.any_thread1 = 0; + ctrl_reg.fields.enable_pmi1 = 0; + + ctrl_reg.fields.os2 = 1; + ctrl_reg.fields.usr2 = 1; + ctrl_reg.fields.any_thread2 = 0; + ctrl_reg.fields.enable_pmi2 = 0; + + ctrl_reg.fields.reserved1 = 0; + } + + MSR[i]->write(IA32_CR_FIXED_CTR_CTRL, ctrl_reg.value); + } + + if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc) + { + if(pExtDesc->OffcoreResponseMsrValue[0]) + MSR[i]->write(MSR_OFFCORE_RSP0, pExtDesc->OffcoreResponseMsrValue[0]); + if(pExtDesc->OffcoreResponseMsrValue[1]) + MSR[i]->write(MSR_OFFCORE_RSP1, pExtDesc->OffcoreResponseMsrValue[1]); + } + + EventSelectRegister event_select_reg; + + for (uint32 j = 0; j < core_gen_counter_num_used; ++j) + { + if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->gpCounterCfg) + { + event_select_reg = pExtDesc->gpCounterCfg[j]; + } + else + { + MSR[i]->read(IA32_PERFEVTSEL0_ADDR + j, &event_select_reg.value); // read-only also safe for perf + + event_select_reg.fields.event_select = coreEventDesc[j].event_number; + event_select_reg.fields.umask = coreEventDesc[j].umask_value; + event_select_reg.fields.usr = 1; + event_select_reg.fields.os = 1; + event_select_reg.fields.edge = 0; + event_select_reg.fields.pin_control = 0; + event_select_reg.fields.apic_int = 0; + event_select_reg.fields.any_thread = 0; + event_select_reg.fields.enable = 1; + event_select_reg.fields.invert = 0; + event_select_reg.fields.cmask = 0; + event_select_reg.fields.in_tx = 0; + event_select_reg.fields.in_txcp = 0; + } +#ifdef PCM_USE_PERF + if(canUsePerf) + { + e.type = PERF_TYPE_RAW; + e.config = (1ULL<<63ULL) + (((long long unsigned)PERF_TYPE_RAW)<<(64ULL-8ULL)) + event_select_reg.value; + if((perfEventHandle[i][PERF_GEN_EVENT_0_POS + j] = syscall(SYS_perf_event_open, &e, -1, + i /* core id */, leader_counter /* group leader */ ,0 )) <= 0) + { + std::cerr <<"Linux Perf: Error on programming generic event #"<< i <<" error: "<write(IA32_PMC0 + j, 0); + MSR[i]->write(IA32_PERFEVTSEL0_ADDR + j, event_select_reg.value); + } + } + + if(!canUsePerf) + { + // start counting, enable all (4 programmable + 3 fixed) counters + uint64 value = (1ULL << 0) + (1ULL << 1) + (1ULL << 2) + (1ULL << 3) + (1ULL << 32) + (1ULL << 33) + (1ULL << 34); + + if (cpu_model == ATOM) // Atom has only 2 programmable counters + value = (1ULL << 0) + (1ULL << 1) + (1ULL << 32) + (1ULL << 33) + (1ULL << 34); + + MSR[i]->write(IA32_CR_PERF_GLOBAL_CTRL, value); + } + + // program uncore counters + + if (cpu_model == NEHALEM_EP || cpu_model == WESTMERE_EP || cpu_model == CLARKDALE) + { + programNehalemEPUncore(i); + } + else if (hasBecktonUncore()) + { + programBecktonUncore(i); + } + } + + if(canUsePerf) + { + std::cerr << "Successfully programmed on-core PMU using Linux perf"<program(); + max_qpi_speed = (std::max)(server_pcicfg_uncore[i]->computeQPISpeed(socketRefCore[i],cpu_model), max_qpi_speed); // parenthesis to avoid macro expansion on Windows + } + } + + reportQPISpeed(); + + return PCM::Success; +} + +void PCM::reportQPISpeed() const +{ + if (!max_qpi_speed) return; + + if (hasPCICFGUncore() && server_pcicfg_uncore && server_pcicfg_uncore[0]) { + for (int i = 0; i < num_sockets; ++i) + { + std::cerr << "Socket " << i << std::endl; + server_pcicfg_uncore[i]->reportQPISpeed(); + } + } else { + std::cerr << "Max QPI speed: " << max_qpi_speed / (1e9) << " GBytes/second (" << max_qpi_speed / (2e9) << " GT/second)" << std::endl; + } + +} + +void PCM::programNehalemEPUncore(int32 core) +{ + +#define CPUCNT_INIT_THE_REST_OF_EVTCNT \ + unc_event_select_reg.fields.occ_ctr_rst = 1; \ + unc_event_select_reg.fields.edge = 0; \ + unc_event_select_reg.fields.enable_pmi = 0; \ + unc_event_select_reg.fields.enable = 1; \ + unc_event_select_reg.fields.invert = 0; \ + unc_event_select_reg.fields.cmask = 0; + + uncore_gen_counter_num_used = 8; + + UncoreEventSelectRegister unc_event_select_reg; + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL0_ADDR, &unc_event_select_reg.value); + + unc_event_select_reg.fields.event_select = UNC_QMC_WRITES_FULL_ANY_EVTNR; + unc_event_select_reg.fields.umask = UNC_QMC_WRITES_FULL_ANY_UMASK; + + CPUCNT_INIT_THE_REST_OF_EVTCNT + + MSR[core]->write(MSR_UNCORE_PERFEVTSEL0_ADDR, unc_event_select_reg.value); + + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL1_ADDR, &unc_event_select_reg.value); + + unc_event_select_reg.fields.event_select = UNC_QMC_NORMAL_READS_ANY_EVTNR; + unc_event_select_reg.fields.umask = UNC_QMC_NORMAL_READS_ANY_UMASK; + + CPUCNT_INIT_THE_REST_OF_EVTCNT + + MSR[core]->write(MSR_UNCORE_PERFEVTSEL1_ADDR, unc_event_select_reg.value); + + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL2_ADDR, &unc_event_select_reg.value); + unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; + unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_IOH_READS_UMASK; + CPUCNT_INIT_THE_REST_OF_EVTCNT + MSR[core]->write(MSR_UNCORE_PERFEVTSEL2_ADDR, unc_event_select_reg.value); + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL3_ADDR, &unc_event_select_reg.value); + unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; + unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_IOH_WRITES_UMASK; + CPUCNT_INIT_THE_REST_OF_EVTCNT + MSR[core]->write(MSR_UNCORE_PERFEVTSEL3_ADDR, unc_event_select_reg.value); + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL4_ADDR, &unc_event_select_reg.value); + unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; + unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_REMOTE_READS_UMASK; + CPUCNT_INIT_THE_REST_OF_EVTCNT + MSR[core]->write(MSR_UNCORE_PERFEVTSEL4_ADDR, unc_event_select_reg.value); + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL5_ADDR, &unc_event_select_reg.value); + unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; + unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_REMOTE_WRITES_UMASK; + CPUCNT_INIT_THE_REST_OF_EVTCNT + MSR[core]->write(MSR_UNCORE_PERFEVTSEL5_ADDR, unc_event_select_reg.value); + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL6_ADDR, &unc_event_select_reg.value); + unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; + unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_LOCAL_READS_UMASK; + CPUCNT_INIT_THE_REST_OF_EVTCNT + MSR[core]->write(MSR_UNCORE_PERFEVTSEL6_ADDR, unc_event_select_reg.value); + + MSR[core]->read(MSR_UNCORE_PERFEVTSEL7_ADDR, &unc_event_select_reg.value); + unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; + unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_LOCAL_WRITES_UMASK; + CPUCNT_INIT_THE_REST_OF_EVTCNT + MSR[core]->write(MSR_UNCORE_PERFEVTSEL7_ADDR, unc_event_select_reg.value); + + +#undef CPUCNT_INIT_THE_REST_OF_EVTCNT + + // start uncore counting + uint64 value = 255 + (1ULL << 32); // enable all counters + MSR[core]->write(MSR_UNCORE_PERF_GLOBAL_CTRL_ADDR, value); + + // synchronise counters + MSR[core]->write(MSR_UNCORE_PMC0, 0); + MSR[core]->write(MSR_UNCORE_PMC1, 0); + MSR[core]->write(MSR_UNCORE_PMC2, 0); + MSR[core]->write(MSR_UNCORE_PMC3, 0); + MSR[core]->write(MSR_UNCORE_PMC4, 0); + MSR[core]->write(MSR_UNCORE_PMC5, 0); + MSR[core]->write(MSR_UNCORE_PMC6, 0); + MSR[core]->write(MSR_UNCORE_PMC7, 0); +} + +void PCM::programBecktonUncore(int32 core) +{ + // program Beckton uncore + if (core == socketRefCore[0]) computeQPISpeedBeckton(core); + + uint64 value = 1 << 29ULL; // reset all counters + MSR[core]->write(U_MSR_PMON_GLOBAL_CTL, value); + + BecktonUncorePMUZDPCTLFVCRegister FVCreg; + FVCreg.value = 0; + if (cpu_model == NEHALEM_EX) + { + FVCreg.fields.bcmd = 0; // rd_bcmd + FVCreg.fields.resp = 0; // ack_resp + FVCreg.fields.evnt0 = 5; // bcmd_match + FVCreg.fields.evnt1 = 6; // resp_match + FVCreg.fields.pbox_init_err = 0; + } + else + { + FVCreg.fields_wsm.bcmd = 0; // rd_bcmd + FVCreg.fields_wsm.resp = 0; // ack_resp + FVCreg.fields_wsm.evnt0 = 5; // bcmd_match + FVCreg.fields_wsm.evnt1 = 6; // resp_match + FVCreg.fields_wsm.pbox_init_err = 0; + } + MSR[core]->write(MB0_MSR_PMU_ZDP_CTL_FVC, FVCreg.value); + MSR[core]->write(MB1_MSR_PMU_ZDP_CTL_FVC, FVCreg.value); + + BecktonUncorePMUCNTCTLRegister CNTCTLreg; + CNTCTLreg.value = 0; + CNTCTLreg.fields.en = 1; + CNTCTLreg.fields.pmi_en = 0; + CNTCTLreg.fields.count_mode = 0; + CNTCTLreg.fields.storage_mode = 0; + CNTCTLreg.fields.wrap_mode = 1; + CNTCTLreg.fields.flag_mode = 0; + CNTCTLreg.fields.inc_sel = 0x0d; // FVC_EV0 + MSR[core]->write(MB0_MSR_PMU_CNT_CTL_0, CNTCTLreg.value); + MSR[core]->write(MB1_MSR_PMU_CNT_CTL_0, CNTCTLreg.value); + CNTCTLreg.fields.inc_sel = 0x0e; // FVC_EV1 + MSR[core]->write(MB0_MSR_PMU_CNT_CTL_1, CNTCTLreg.value); + MSR[core]->write(MB1_MSR_PMU_CNT_CTL_1, CNTCTLreg.value); + + value = 1 + ((0x0C) << 1ULL); // enable bit + (event select IMT_INSERTS_WR) + MSR[core]->write(BB0_MSR_PERF_CNT_CTL_1, value); + MSR[core]->write(BB1_MSR_PERF_CNT_CTL_1, value); + + MSR[core]->write(MB0_MSR_PERF_GLOBAL_CTL, 3); // enable two counters + MSR[core]->write(MB1_MSR_PERF_GLOBAL_CTL, 3); // enable two counters + + MSR[core]->write(BB0_MSR_PERF_GLOBAL_CTL, 2); // enable second counter + MSR[core]->write(BB1_MSR_PERF_GLOBAL_CTL, 2); // enable second counter + + // program R-Box to monitor QPI traffic + + // enable counting on all counters on the left side (port 0-3) + MSR[core]->write(R_MSR_PMON_GLOBAL_CTL_7_0, 255); + // ... on the right side (port 4-7) + MSR[core]->write(R_MSR_PMON_GLOBAL_CTL_15_8, 255); + + // pick the event + value = (1 << 7ULL) + (1 << 6ULL) + (1 << 2ULL); // count any (incoming) data responses + MSR[core]->write(R_MSR_PORT0_IPERF_CFG0, value); + MSR[core]->write(R_MSR_PORT1_IPERF_CFG0, value); + MSR[core]->write(R_MSR_PORT4_IPERF_CFG0, value); + MSR[core]->write(R_MSR_PORT5_IPERF_CFG0, value); + + // pick the event + value = (1ULL << 30ULL); // count null idle flits sent + MSR[core]->write(R_MSR_PORT0_IPERF_CFG1, value); + MSR[core]->write(R_MSR_PORT1_IPERF_CFG1, value); + MSR[core]->write(R_MSR_PORT4_IPERF_CFG1, value); + MSR[core]->write(R_MSR_PORT5_IPERF_CFG1, value); + + // choose counter 0 to monitor R_MSR_PORT0_IPERF_CFG0 + MSR[core]->write(R_MSR_PMON_CTL0, 1 + 2 * (0)); + // choose counter 1 to monitor R_MSR_PORT1_IPERF_CFG0 + MSR[core]->write(R_MSR_PMON_CTL1, 1 + 2 * (6)); + // choose counter 8 to monitor R_MSR_PORT4_IPERF_CFG0 + MSR[core]->write(R_MSR_PMON_CTL8, 1 + 2 * (0)); + // choose counter 9 to monitor R_MSR_PORT5_IPERF_CFG0 + MSR[core]->write(R_MSR_PMON_CTL9, 1 + 2 * (6)); + + // choose counter 2 to monitor R_MSR_PORT0_IPERF_CFG1 + MSR[core]->write(R_MSR_PMON_CTL2, 1 + 2 * (1)); + // choose counter 3 to monitor R_MSR_PORT1_IPERF_CFG1 + MSR[core]->write(R_MSR_PMON_CTL3, 1 + 2 * (7)); + // choose counter 10 to monitor R_MSR_PORT4_IPERF_CFG1 + MSR[core]->write(R_MSR_PMON_CTL10, 1 + 2 * (1)); + // choose counter 11 to monitor R_MSR_PORT5_IPERF_CFG1 + MSR[core]->write(R_MSR_PMON_CTL11, 1 + 2 * (7)); + + // enable uncore TSC counter (fixed one) + MSR[core]->write(W_MSR_PMON_GLOBAL_CTL, 1ULL << 31ULL); + MSR[core]->write(W_MSR_PMON_FIXED_CTR_CTL, 1ULL); + + value = (1 << 28ULL) + 1; // enable all counters + MSR[core]->write(U_MSR_PMON_GLOBAL_CTL, value); +} + +uint64 RDTSC(); + +void PCM::computeNominalFrequency() +{ + const int ref_core = 0; + uint64 before = 0, after = 0; + MSR[ref_core]->read(IA32_TIME_STAMP_COUNTER, &before); +// sleep fo 100 ms +#ifdef _MSC_VER + Sleep(1000); +#else + usleep(1000*1000); +#endif + MSR[ref_core]->read(IA32_TIME_STAMP_COUNTER, &after); + nominal_frequency = after-before; +} +std::string PCM::getCPUBrandString() +{ + char buffer[sizeof(int)*4*3+1]; + PCM_CPUID_INFO * info = (PCM_CPUID_INFO *) buffer; + pcm_cpuid(0x80000002, *info); + ++info; + pcm_cpuid(0x80000003, *info); + ++info; + pcm_cpuid(0x80000004, *info); + buffer[sizeof(int)*4*3] = 0; + std::string result(buffer); + while(result[0]==' ') result.erase(0,1); + size_t i = std::string::npos; + while((i=result.find(" "))!=std::string::npos) result.replace(i,2," "); // remove duplicate spaces + return result; +} + +uint64 get_frequency_from_cpuid() // from Pat Fay (Intel) +{ + double speed=0; + std::string brand = PCM::getCPUBrandString(); + if(brand.length() > 0) + { + size_t unitsg = brand.find("GHz"); + if(unitsg != std::string::npos) + { + size_t atsign = brand.rfind(' ', unitsg); + if(atsign != std::string::npos) + { + std::istringstream(brand.substr(atsign)) >> speed; + speed *= 1000; + } + } + else + { + size_t unitsg = brand.find("MHz"); + if(unitsg != std::string::npos) + { + size_t atsign = brand.rfind(' ', unitsg); + if(atsign != std::string::npos) + { + std::istringstream(brand.substr(atsign)) >> speed; + } + } + } + } + return (uint64)speed * 1000ULL * 1000ULL; +} + +std::string PCM::getSupportedUarchCodenames() const +{ + std::ostringstream ostr; + for(int32 i=0; i < PCM::END_OF_MODEL_LIST ; ++i) + if(isCPUModelSupported(i)) + ostr << getUArchCodename(i) << ", "; + return ostr.str().substr(0, ostr.str().length() - 2); +} + +std::string PCM::getUnsupportedMessage() const +{ + std::ostringstream ostr; + ostr << "Error: unsupported processor. Only Intel(R) processors are supported (Atom(R) and microarchitecture codename "<< getSupportedUarchCodenames() <<")."; + return ostr.str(); +} + +void PCM::computeQPISpeedBeckton(int core_nr) +{ + uint64 startFlits = 0; + // reset all counters + MSR[core_nr]->write(U_MSR_PMON_GLOBAL_CTL, 1 << 29ULL); + + // enable counting on all counters on the left side (port 0-3) + MSR[core_nr]->write(R_MSR_PMON_GLOBAL_CTL_7_0, 255); + // disable on the right side (port 4-7) + MSR[core_nr]->write(R_MSR_PMON_GLOBAL_CTL_15_8, 0); + + // count flits sent + MSR[core_nr]->write(R_MSR_PORT0_IPERF_CFG0, 1ULL << 31ULL); + + // choose counter 0 to monitor R_MSR_PORT0_IPERF_CFG0 + MSR[core_nr]->write(R_MSR_PMON_CTL0, 1 + 2 * (0)); + + // enable all counters + MSR[core_nr]->write(U_MSR_PMON_GLOBAL_CTL, (1 << 28ULL) + 1); + + MSR[core_nr]->read(R_MSR_PMON_CTR0, &startFlits); + + const uint64 timerGranularity = 1000000ULL; // mks + uint64 startTSC = getTickCount(timerGranularity, core_nr); + uint64 endTSC; + do + { + endTSC = getTickCount(timerGranularity, core_nr); + } while (endTSC - startTSC < 200000ULL); // spin for 200 ms + + uint64 endFlits = 0; + MSR[core_nr]->read(R_MSR_PMON_CTR0, &endFlits); + max_qpi_speed = (endFlits - startFlits) * 8ULL * timerGranularity / (endTSC - startTSC); + +} + +bool PCM::PMUinUse() +{ + // follow the "Performance Monitoring Unit Sharing Guide" by P. Irelan and Sh. Kuo + for (int i = 0; i < num_cores; ++i) + { + //std::cout << "Core "<read(IA32_CR_PERF_GLOBAL_CTRL, &value); + // std::cout << "Core "<read(IA32_PERFEVTSEL0_ADDR + j, &event_select_reg.value); + + if (event_select_reg.fields.event_select != 0 || event_select_reg.fields.apic_int != 0) + { + std::cerr << "WARNING: Core "<read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); + + // Check if someone has installed pmi handler on counter overflow. + // If so, that agent might potentially need to change counter value + // for the "sample after"-mode messing up PCM measurements + if(ctrl_reg.fields.enable_pmi0 || ctrl_reg.fields.enable_pmi1 || ctrl_reg.fields.enable_pmi2) + { + std::cerr << "WARNING: Core "<cpu_model ; + + switch(cpu_model_) + { + case NEHALEM_EP: + case NEHALEM: + return "Nehalem/Nehalem-EP"; + case ATOM: + return "Atom(tm)"; + case CLARKDALE: + return "Westmere/Clarkdale"; + case WESTMERE_EP: + return "Westmere-EP"; + case NEHALEM_EX: + return "Nehalem-EX"; + case WESTMERE_EX: + return "Westmere-EX"; + case SANDY_BRIDGE: + return "Sandy Bridge"; + case JAKETOWN: + return "Sandy Bridge-EP/Jaketown"; + case IVYTOWN: + return "Ivy Bridge-EP/EN/EX/Ivytown"; + case HASWELLX: + return "Haswell-EP/EN/EX"; + case IVY_BRIDGE: + return "Ivy Bridge"; + case HASWELL: + return "Haswell"; + case BROADWELL: + return "Broadwell"; + } + return "unknown"; +} + +void PCM::cleanupPMU() +{ +#ifdef PCM_USE_PERF + if(canUsePerf) + { + for (int i = 0; i < num_cores; ++i) + for(int c = 0; c < PERF_MAX_COUNTERS; ++c) + ::close(perfEventHandle[i][c]); + + return; + } +#endif + + // follow the "Performance Monitoring Unit Sharing Guide" by P. Irelan and Sh. Kuo + for (int i = 0; i < num_cores; ++i) + { + // disable generic counters and continue free running counting for fixed counters + MSR[i]->write(IA32_CR_PERF_GLOBAL_CTRL, (1ULL << 32) + (1ULL << 33) + (1ULL << 34)); + + for (uint32 j = 0; j < core_gen_counter_num_max; ++j) + { + MSR[i]->write(IA32_PERFEVTSEL0_ADDR + j, 0); + } + } + + if(cpu_model == JAKETOWN) + enableJKTWorkaround(false); + + std::cerr << " Zeroed PMU registers" << std::endl; +} + +void PCM::resetPMU() +{ + for (int i = 0; i < num_cores; ++i) + { + // disable all counters + MSR[i]->write(IA32_CR_PERF_GLOBAL_CTRL, 0); + + for (uint32 j = 0; j < core_gen_counter_num_max; ++j) + { + MSR[i]->write(IA32_PERFEVTSEL0_ADDR + j, 0); + } + + + FixedEventControlRegister ctrl_reg; + ctrl_reg.value = 0xffffffffffffffff; + + MSR[i]->read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); + if ((ctrl_reg.fields.os0 || + ctrl_reg.fields.usr0 || + ctrl_reg.fields.enable_pmi0 || + ctrl_reg.fields.os1 || + ctrl_reg.fields.usr1 || + ctrl_reg.fields.enable_pmi1 || + ctrl_reg.fields.os2 || + ctrl_reg.fields.usr2 || + ctrl_reg.fields.enable_pmi2) + != 0) + MSR[i]->write(IA32_CR_FIXED_CTR_CTRL, 0); + } + + std::cerr << " Zeroed PMU registers" << std::endl; +} +void PCM::freeRMID() +{ + for(int32 core = 0; core < num_cores; core ++ ) + { + if(!isCoreOnline(core)) continue; + uint64 msr_pqr_assoc = 0 ; + uint64 msr_qm_evtsel = 0; + int32 rmid = 0; + int32 event = 0; + + //Read 0xC8F MSR for each core + MSR[core]->read(IA32_PQR_ASSOC, &msr_pqr_assoc); + msr_pqr_assoc &= 0xffffffff00000000ULL; + + //Write 0xC8F MSR with RMID 0 + MSR[core]->write(IA32_PQR_ASSOC,msr_pqr_assoc); + + msr_qm_evtsel = rmid & ((1ULL<<10)-1ULL) ; + msr_qm_evtsel <<= 32 ; + msr_qm_evtsel |= event & ((1ULL<<8)-1ULL); + + //Write Event Id as 0 and RMID 0 to the MSR for each core + MSR[core]->write(IA32_QM_EVTSEL,msr_qm_evtsel); + + } + + + std::cerr << " Freeing up all RMIDs" << std::endl; +} +void PCM::setOutput(const std::string filename) +{ + outfile = new std::ofstream(filename.c_str()); + backup_ofile = std::cout.rdbuf(); + std::cout.rdbuf(outfile->rdbuf()); +} + +void PCM::restoreOutput() +{ + // restore cout back to what it was originally + if(backup_ofile) + std::cout.rdbuf(backup_ofile); + +// close output file + if(outfile) + outfile->close(); +} + +void PCM::cleanup() +{ + InstanceLock lock(allow_multiple_instances); + + if (!MSR) return; + + std::cerr << "Cleaning up" << std::endl; + + if (decrementInstanceSemaphore()) + cleanupPMU(); + + freeRMID(); +} + +#ifdef __APPLE__ + +uint32 PCM::getNumInstances() +{ + return MSR[0]->getNumInstances(); +} + + +uint32 PCM::incrementNumInstances() +{ + return MSR[0]->incrementNumInstances(); +} + +uint32 PCM::decrementNumInstances() +{ + return MSR[0]->decrementNumInstances();; +} + +int convertUnknownToInt(size_t size, char* value){ + if(sizeof(int) == size) + { + return *(int*)value; + } + else if(sizeof(long) == size) + { + return *(long *)value; + } + else if(sizeof(long long) == size) + { + return *(long long *)value; + } + else + { + // In this case, we don't know what it is so we guess int + return *(int *)value; + } +} + +#endif + +bool PCM::decrementInstanceSemaphore() +{ + if(allow_multiple_instances == false) + { + return programmed_pmu; + } + bool isLastInstance = false; + // when decrement was called before program() the numInstancesSemaphore + // may not be initialized, causing SIGSEGV. This fixes it. + if(numInstancesSemaphore == NULL) + return true; + + #ifdef _MSC_VER + WaitForSingleObject(numInstancesSemaphore, 0); + + DWORD res = WaitForSingleObject(numInstancesSemaphore, 0); + if (res == WAIT_TIMEOUT) + { + // I have the last instance of monitor + + isLastInstance = true; + + CloseHandle(numInstancesSemaphore); + } + else if (res == WAIT_OBJECT_0) + { + ReleaseSemaphore(numInstancesSemaphore, 1, NULL); + + // std::cerr << "Someone else is running monitor instance, no cleanup needed"<< std::endl; + } + else + { + // unknown error + std::cerr << "ERROR: Bad semaphore. Performed cleanup twice?" << std::endl; + } + + + #elif __APPLE__ + sem_wait(numInstancesSemaphore); + uint32 oldValue = PCM::getNumInstances(); + sem_post(numInstancesSemaphore); + if(oldValue == 0) + { + // see same case for linux + return false; + } + sem_wait(numInstancesSemaphore); + uint32 currValue = PCM::decrementNumInstances(); + sem_post(numInstancesSemaphore); + if(currValue == 0){ + isLastInstance = true; + } + + #else // if linux + int oldValue = -1; + sem_getvalue(numInstancesSemaphore, &oldValue); + if(oldValue == 0) + { + // the current value is already zero, somewhere the semaphore has been already decremented (and thus the clean up has been done if needed) + // that means logically we are do not own the last instance anymore, thus returning false + return false; + } + sem_wait(numInstancesSemaphore); + int curValue = -1; + sem_getvalue(numInstancesSemaphore, &curValue); + if (curValue == 0) + { + // I have the last instance of monitor + + isLastInstance = true; + + // std::cerr << "I am the last one"<< std::endl; + } + #endif // end ifdef _MSC_VER + + return isLastInstance; +} + +uint64 PCM::getTickCount(uint64 multiplier, uint32 core) +{ + return (multiplier * getInvariantTSC(CoreCounterState(), getCoreCounterState(core))) / getNominalFrequency(); +} + +uint64 RDTSC() +{ + uint64 result = 0; +#ifdef _MSC_VER + // Windows + #if _MSC_VER>= 1600 + result = __rdtsc(); + #endif +#else + // Linux + uint32 high = 0, low = 0; + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + result = low + (uint64(high)<<32ULL); +#endif + return result; + +} + +uint64 RDTSCP() +{ + uint64 result = 0; +#ifdef _MSC_VER + // Windows + #if _MSC_VER>= 1600 + unsigned int Aux; + result = __rdtscp(&Aux); + #endif +#else + // Linux and OS X + uint32 high = 0, low = 0; + asm volatile ( + "rdtscp\n\t" + "mov %%edx, %0\n\t" + "mov %%eax, %1\n\t": + "=r" (high), "=r" (low) :: "%rax", "%rcx", "%rdx"); + result = low + (uint64(high)<<32ULL); +#endif + return result; +} + + +uint64 PCM::getTickCountRDTSCP(uint64 multiplier) +{ + return (multiplier*RDTSCP())/getNominalFrequency(); +} + +SystemCounterState getSystemCounterState() +{ + PCM * inst = PCM::getInstance(); + SystemCounterState result; + if (inst) result = inst->getSystemCounterState(); + return result; +} + +SocketCounterState getSocketCounterState(uint32 socket) +{ + PCM * inst = PCM::getInstance(); + SocketCounterState result; + if (inst) result = inst->getSocketCounterState(socket); + return result; +} + +CoreCounterState getCoreCounterState(uint32 core) +{ + PCM * inst = PCM::getInstance(); + CoreCounterState result; + if (inst) result = inst->getCoreCounterState(core); + return result; +} + +#ifdef PCM_USE_PERF +void PCM::readPerfData(uint32 core, std::vector & outData) +{ + uint64 data[1 + PERF_MAX_COUNTERS]; + const int32 bytes2read = sizeof(uint64)*(1 + core_fixed_counter_num_used + core_gen_counter_num_used); + int result = ::read(perfEventHandle[core][PERF_GROUP_LEADER_COUNTER], data, bytes2read ); + // data layout: nr counters; counter 0, counter 1, counter 2,... + if(result != bytes2read) + { + std::cerr << "Error while reading perf data. Result is "<< result << std::endl; + std::cerr << "Check if you run other competing Linux perf clients." << std::endl; + } else if(data[0] != core_fixed_counter_num_used + core_gen_counter_num_used) + { + std::cerr << "Number of counters read from perf is wrong. Elements read: "<< data[0] << std::endl; + } + else + { // copy all counters, they start from position 1 in data + std::copy((data + 1), (data + 1) + data[0], outData.begin()); + } +} +#endif + +void BasicCounterState::readAndAggregate(SafeMsrHandle * msr) +{ + uint64 cInstRetiredAny = 0, cCpuClkUnhaltedThread = 0, cCpuClkUnhaltedRef = 0; + uint64 cL3Miss = 0; + uint64 cL3UnsharedHit = 0; + uint64 cL2HitM = 0; + uint64 cL2Hit = 0; + uint64 cInvariantTSC = 0; + uint64 cL3Occupancy = 0; + uint64 cCStateResidency[PCM::MAX_C_STATE + 1]; + memset(&(cCStateResidency[0]), 0, sizeof(cCStateResidency)); + uint64 thermStatus = 0; + TemporalThreadAffinity tempThreadAffinity(msr->getCoreId()); // speedup trick for Linux + + PCM * m = PCM::getInstance(); + uint32 cpu_model = m->getCPUModel(); + + // reading core PMU counters +#ifdef PCM_USE_PERF + if(m->canUsePerf) + { + std::vector perfData(PERF_MAX_COUNTERS, 0ULL); + m->readPerfData(msr->getCoreId(), perfData); + cInstRetiredAny = perfData[PCM::PERF_INST_RETIRED_ANY_POS]; + cCpuClkUnhaltedThread = perfData[PCM::PERF_CPU_CLK_UNHALTED_THREAD_POS]; + cCpuClkUnhaltedRef = perfData[PCM::PERF_CPU_CLK_UNHALTED_REF_POS]; + cL3Miss = perfData[PCM::PERF_GEN_EVENT_0_POS]; + cL3UnsharedHit = perfData[PCM::PERF_GEN_EVENT_1_POS]; + cL2HitM = perfData[PCM::PERF_GEN_EVENT_2_POS]; + cL2Hit = perfData[PCM::PERF_GEN_EVENT_3_POS]; + } + else +#endif + { + msr->read(INST_RETIRED_ANY_ADDR, &cInstRetiredAny); + msr->read(CPU_CLK_UNHALTED_THREAD_ADDR, &cCpuClkUnhaltedThread); + msr->read(CPU_CLK_UNHALTED_REF_ADDR, &cCpuClkUnhaltedRef); + switch (cpu_model) + { + case PCM::WESTMERE_EP: + case PCM::NEHALEM_EP: + case PCM::NEHALEM_EX: + case PCM::WESTMERE_EX: + case PCM::CLARKDALE: + case PCM::SANDY_BRIDGE: + case PCM::JAKETOWN: + case PCM::IVYTOWN: + case PCM::HASWELLX: + case PCM::IVY_BRIDGE: + case PCM::HASWELL: + case PCM::BROADWELL: + msr->read(IA32_PMC0, &cL3Miss); + msr->read(IA32_PMC1, &cL3UnsharedHit); + msr->read(IA32_PMC2, &cL2HitM); + msr->read(IA32_PMC3, &cL2Hit); + break; + case PCM::ATOM: + msr->read(IA32_PMC0, &cL3Miss); // for Atom mapped to ArchLLCMiss field + msr->read(IA32_PMC1, &cL3UnsharedHit); // for Atom mapped to ArchLLCRef field + break; + } + } + + // std::cout << "DEBUG1: "<< msr->getCoreId() << " " << cInstRetiredAny<< " "<< std::endl; + if(m->L3CacheOccupancyMetricAvailable()) + msr->read(IA32_QM_CTR,&cL3Occupancy); + + if(cpu_model != PCM::ATOM || m->getOriginalCPUModel() == PCM::ATOM_AVOTON) msr->read(IA32_TIME_STAMP_COUNTER, &cInvariantTSC); + else + { +#ifdef _MSC_VER + cInvariantTSC = ((uint64)(GetTickCount()/1000))*m->getNominalFrequency(); +#else + struct timeval tp; + gettimeofday(&tp, NULL); + cInvariantTSC = (double(tp.tv_sec) + tp.tv_usec / 1000000.)*m->getNominalFrequency(); +#endif + } + + // reading core C state counters + for(int i=0; i <= PCM::MAX_C_STATE ;++i) + if(m->coreCStateMsr && m->coreCStateMsr[i]) + msr->read(m->coreCStateMsr[i], &(cCStateResidency[i])); + + // reading temperature + msr->read(MSR_IA32_THERM_STATUS, &thermStatus); + + InstRetiredAny += m->extractCoreFixedCounterValue(cInstRetiredAny); + CpuClkUnhaltedThread += m->extractCoreFixedCounterValue(cCpuClkUnhaltedThread); + CpuClkUnhaltedRef += m->extractCoreFixedCounterValue(cCpuClkUnhaltedRef); + L3Miss += m->extractCoreGenCounterValue(cL3Miss); + L3UnsharedHit += m->extractCoreGenCounterValue(cL3UnsharedHit); + //std::cout << "Scaling Factor " << m->L3ScalingFactor; + cL3Occupancy = m->extractL3CacheOccupancy(cL3Occupancy); + L3Occupancy = (cL3Occupancy==PCM_INVALID_L3_CACHE_OCCUPANCY)? PCM_INVALID_L3_CACHE_OCCUPANCY : (uint64)((double)(cL3Occupancy * m->L3ScalingFactor) / 1024.0); + L2HitM += m->extractCoreGenCounterValue(cL2HitM); + L2Hit += m->extractCoreGenCounterValue(cL2Hit); + InvariantTSC += cInvariantTSC; + for(int i=0; i <= PCM::MAX_C_STATE ;++i) + CStateResidency[i] += cCStateResidency[i]; + ThermalHeadroom = extractThermalHeadroom(thermStatus); +} + +PCM::ErrorCode PCM::programServerUncorePowerMetrics(int mc_profile, int pcu_profile, int * freq_bands) +{ + if(MSR == NULL || server_pcicfg_uncore == NULL) return PCM::MSRAccessDenied; + + uint32 PCUCntConf[4] = {0,0,0,0}; + + PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0); // clock ticks + + switch(pcu_profile) + { + case 0: + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0xB); // FREQ_BAND0_CYCLES + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0xC); // FREQ_BAND1_CYCLES + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0xD); // FREQ_BAND2_CYCLES + break; + case 1: + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x80) + PCU_MSR_PMON_CTL_OCC_SEL(1); // POWER_STATE_OCCUPANCY.C0 using CLOCKTICKS + 8th-bit + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x80) + PCU_MSR_PMON_CTL_OCC_SEL(2); // POWER_STATE_OCCUPANCY.C3 using CLOCKTICKS + 8th-bit + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x80) + PCU_MSR_PMON_CTL_OCC_SEL(3); // POWER_STATE_OCCUPANCY.C6 using CLOCKTICKS + 8th-bit + break; + case 2: + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x09); // PROCHOT_INTERNAL_CYCLES + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x0A); // PROCHOT_EXTERNAL_CYCLES + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x04); // Thermal frequency limit cycles: FREQ_MAX_LIMIT_THERMAL_CYCLES + break; + case 3: + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x04); // Thermal frequency limit cycles: FREQ_MAX_LIMIT_THERMAL_CYCLES + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x05); // Power frequency limit cycles: FREQ_MAX_POWER_CYCLES + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x07); // Clipped frequency limit cycles: FREQ_MAX_CURRENT_CYCLES + break; + case 4: + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x06); // OS frequency limit cycles: FREQ_MAX_OS_CYCLES + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x05); // Power frequency limit cycles: FREQ_MAX_POWER_CYCLES + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x07); // Clipped frequency limit cycles: FREQ_MAX_CURRENT_CYCLES + break; + case 5: + if(JAKETOWN == cpu_model) + { + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0) + PCU_MSR_PMON_CTL_EXTRA_SEL + PCU_MSR_PMON_CTL_EDGE_DET ; // number of frequency transitions + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0) + PCU_MSR_PMON_CTL_EXTRA_SEL ; // cycles spent changing frequency + } else if (IVYTOWN == cpu_model ) + { + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x60) + PCU_MSR_PMON_CTL_EDGE_DET ; // number of frequency transitions + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x60) ; // cycles spent changing frequency: FREQ_TRANS_CYCLES + } else if (HASWELLX == cpu_model ) + { + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x74) + PCU_MSR_PMON_CTL_EDGE_DET ; // number of frequency transitions + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x74) ; // cycles spent changing frequency: FREQ_TRANS_CYCLES + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x79) + PCU_MSR_PMON_CTL_EDGE_DET ; // number of UFS transitions + PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x79) ; // UFS transition cyclesss + } else + { + std::cerr << "ERROR: no frequency transition events defined for CPU model "<< cpu_model << std::endl; + } + break; + case 6: + if (IVYTOWN == cpu_model ) + { + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x2B) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC2 transitions + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x2D) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC6 transitions + } else if (HASWELLX == cpu_model ) + { + PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x4E) ; // PC1e residencys + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x4E) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC1 transitions + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x2B) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC2 transitions + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x2D) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC6 transitions + } else + { + std::cerr << "ERROR: no package C-state transition events defined for CPU model "<< cpu_model << std::endl; + } + break; + case 7: + if (HASWELLX == cpu_model ) + { + PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x7E) ; // UFS_TRANSITIONS_PERF_P_LIMIT + PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x7D) ; // UFS_TRANSITIONS_IO_P_LIMIT + PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x7A) ; // UFS_TRANSITIONS_UP_RING_TRAFFIC + PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x7B) ; // UFS_TRANSITIONS_UP_STALL_CYCLES + } else + { + std::cerr << "ERROR: no UFS transition events defined for CPU model "<< cpu_model << std::endl; + } + break; + case 8: + if (HASWELLX == cpu_model ) + { + PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x7C) ; // UFS_TRANSITIONS_DOWN + } else + { + std::cerr << "ERROR: no UFS transition events defined for CPU model "<< cpu_model << std::endl; + } + break; + default: + std::cerr << "ERROR: unsupported PCU profile "<< pcu_profile << std::endl; + } + + uint32 PCU_MSR_PMON_BOX_FILTER_ADDR, PCU_MSR_PMON_CTLX_ADDR[4] = { 0, 0, 0, 0 }; + + switch(cpu_model) + { + case IVYTOWN: + case JAKETOWN: + PCU_MSR_PMON_BOX_FILTER_ADDR = JKTIVT_PCU_MSR_PMON_BOX_FILTER_ADDR; + PCU_MSR_PMON_CTLX_ADDR[0] = JKTIVT_PCU_MSR_PMON_CTL0_ADDR; + PCU_MSR_PMON_CTLX_ADDR[1] = JKTIVT_PCU_MSR_PMON_CTL1_ADDR; + PCU_MSR_PMON_CTLX_ADDR[2] = JKTIVT_PCU_MSR_PMON_CTL2_ADDR; + PCU_MSR_PMON_CTLX_ADDR[3] = JKTIVT_PCU_MSR_PMON_CTL3_ADDR; + break; + case HASWELLX: + PCU_MSR_PMON_BOX_FILTER_ADDR = HSX_PCU_MSR_PMON_BOX_FILTER_ADDR; + PCU_MSR_PMON_CTLX_ADDR[0] = HSX_PCU_MSR_PMON_CTL0_ADDR; + PCU_MSR_PMON_CTLX_ADDR[1] = HSX_PCU_MSR_PMON_CTL1_ADDR; + PCU_MSR_PMON_CTLX_ADDR[2] = HSX_PCU_MSR_PMON_CTL2_ADDR; + PCU_MSR_PMON_CTLX_ADDR[3] = HSX_PCU_MSR_PMON_CTL3_ADDR; + break; + default: + std::cerr << "Error: unsupported uncore. cpu model is "<< cpu_model << std::endl; + return PCM::UnknownError; + } + + for (int i = 0; (i < num_sockets) && server_pcicfg_uncore && MSR; ++i) + { + server_pcicfg_uncore[i]->program_power_metrics(mc_profile); + + uint32 refCore = socketRefCore[i]; + TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux + + // freeze enable + MSR[refCore]->write(PCU_MSR_PMON_BOX_CTL_ADDR, PCU_MSR_PMON_BOX_CTL_FRZ_EN); + // freeze + MSR[refCore]->write(PCU_MSR_PMON_BOX_CTL_ADDR, PCU_MSR_PMON_BOX_CTL_FRZ_EN + PCU_MSR_PMON_BOX_CTL_FRZ); + +#ifdef PCM_UNCORE_PMON_BOX_CHECK_STATUS + uint64 val = 0; + MSR[refCore]->read(PCU_MSR_PMON_BOX_CTL_ADDR,&val); + if ((val & UNCORE_PMON_BOX_CTL_VALID_BITS_MASK) != (PCU_MSR_PMON_BOX_CTL_FRZ_EN + PCU_MSR_PMON_BOX_CTL_FRZ)) + std::cerr << "ERROR: PCU counter programming seems not to work. PCU_MSR_PMON_BOX_CTL=0x" << std::hex << val << " needs to be =0x"<< (PCU_MSR_PMON_BOX_CTL_FRZ_EN + PCU_MSR_PMON_BOX_CTL_FRZ) << std::endl; +#endif + + if(freq_bands == NULL) + MSR[refCore]->write(PCU_MSR_PMON_BOX_FILTER_ADDR, + PCU_MSR_PMON_BOX_FILTER_BAND_0(10) + // 1000 MHz + PCU_MSR_PMON_BOX_FILTER_BAND_1(20) + // 2000 MHz + PCU_MSR_PMON_BOX_FILTER_BAND_2(30) // 3000 MHz + ); + else + MSR[refCore]->write(PCU_MSR_PMON_BOX_FILTER_ADDR, + PCU_MSR_PMON_BOX_FILTER_BAND_0(freq_bands[0]) + + PCU_MSR_PMON_BOX_FILTER_BAND_1(freq_bands[1]) + + PCU_MSR_PMON_BOX_FILTER_BAND_2(freq_bands[2]) + ); + + for(int ctr = 0; ctr < 4; ++ctr) + { + MSR[refCore]->write(PCU_MSR_PMON_CTLX_ADDR[ctr], PCU_MSR_PMON_CTL_EN); + MSR[refCore]->write(PCU_MSR_PMON_CTLX_ADDR[ctr], PCU_MSR_PMON_CTL_EN + PCUCntConf[ctr]); + } + + // reset counter values + MSR[refCore]->write(PCU_MSR_PMON_BOX_CTL_ADDR, PCU_MSR_PMON_BOX_CTL_FRZ_EN + PCU_MSR_PMON_BOX_CTL_FRZ + PCU_MSR_PMON_BOX_CTL_RST_COUNTERS); + + // unfreeze counters + MSR[refCore]->write(PCU_MSR_PMON_BOX_CTL_ADDR, PCU_MSR_PMON_BOX_CTL_FRZ_EN); + + } + return PCM::Success; +} + +void PCM::freezeServerUncoreCounters() +{ + for (int i = 0; (i < num_sockets) && server_pcicfg_uncore && MSR; ++i) + { + server_pcicfg_uncore[i]->freezeCounters(); + MSR[socketRefCore[i]]->write(PCU_MSR_PMON_BOX_CTL_ADDR, PCU_MSR_PMON_BOX_CTL_FRZ_EN + PCU_MSR_PMON_BOX_CTL_FRZ); + } +} +void PCM::unfreezeServerUncoreCounters() +{ + for (int i = 0; (i < num_sockets) && server_pcicfg_uncore && MSR; ++i) + { + server_pcicfg_uncore[i]->unfreezeCounters(); + MSR[socketRefCore[i]]->write(PCU_MSR_PMON_BOX_CTL_ADDR, PCU_MSR_PMON_BOX_CTL_FRZ_EN); + } +} +void UncoreCounterState::readAndAggregate(SafeMsrHandle * msr) +{ + TemporalThreadAffinity tempThreadAffinity(msr->getCoreId()); // speedup trick for Linux + + PCM::getInstance()->readAndAggregatePackageCStateResidencies(msr, *this); +} + + +SystemCounterState PCM::getSystemCounterState() +{ + SystemCounterState result; + if (MSR) + { + // read core and uncore counter state + for (int32 core = 0; core < num_cores; ++core) + result.readAndAggregate(MSR[core]); + + for (int32 s=0; s < num_sockets; s++) + { + readAndAggregateUncoreMCCounters(s, result); + readAndAggregateEnergyCounters(s, result); + } + + readQPICounters(result); + + result.ThermalHeadroom = PCM_INVALID_THERMAL_HEADROOM; // not available for system + } + return result; +} + +template +void PCM::readAndAggregateUncoreMCCounters(const uint32 socket, CounterStateType & result) +{ + if (hasPCICFGUncore()) + { + if (server_pcicfg_uncore) + { + server_pcicfg_uncore[socket]->freezeCounters(); + result.UncMCNormalReads += server_pcicfg_uncore[socket]->getImcReads(); + result.UncMCFullWrites += server_pcicfg_uncore[socket]->getImcWrites(); + server_pcicfg_uncore[socket]->unfreezeCounters(); + } + } + else if(clientBW && socket == 0) + { + result.UncMCNormalReads += clientImcReads->read(); + result.UncMCFullWrites += clientImcWrites->read(); + result.UncMCIORequests += clientIoRequests->read(); + } + else + { + SafeMsrHandle * msr = MSR[socketRefCore[socket]]; + TemporalThreadAffinity tempThreadAffinity(socketRefCore[socket]); // speedup trick for Linux + switch (cpu_model) + { + case PCM::WESTMERE_EP: + case PCM::NEHALEM_EP: + { + uint64 cUncMCFullWrites = 0; + uint64 cUncMCNormalReads = 0; + msr->read(MSR_UNCORE_PMC0, &cUncMCFullWrites); + msr->read(MSR_UNCORE_PMC1, &cUncMCNormalReads); + result.UncMCFullWrites += extractUncoreGenCounterValue(cUncMCFullWrites); + result.UncMCNormalReads += extractUncoreGenCounterValue(cUncMCNormalReads); + } + break; + case PCM::NEHALEM_EX: + case PCM::WESTMERE_EX: + { + uint64 cUncMCNormalReads = 0; + msr->read(MB0_MSR_PMU_CNT_0, &cUncMCNormalReads); + result.UncMCNormalReads += extractUncoreGenCounterValue(cUncMCNormalReads); + msr->read(MB1_MSR_PMU_CNT_0, &cUncMCNormalReads); + result.UncMCNormalReads += extractUncoreGenCounterValue(cUncMCNormalReads); + + uint64 cUncMCFullWrites = 0; // really good approximation of + msr->read(BB0_MSR_PERF_CNT_1, &cUncMCFullWrites); + result.UncMCFullWrites += extractUncoreGenCounterValue(cUncMCFullWrites); + msr->read(BB1_MSR_PERF_CNT_1, &cUncMCFullWrites); + result.UncMCFullWrites += extractUncoreGenCounterValue(cUncMCFullWrites); + } + break; + + default:; + } + } +} + +template +void PCM::readAndAggregateEnergyCounters(const uint32 socket, CounterStateType & result) +{ + if(socket < snb_energy_status.size()) + result.PackageEnergyStatus += snb_energy_status[socket]->read(); + + if(socket < jkt_dram_energy_status.size()) + result.DRAMEnergyStatus += jkt_dram_energy_status[socket]->read(); +} + + +template +void PCM::readAndAggregatePackageCStateResidencies(SafeMsrHandle * msr, CounterStateType & result) +{ + // reading package C state counters + uint64 cCStateResidency[PCM::MAX_C_STATE + 1]; + memset(&(cCStateResidency[0]), 0, sizeof(cCStateResidency)); + + for(int i=0; i <= PCM::MAX_C_STATE ;++i) + if(pkgCStateMsr && pkgCStateMsr[i]) + msr->read(pkgCStateMsr[i], &(cCStateResidency[i])); + + if (&result!=NULL) { // to make security check happy + + for(int i=0; i <= PCM::MAX_C_STATE ;++i) + result.CStateResidency[i] += cCStateResidency[i]; + } +} + +void PCM::readQPICounters(SystemCounterState & result) +{ + // read QPI counters + std::vector SocketProcessed(num_sockets, false); + if (cpu_model == PCM::NEHALEM_EX || cpu_model == PCM::WESTMERE_EX) + { + for (int32 core = 0; core < num_cores; ++core) + { + if(isCoreOnline(core) == false) continue; + + if(core == socketRefCore[0]) MSR[core]->read(W_MSR_PMON_FIXED_CTR, &(result.uncoreTSC)); + + uint32 s = topology[core].socket; + + if (!SocketProcessed[s]) + { + TemporalThreadAffinity tempThreadAffinity(core); // speedup trick for Linux + + // incoming data responses from QPI link 0 + MSR[core]->read(R_MSR_PMON_CTR1, &(result.incomingQPIPackets[s][0])); + // incoming data responses from QPI link 1 (yes, from CTR0) + MSR[core]->read(R_MSR_PMON_CTR0, &(result.incomingQPIPackets[s][1])); + // incoming data responses from QPI link 2 + MSR[core]->read(R_MSR_PMON_CTR8, &(result.incomingQPIPackets[s][2])); + // incoming data responses from QPI link 3 + MSR[core]->read(R_MSR_PMON_CTR9, &(result.incomingQPIPackets[s][3])); + + // outgoing idle flits from QPI link 0 + MSR[core]->read(R_MSR_PMON_CTR3, &(result.outgoingQPIIdleFlits[s][0])); + // outgoing idle flits from QPI link 1 (yes, from CTR0) + MSR[core]->read(R_MSR_PMON_CTR2, &(result.outgoingQPIIdleFlits[s][1])); + // outgoing idle flits from QPI link 2 + MSR[core]->read(R_MSR_PMON_CTR10, &(result.outgoingQPIIdleFlits[s][2])); + // outgoing idle flits from QPI link 3 + MSR[core]->read(R_MSR_PMON_CTR11, &(result.outgoingQPIIdleFlits[s][3])); + + SocketProcessed[s] = true; + } + } + } + else if ((cpu_model == PCM::NEHALEM_EP || cpu_model == PCM::WESTMERE_EP)) + { + if (num_sockets == 2) + { + uint32 SCore[2] = { 0, 0 }; + uint64 Total_Reads[2] = { 0, 0 }; + uint64 Total_Writes[2] = { 0, 0 }; + uint64 IOH_Reads[2] = { 0, 0 }; + uint64 IOH_Writes[2] = { 0, 0 }; + uint64 Remote_Reads[2] = { 0, 0 }; + uint64 Remote_Writes[2] = { 0, 0 }; + uint64 Local_Reads[2] = { 0, 0 }; + uint64 Local_Writes[2] = { 0, 0 }; + + while (topology[SCore[0]].socket != 0) ++(SCore[0]); + while (topology[SCore[1]].socket != 1) ++(SCore[1]); + for (int s = 0; s < 2; ++s) + { + TemporalThreadAffinity tempThreadAffinity(SCore[s]); // speedup trick for Linux + + MSR[SCore[s]]->read(MSR_UNCORE_PMC0, &Total_Writes[s]); + MSR[SCore[s]]->read(MSR_UNCORE_PMC1, &Total_Reads[s]); + MSR[SCore[s]]->read(MSR_UNCORE_PMC2, &IOH_Reads[s]); + MSR[SCore[s]]->read(MSR_UNCORE_PMC3, &IOH_Writes[s]); + MSR[SCore[s]]->read(MSR_UNCORE_PMC4, &Remote_Reads[s]); + MSR[SCore[s]]->read(MSR_UNCORE_PMC5, &Remote_Writes[s]); + MSR[SCore[s]]->read(MSR_UNCORE_PMC6, &Local_Reads[s]); + MSR[SCore[s]]->read(MSR_UNCORE_PMC7, &Local_Writes[s]); + } + +#if 1 + // compute Remote_Reads differently + for (int s = 0; s < 2; ++s) + { + uint64 total = Total_Writes[s] + Total_Reads[s]; + uint64 rem = IOH_Reads[s] + + IOH_Writes[s] + + Local_Reads[s] + + Local_Writes[s] + + Remote_Writes[s]; + Remote_Reads[s] = (total > rem) ? (total - rem) : 0; + } +#endif + + + // only an estimation (lower bound) - does not count NT stores correctly + result.incomingQPIPackets[0][0] = Remote_Reads[1] + Remote_Writes[0]; + result.incomingQPIPackets[0][1] = IOH_Reads[0]; + result.incomingQPIPackets[1][0] = Remote_Reads[0] + Remote_Writes[1]; + result.incomingQPIPackets[1][1] = IOH_Reads[1]; + } + else + { + // for a single socket systems no information is available + result.incomingQPIPackets[0][0] = 0; + } + } + else if (hasPCICFGUncore()) + { + if (server_pcicfg_uncore) + for (int32 s = 0; (s < num_sockets); ++s) + { + server_pcicfg_uncore[s]->freezeCounters(); + for (uint32 port = 0; port < getQPILinksPerSocket(); ++port) + { + result.incomingQPIPackets[s][port] = server_pcicfg_uncore[s]->getIncomingDataFlits(port) / 8; + result.outgoingQPIDataNonDataFlits[s][port] = server_pcicfg_uncore[s]->getOutgoingDataNonDataFlits(port); + } + server_pcicfg_uncore[s]->unfreezeCounters(); + } + } + // end of reading QPI counters +} + +template +void PCM::readPackageThermalHeadroom(const uint32 socket, CounterStateType & result) +{ + if(packageThermalMetricsAvailable()) + { + uint64 val = 0; + MSR[socketRefCore[socket]]->read(MSR_PACKAGE_THERM_STATUS,&val); + result.ThermalHeadroom = extractThermalHeadroom(val); + } + else + result.ThermalHeadroom = PCM_INVALID_THERMAL_HEADROOM; // not available +} + +SocketCounterState PCM::getSocketCounterState(uint32 socket) +{ + SocketCounterState result; + if (MSR) + { + // reading core and uncore counter states + for (int32 core = 0; core < num_cores; ++core) + if (isCoreOnline(core) && (topology[core].socket == int32(socket))) + result.readAndAggregate(MSR[core]); + + readAndAggregateUncoreMCCounters(socket, result); + + readAndAggregateEnergyCounters(socket, result); + + readPackageThermalHeadroom(socket, result); + + } + return result; +} + +void PCM::getAllCounterStates(SystemCounterState & systemState, std::vector & socketStates, std::vector & coreStates) +{ + // clear and zero-initialize all inputs + systemState = SystemCounterState(); + socketStates.clear(); + socketStates.resize(num_sockets); + coreStates.clear(); + coreStates.resize(num_cores); + + for (int32 core = 0; core < num_cores; ++core) + { + // read core counters + if(isCoreOnline(core)) + { + coreStates[core].readAndAggregate(MSR[core]); + socketStates[topology[core].socket].UncoreCounterState::readAndAggregate(MSR[core]); // read package C state counters + } + // std::cout << "DEBUG2: "<< core<< " "<< coreStates[core].InstRetiredAny << " "<< std::endl; + } + + for (int32 s=0; s < num_sockets; ++s) + { + readAndAggregateUncoreMCCounters(s, socketStates[s]); + readAndAggregateEnergyCounters(s, socketStates[s]); + readPackageThermalHeadroom(s, socketStates[s]); + } + + readQPICounters(systemState); + + for (int32 core = 0; core < num_cores; ++core) + { // aggregate core counters into sockets + if(isCoreOnline(core)) + socketStates[topology[core].socket].accumulateCoreState(coreStates[core]); + } + + for (int32 s = 0; s < num_sockets; ++s) + { // aggregate core counters from sockets into system state and + // aggregate socket uncore iMC, energy and package C state counters into system + systemState.accumulateSocketState(socketStates[s]); + } +} + +CoreCounterState PCM::getCoreCounterState(uint32 core) +{ + CoreCounterState result; + if (MSR) result.readAndAggregate(MSR[core]); + return result; +} + +uint32 PCM::getNumCores() +{ + return num_cores; +} + +uint32 PCM::getNumOnlineCores() +{ + return num_online_cores; +} + +uint32 PCM::getNumSockets() +{ + return num_sockets; +} + +uint32 PCM::getThreadsPerCore() +{ + return threads_per_core; +} + +bool PCM::getSMT() +{ + return threads_per_core > 1; +} + +uint64 PCM::getNominalFrequency() +{ + return nominal_frequency; +} + +uint32 PCM::getL3ScalingFactor() +{ + PCM_CPUID_INFO cpuinfo; + pcm_cpuid(0xf,0x1,cpuinfo); + + return cpuinfo.reg.ebx; + +} + +ServerUncorePowerState PCM::getServerUncorePowerState(uint32 socket) +{ + ServerUncorePowerState result; + if(server_pcicfg_uncore && server_pcicfg_uncore[socket]) + { + server_pcicfg_uncore[socket]->freezeCounters(); + for(uint32 port=0;port < server_pcicfg_uncore[socket]->getNumQPIPorts();++port) + { + result.QPIClocks[port] = server_pcicfg_uncore[socket]->getQPIClocks(port); + result.QPIL0pTxCycles[port] = server_pcicfg_uncore[socket]->getQPIL0pTxCycles(port); + result.QPIL1Cycles[port] = server_pcicfg_uncore[socket]->getQPIL1Cycles(port); + } + for(uint32 channel=0;channelgetNumMCChannels();++channel) + { + result.DRAMClocks[channel] = server_pcicfg_uncore[socket]->getDRAMClocks(channel); + for(uint32 cnt=0;cnt<4;++cnt) + result.MCCounter[channel][cnt] = server_pcicfg_uncore[socket]->getMCCounter(channel,cnt); + } + server_pcicfg_uncore[socket]->unfreezeCounters(); + } + if(MSR) + { + uint32 refCore = socketRefCore[socket]; + TemporalThreadAffinity tempThreadAffinity(refCore); + for(int i=0; i<4; ++i) + MSR[refCore]->read(PCU_MSR_PMON_CTRX_ADDR[i],&(result.PCUCounter[i])); + // std::cout<< "values read: " << result.PCUCounter[0]<<" "<read(MSR_PKG_ENERGY_STATUS,&val); + //std::cout << "Energy status: "<< val << std::endl; + MSR[refCore]->read(MSR_PACKAGE_THERM_STATUS,&val); + result.PackageThermalHeadroom = extractThermalHeadroom(val); + MSR[refCore]->read(IA32_TIME_STAMP_COUNTER, &result.InvariantTSC); + readAndAggregatePackageCStateResidencies(MSR[refCore], result); + } + + readAndAggregateEnergyCounters(socket, result); + + return result; +} + +#ifndef _MSC_VER +void print_mcfg(const char * path) +{ + int mcfg_handle = ::open(path, O_RDONLY); + + if (mcfg_handle < 0) + { + std::cerr << "PCM Error: Cannot open " << path << std::endl; + throw std::exception(); + } + + MCFGHeader header; + + ssize_t read_bytes = ::read(mcfg_handle, (void *)&header, sizeof(MCFGHeader)); + + if(read_bytes == 0) + { + std::cerr << "PCM Error: Cannot read " << path << std::endl; + throw std::exception(); + } + + const unsigned segments = header.nrecords(); + header.print(); + std::cout << "Segments: "< > ServerPCICFGUncore::socket2bus; + +void ServerPCICFGUncore::initSocket2Bus() +{ + if(!socket2bus.empty()) return; + + #ifdef __linux__ + const std::vector & mcfg = PciHandleMM::getMCFGRecords(); + #else + std::vector mcfg; + MCFGRecord segment; + segment.PCISegmentGroupNumber = 0; + segment.startBusNumber = 0; + segment.endBusNumber = 0xff; + mcfg.push_back(segment); + #endif + + + for(uint32 s = 0; s < mcfg.size(); ++s) + for(uint32 bus = mcfg[s].startBusNumber; bus <= mcfg[s].endBusNumber; ++bus) + { + uint32 value = 0; + try + { + PciHandleM h(mcfg[s].PCISegmentGroupNumber, bus, MCX_CHY_REGISTER_DEV_ADDR[0][0], MCX_CHY_REGISTER_FUNC_ADDR[0][0]); + h.read32(0, &value); + + } catch(...) + { + // reached invalid bus + return; + } + const uint32 vendor_id = value & 0xffff; + const uint32 device_id = (value >> 16) & 0xffff; + if (vendor_id != PCM_INTEL_PCI_VENDOR_ID) + continue; + + for(uint32 i = 0; i< sizeof(IMC_DEV_IDS)/sizeof(IMC_DEV_IDS[0]) ; ++i) + { + // match + if(IMC_DEV_IDS[i] == device_id) + { + // std::cout << "DEBUG: found bus "<> 8)& 0x0ff; + // std::cout << "socket: "<< cur_socket<< std::hex << " cpubusno: 0x"<< std::hex << cpubusno << " "< 0x0ff) + return -1; + } + + return -1; +} + +PciHandleM * ServerPCICFGUncore::createIntelPerfMonDevice(uint32 groupnr_, uint32 bus_, uint32 dev_, uint32 func_, bool checkVendor) +{ + if (PciHandleM::exists(bus_, dev_, func_)) + { + PciHandleM * handle = new PciHandleM(groupnr_, bus_, dev_, func_); + + if(!checkVendor) return handle; + + uint32 vendor_id = 0; + handle->read32(PCM_PCI_VENDOR_ID_OFFSET,&vendor_id); + vendor_id &= 0x0ffff; + + if(vendor_id == PCM_INTEL_PCI_VENDOR_ID) return handle; + + delete handle; + } + return NULL; +} + +ServerPCICFGUncore::ServerPCICFGUncore(uint32 socket_, PCM * pcm) : + bus(-1) + , groupnr(0) + , imcHandles(NULL) + , num_imc_channels(0) + , qpiLLHandles(NULL) + , num_qpi_ports(0) + , qpi_speed(0) + , num_imc(0) +{ + +#define PCM_PCICFG_MC_INIT(controller, channel, arch) \ + MCX_CHY_REGISTER_DEV_ADDR[controller][channel] = arch##_MC##controller##_CH##channel##_REGISTER_DEV_ADDR; \ + MCX_CHY_REGISTER_FUNC_ADDR[controller][channel] = arch##_MC##controller##_CH##channel##_REGISTER_FUNC_ADDR; + + const uint32 cpu_model = pcm->getCPUModel(); + + if(cpu_model == PCM::JAKETOWN || cpu_model == PCM::IVYTOWN) + { + PCM_PCICFG_MC_INIT(0, 0, JKTIVT) + PCM_PCICFG_MC_INIT(0, 1, JKTIVT) + PCM_PCICFG_MC_INIT(0, 2, JKTIVT) + PCM_PCICFG_MC_INIT(0, 3, JKTIVT) + PCM_PCICFG_MC_INIT(1, 0, JKTIVT) + PCM_PCICFG_MC_INIT(1, 1, JKTIVT) + PCM_PCICFG_MC_INIT(1, 2, JKTIVT) + PCM_PCICFG_MC_INIT(1, 3, JKTIVT) + } + else if(cpu_model == PCM::HASWELLX) + { + PCM_PCICFG_MC_INIT(0, 0, HSX) + PCM_PCICFG_MC_INIT(0, 1, HSX) + PCM_PCICFG_MC_INIT(0, 2, HSX) + PCM_PCICFG_MC_INIT(0, 3, HSX) + PCM_PCICFG_MC_INIT(1, 0, HSX) + PCM_PCICFG_MC_INIT(1, 1, HSX) + PCM_PCICFG_MC_INIT(1, 2, HSX) + PCM_PCICFG_MC_INIT(1, 3, HSX) + } + else + { + std::cout << "Error: Uncore PMU for processor with model id "<< cpu_model << " is not supported."<< std::endl; + throw std::exception(); + } + +#undef PCM_PCICFG_MC_INIT + + initSocket2Bus(); + const uint32 total_sockets_ = pcm->getNumSockets(); + + if(total_sockets_ == socket2bus.size()) + { + groupnr = socket2bus[socket_].first; + bus = socket2bus[socket_].second; + + } else if(total_sockets_ <= 4) + { + bus = getBusFromSocket(socket_); + if(bus < 0) + { + std::cerr << "Cannot find bus for socket "<< socket_ <<" on system with "<< total_sockets_ << " sockets."<< std::endl; + throw std::exception(); + } + else + { + std::cerr << "PCM Warning: the bus for socket "<< socket_ <<" on system with "<< total_sockets_ << " sockets could not find via PCI bus scan. Using cpubusno register. Bus = "<< bus << std::endl; + } + } + else + { + std::cerr << "Cannot find bus for socket "<< socket_ <<" on system with "<< total_sockets_ << " sockets."<< std::endl; + throw std::exception(); + } + + imcHandles = new PciHandleM *[8]; + + { +#define PCM_PCICFG_SETUP_MC_HANDLE(controller,channel) \ + { \ + PciHandleM * handle = createIntelPerfMonDevice(groupnr, bus, \ + MCX_CHY_REGISTER_DEV_ADDR[controller][channel], MCX_CHY_REGISTER_FUNC_ADDR[controller][channel], true); \ + if(handle) imcHandles[num_imc_channels++] = handle; \ + } + + PCM_PCICFG_SETUP_MC_HANDLE(0,0) + PCM_PCICFG_SETUP_MC_HANDLE(0,1) + PCM_PCICFG_SETUP_MC_HANDLE(0,2) + PCM_PCICFG_SETUP_MC_HANDLE(0,3) + + if(num_imc_channels > 0) ++num_imc; // at least one memory controller + const uint32 num_imc_channels1 = num_imc_channels; + + PCM_PCICFG_SETUP_MC_HANDLE(1,0) + PCM_PCICFG_SETUP_MC_HANDLE(1,1) + PCM_PCICFG_SETUP_MC_HANDLE(1,2) + PCM_PCICFG_SETUP_MC_HANDLE(1,3) + + if(num_imc_channels > num_imc_channels1 ) ++num_imc; // another memory controller found + +#undef PCM_PCICFG_SETUP_MC_HANDLE + } + + if (num_imc_channels == 0) + { + delete [] imcHandles; + imcHandles = NULL; + std::cerr << "PCM error: no memory controllers found." << std::endl; + throw std::exception(); + } + + if (num_imc_channels < 3) + { + std::cerr << "Intel PCM: warning only " << num_imc_channels << " memory channels detected, must be >= 3." << std::endl; + } + + if (total_sockets_ == 1) { + /* + * For single socket systems, do not worry at all about QPI ports. This + * eliminates QPI LL programming error messages on single socket systems + * with BIOS that hides QPI performance counting PCI functions. It also + * eliminates register programming that is not needed since no QPI traffic + * is possible with single socket systems. + */ + num_qpi_ports = 0; + std::cerr << num_imc<<" memory controllers detected with total number of "<< num_imc_channels <<" channels. " << std::endl; + return; + } + +#ifdef PCM_NOQPI + num_qpi_ports = 0; + std::cerr << num_imc<<" memory controllers detected with total number of "<< num_imc_channels <<" channels. " << std::endl; + return; +#else + qpiLLHandles = new PciHandleM *[3]; + for(uint32 i=0; i<3; ++i) + qpiLLHandles[i] = NULL; + + #define PCM_PCICFG_QPI_INIT(port, arch) \ + QPI_PORTX_REGISTER_DEV_ADDR[port] = arch##_QPI_PORT##port##_REGISTER_DEV_ADDR; \ + QPI_PORTX_REGISTER_FUNC_ADDR[port] = arch##_QPI_PORT##port##_REGISTER_FUNC_ADDR; + + if(cpu_model == PCM::JAKETOWN || cpu_model == PCM::IVYTOWN) + { + PCM_PCICFG_QPI_INIT(0, JKTIVT); + PCM_PCICFG_QPI_INIT(1, JKTIVT); + PCM_PCICFG_QPI_INIT(2, JKTIVT); + } + else if(cpu_model == PCM::HASWELLX) + { + PCM_PCICFG_QPI_INIT(0, HSX); + PCM_PCICFG_QPI_INIT(1, HSX); + PCM_PCICFG_QPI_INIT(2, HSX); + } + else + { + std::cout << "Error: Uncore PMU for processor with model id "<< cpu_model << " is not supported."<< std::endl; + throw std::exception(); + } + + #undef PCM_PCICFG_QPI_INIT + + try + { + { + PciHandleM * handle = createIntelPerfMonDevice(groupnr, bus, QPI_PORTX_REGISTER_DEV_ADDR[0], QPI_PORTX_REGISTER_FUNC_ADDR[0], true); + if (handle) + qpiLLHandles[num_qpi_ports++] = handle; + else + std::cerr << "ERROR: QPI LL monitoring device ("<< groupnr<<":"<write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN); + // freeze + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN + MC_CH_PCI_PMON_BOX_CTL_FRZ); + +#ifdef PCM_UNCORE_PMON_BOX_CHECK_STATUS + uint32 val = 0; + imcHandles[i]->read32(MC_CH_PCI_PMON_BOX_CTL_ADDR, &val); + if ((val & UNCORE_PMON_BOX_CTL_VALID_BITS_MASK) != (MC_CH_PCI_PMON_BOX_CTL_FRZ_EN + MC_CH_PCI_PMON_BOX_CTL_FRZ)) + { + std::cerr << "ERROR: IMC counter programming seems not to work. MC_CH" << i << "_PCI_PMON_BOX_CTL=0x" << std::hex << val << std::endl; + std::cerr << " Please see BIOS options to enable the export of performance monitoring devices." << std::endl; + } +#endif + + // enable counter 0 + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL0_ADDR, MC_CH_PCI_PMON_CTL_EN); + + // monitor reads on counter 0: CAS_COUNT.RD + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL0_ADDR, MC_CH_PCI_PMON_CTL_EN + MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(3)); + + // enable counter 1 + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL1_ADDR, MC_CH_PCI_PMON_CTL_EN); + + // monitor writes on counter 1: CAS_COUNT.WR + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL1_ADDR, MC_CH_PCI_PMON_CTL_EN + MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(12)); + + // enable counter 2 + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL2_ADDR, MC_CH_PCI_PMON_CTL_EN); + + // monitor partial writes on counter 2: CAS_COUNT.RD_UNDERFILL + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL2_ADDR, MC_CH_PCI_PMON_CTL_EN + MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(2)); + + // enable fixed counter (DRAM clocks) + imcHandles[i]->write32(MC_CH_PCI_PMON_FIXED_CTL_ADDR, MC_CH_PCI_PMON_FIXED_CTL_EN); + + // reset it + imcHandles[i]->write32(MC_CH_PCI_PMON_FIXED_CTL_ADDR, MC_CH_PCI_PMON_FIXED_CTL_EN + MC_CH_PCI_PMON_FIXED_CTL_RST); + + // reset counters values + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN + MC_CH_PCI_PMON_BOX_CTL_FRZ + MC_CH_PCI_PMON_BOX_CTL_RST_COUNTERS); + + // unfreeze counters + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN); + } + + for (uint32 i = 0; i < num_qpi_ports; ++i) + { + // QPI LL PMU + + // freeze enable + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN); + // freeze + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN + Q_P_PCI_PMON_BOX_CTL_RST_FRZ); + +#ifdef PCM_UNCORE_PMON_BOX_CHECK_STATUS + uint32 val = 0; + qpiLLHandles[i]->read32(Q_P_PCI_PMON_BOX_CTL_ADDR, &val); + if ((val & UNCORE_PMON_BOX_CTL_VALID_BITS_MASK) != (Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN + Q_P_PCI_PMON_BOX_CTL_RST_FRZ)) + { + std::cerr << "ERROR: QPI LL counter programming seems not to work. Q_P" << i << "_PCI_PMON_BOX_CTL=0x" << std::hex << val << std::endl; + std::cerr << " Please see BIOS options to enable the export of performance monitoring devices (devices 8 and 9: function 2)." << std::endl; + } +#endif + + // enable counter 0 + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL0_ADDR, Q_P_PCI_PMON_CTL_EN); + + // monitor DRS data received on counter 0: RxL_FLITS_G1.DRS_DATA + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL0_ADDR, Q_P_PCI_PMON_CTL_EN + Q_P_PCI_PMON_CTL_EVENT(0x02) + Q_P_PCI_PMON_CTL_EVENT_EXT + Q_P_PCI_PMON_CTL_UMASK(8)); + + // enable counter 1 + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL1_ADDR, Q_P_PCI_PMON_CTL_EN); + + // monitor NCB data received on counter 1: RxL_FLITS_G2.NCB_DATA + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL1_ADDR, Q_P_PCI_PMON_CTL_EN + Q_P_PCI_PMON_CTL_EVENT(0x03) + Q_P_PCI_PMON_CTL_EVENT_EXT + Q_P_PCI_PMON_CTL_UMASK(4)); + + // enable counter 2 + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL2_ADDR, Q_P_PCI_PMON_CTL_EN); + + // monitor outgoing data+nondata flits on counter 2: TxL_FLITS_G0.DATA + TxL_FLITS_G0.NON_DATA + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL2_ADDR, Q_P_PCI_PMON_CTL_EN + Q_P_PCI_PMON_CTL_EVENT(0x00) + Q_P_PCI_PMON_CTL_UMASK(6)); + + // enable counter 3 + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL3_ADDR, Q_P_PCI_PMON_CTL_EN); + + // monitor QPI clocks + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL3_ADDR, Q_P_PCI_PMON_CTL_EN + Q_P_PCI_PMON_CTL_EVENT(0x14)); // QPI clocks (CLOCKTICKS) + + // reset counters values + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN + Q_P_PCI_PMON_BOX_CTL_RST_FRZ + Q_P_PCI_PMON_BOX_CTL_RST_COUNTERS); + + // unfreeze counters + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN); + } +} + +uint64 ServerPCICFGUncore::getImcReads() +{ + uint64 result = 0; + + for (uint32 i = 0; i < num_imc_channels; ++i) + { + uint64 value = 0; + imcHandles[i]->read64(MC_CH_PCI_PMON_CTR0_ADDR, &value); + result += value; + } + + return result; +} + +uint64 ServerPCICFGUncore::getImcWrites() +{ + uint64 result = 0; + + for (uint32 i = 0; i < num_imc_channels; ++i) + { + uint64 value = 0; + imcHandles[i]->read64(MC_CH_PCI_PMON_CTR1_ADDR, &value); + result += value; + } + + return result; +} + +uint64 ServerPCICFGUncore::getIncomingDataFlits(uint32 port) +{ + uint64 drs = 0, ncb = 0; + + if (port >= num_qpi_ports) + return 0; + + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR0_ADDR, &drs); + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR1_ADDR, &ncb); + + return drs + ncb; +} + +uint64 ServerPCICFGUncore::getOutgoingDataNonDataFlits(uint32 port) +{ + return getQPILLCounter(port,2); +} + +void ServerPCICFGUncore::program_power_metrics(int mc_profile) +{ + for (uint32 i = 0; i < num_qpi_ports; ++i) + { + // QPI LL PMU + + // freeze enable + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN); + // freeze + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN + Q_P_PCI_PMON_BOX_CTL_RST_FRZ); + +#ifdef PCM_UNCORE_PMON_BOX_CHECK_STATUS + uint32 val = 0; + qpiLLHandles[i]->read32(Q_P_PCI_PMON_BOX_CTL_ADDR, &val); + if ((val & UNCORE_PMON_BOX_CTL_VALID_BITS_MASK) != (Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN + Q_P_PCI_PMON_BOX_CTL_RST_FRZ)) + { + std::cerr << "ERROR: QPI LL counter programming seems not to work. Q_P" << i << "_PCI_PMON_BOX_CTL=0x" << std::hex << val << std::endl; + std::cerr << " Please see BIOS options to enable the export of performance monitoring devices." << std::endl; + } +#endif + + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL3_ADDR, Q_P_PCI_PMON_CTL_EN); + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL3_ADDR, Q_P_PCI_PMON_CTL_EN + Q_P_PCI_PMON_CTL_EVENT(0x14)); // QPI clocks (CLOCKTICKS) + + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL0_ADDR, Q_P_PCI_PMON_CTL_EN); + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL0_ADDR, Q_P_PCI_PMON_CTL_EN + Q_P_PCI_PMON_CTL_EVENT(0x0D)); // L0p Tx Cycles (TxL0P_POWER_CYCLES) + + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL2_ADDR, Q_P_PCI_PMON_CTL_EN); + qpiLLHandles[i]->write32(Q_P_PCI_PMON_CTL2_ADDR, Q_P_PCI_PMON_CTL_EN + Q_P_PCI_PMON_CTL_EVENT(0x12)); // L1 Cycles (L1_POWER_CYCLES) + + // reset counters values + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN + Q_P_PCI_PMON_BOX_CTL_RST_FRZ + Q_P_PCI_PMON_BOX_CTL_RST_COUNTERS); + + // unfreeze counters + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN); + } + + uint32 MCCntConfig[4] = {0,0,0,0}; + switch(mc_profile) + { + case 0: // POWER_CKE_CYCLES.RANK0 and POWER_CKE_CYCLES.RANK1 + MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(1) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(1) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(2) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(2) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + break; + case 1: // POWER_CKE_CYCLES.RANK2 and POWER_CKE_CYCLES.RANK3 + MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(4) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(4) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(8) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(8) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + break; + case 2: // POWER_CKE_CYCLES.RANK4 and POWER_CKE_CYCLES.RANK5 + MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x10) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x10) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x20) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x20) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + break; + case 3: // POWER_CKE_CYCLES.RANK6 and POWER_CKE_CYCLES.RANK7 + MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x40) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x40) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x80) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); + MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(0x83) + MC_CH_PCI_PMON_CTL_UMASK(0x80) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + break; + case 4: // POWER_SELF_REFRESH + MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x43); + MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x43) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; + MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x85); + break; + } + + for (uint32 i = 0; i < num_imc_channels; ++i) + { + // imc PMU + + // freeze enable + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN); + // freeze + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN + MC_CH_PCI_PMON_BOX_CTL_FRZ); + +#ifdef PCM_UNCORE_PMON_BOX_CHECK_STATUS + uint32 val = 0; + imcHandles[i]->read32(MC_CH_PCI_PMON_BOX_CTL_ADDR, &val); + if ((val & UNCORE_PMON_BOX_CTL_VALID_BITS_MASK) != (MC_CH_PCI_PMON_BOX_CTL_FRZ_EN + MC_CH_PCI_PMON_BOX_CTL_FRZ)) + { + std::cerr << "ERROR: IMC counter programming seems not to work. MC_CH" << i << "_PCI_PMON_BOX_CTL=0x" << std::hex << val << std::endl; + std::cerr << " Please see BIOS options to enable the export of performance monitoring devices." << std::endl; + } +#endif + + // enable fixed counter (DRAM clocks) + imcHandles[i]->write32(MC_CH_PCI_PMON_FIXED_CTL_ADDR, MC_CH_PCI_PMON_FIXED_CTL_EN); + + // reset it + imcHandles[i]->write32(MC_CH_PCI_PMON_FIXED_CTL_ADDR, MC_CH_PCI_PMON_FIXED_CTL_EN + MC_CH_PCI_PMON_FIXED_CTL_RST); + + + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL0_ADDR, MC_CH_PCI_PMON_CTL_EN); + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL0_ADDR, MC_CH_PCI_PMON_CTL_EN + MCCntConfig[0]); + + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL1_ADDR, MC_CH_PCI_PMON_CTL_EN); + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL1_ADDR, MC_CH_PCI_PMON_CTL_EN + MCCntConfig[1]); + + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL2_ADDR, MC_CH_PCI_PMON_CTL_EN); + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL2_ADDR, MC_CH_PCI_PMON_CTL_EN + MCCntConfig[2]); + + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL3_ADDR, MC_CH_PCI_PMON_CTL_EN); + imcHandles[i]->write32(MC_CH_PCI_PMON_CTL3_ADDR, MC_CH_PCI_PMON_CTL_EN + MCCntConfig[3]); + + // reset counters values + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN + MC_CH_PCI_PMON_BOX_CTL_FRZ + MC_CH_PCI_PMON_BOX_CTL_RST_COUNTERS); + + // unfreeze counters + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN); + } +} + +void ServerPCICFGUncore::freezeCounters() +{ + for (uint32 i = 0; i < num_qpi_ports; ++i) + { + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN + Q_P_PCI_PMON_BOX_CTL_RST_FRZ); + } + for (uint32 i = 0; i < num_imc_channels; ++i) + { + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN + MC_CH_PCI_PMON_BOX_CTL_FRZ); + } +} + +void ServerPCICFGUncore::unfreezeCounters() +{ + for (uint32 i = 0; i < num_qpi_ports; ++i) + { + qpiLLHandles[i]->write32(Q_P_PCI_PMON_BOX_CTL_ADDR, Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN); + } + for (uint32 i = 0; i < num_imc_channels; ++i) + { + imcHandles[i]->write32(MC_CH_PCI_PMON_BOX_CTL_ADDR, MC_CH_PCI_PMON_BOX_CTL_FRZ_EN); + } +} + +uint64 ServerPCICFGUncore::getQPIClocks(uint32 port) +{ + uint64 res = 0; + + if (port >= num_qpi_ports) + return 0; + + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR3_ADDR, &res); + + return res; +} + +uint64 ServerPCICFGUncore::getQPIL0pTxCycles(uint32 port) +{ + uint64 res = 0; + + if (port >= num_qpi_ports) + return 0; + + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR0_ADDR, &res); + + return res; +} + +uint64 ServerPCICFGUncore::getQPIL1Cycles(uint32 port) +{ + uint64 res = 0; + + if (port >= num_qpi_ports) + return 0; + + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR2_ADDR, &res); + + return res; +} + +uint64 ServerPCICFGUncore::getDRAMClocks(uint32 channel) +{ + uint64 result = 0; + + if(channel < num_imc_channels) + imcHandles[channel]->read64(MC_CH_PCI_PMON_FIXED_CTR_ADDR, &result); + + return result; +} + +uint64 ServerPCICFGUncore::getMCCounter(uint32 channel, uint32 counter) +{ + uint64 result = 0; + + if(channel < num_imc_channels) + { + switch(counter) + { + case 0: + imcHandles[channel]->read64(MC_CH_PCI_PMON_CTR0_ADDR, &result); + break; + case 1: + imcHandles[channel]->read64(MC_CH_PCI_PMON_CTR1_ADDR, &result); + break; + case 2: + imcHandles[channel]->read64(MC_CH_PCI_PMON_CTR2_ADDR, &result); + break; + case 3: + imcHandles[channel]->read64(MC_CH_PCI_PMON_CTR3_ADDR, &result); + break; + } + } + + return result; +} + +uint64 ServerPCICFGUncore::getQPILLCounter(uint32 port, uint32 counter) +{ + uint64 result = 0; + + if(port < num_qpi_ports) + { + switch(counter) + { + case 0: + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR0_ADDR, &result); + break; + case 1: + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR1_ADDR, &result); + break; + case 2: + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR2_ADDR, &result); + break; + case 3: + qpiLLHandles[port]->read64(Q_P_PCI_PMON_CTR3_ADDR, &result); + break; + } + } + + return result; +} + +void ServerPCICFGUncore::enableJKTWorkaround(bool enable) +{ + { + PciHandleM reg(groupnr,bus,14,0); + uint32 value = 0; + reg.read32(0x84, &value); + if(enable) + value |= 2; + else + value &= (~2); + reg.write32(0x84, value); + } + { + PciHandleM reg(groupnr,bus,8,0); + uint32 value = 0; + reg.read32(0x80, &value); + if(enable) + value |= 2; + else + value &= (~2); + reg.write32(0x80, value); + } + { + PciHandleM reg(groupnr,bus,9,0); + uint32 value = 0; + reg.read32(0x80, &value); + if(enable) + value |= 2; + else + value &= (~2); + reg.write32(0x80, value); + } +} + + +uint64 ServerPCICFGUncore::computeQPISpeed(const uint32 core_nr, const int cpumodel) +{ + if(qpi_speed.size()==0) + { + qpi_speed.resize(num_qpi_ports); + for (uint32 i=0; igetTickCount(timerGranularity, core_nr); + uint64 endTSC; + do + { + endTSC = pcm->getTickCount(timerGranularity, core_nr); + } while (endTSC - startTSC < 200000ULL); // spin for 200 ms + + uint64 endClocks = getQPIClocks(i); + + qpi_speed[i] = ((std::max)((endClocks - startClocks) * 16ULL * timerGranularity / (endTSC - startTSC),0ULL)); + if(cpumodel == PCM::HASWELLX) { + qpi_speed[i] /=2; // HSX runs QPI clocks with doubled speed + } + } + } + } + if(!qpi_speed.empty()) + { + return *std::max_element(qpi_speed.begin(),qpi_speed.end()); + } + else + { + return 0; + } +} + +#ifdef _MSC_VER +DWORD WINAPI WatchDogProc(LPVOID state) +#else +void * WatchDogProc(void * state) +#endif +{ + CounterWidthExtender * ext = (CounterWidthExtender * ) state; + while(1) + { +#ifdef _MSC_VER + Sleep(10000); +#else + sleep(10); +#endif + /* uint64 dummy = */ ext->read(); + } + return NULL; +} + +uint32 PCM::CX_MSR_PMON_CTRY(uint32 Cbo, uint32 Ctr) const +{ + if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) + { + return JKT_C0_MSR_PMON_CTR0 + ((JKTIVT_CBO_MSR_STEP)*Cbo) + Ctr; + + } else if(HASWELLX == cpu_model) + { + return HSX_C0_MSR_PMON_CTR0 + ((HSX_CBO_MSR_STEP)*Cbo) + Ctr; + } + return 0; +} + +uint32 PCM::CX_MSR_PMON_BOX_FILTER(uint32 Cbo) const +{ + if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) + { + return JKT_C0_MSR_PMON_BOX_FILTER + ((JKTIVT_CBO_MSR_STEP)*Cbo); + + } else if(HASWELLX == cpu_model) + { + return HSX_C0_MSR_PMON_BOX_FILTER + ((HSX_CBO_MSR_STEP)*Cbo); + } + + return 0; +} + +uint32 PCM::CX_MSR_PMON_BOX_FILTER1(uint32 Cbo) const +{ + if(IVYTOWN == cpu_model) + { + return IVT_C0_MSR_PMON_BOX_FILTER1 + ((JKTIVT_CBO_MSR_STEP)*Cbo); + + } else if(HASWELLX == cpu_model) + { + return HSX_C0_MSR_PMON_BOX_FILTER1 + ((HSX_CBO_MSR_STEP)*Cbo); + } + return 0; +} + +uint32 PCM::CX_MSR_PMON_CTLY(uint32 Cbo, uint32 Ctl) const +{ + if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) + { + return JKT_C0_MSR_PMON_CTL0 + ((JKTIVT_CBO_MSR_STEP)*Cbo) + Ctl; + + } else if(HASWELLX == cpu_model) + { + return HSX_C0_MSR_PMON_CTL0 + ((HSX_CBO_MSR_STEP)*Cbo) + Ctl; + } + return 0; +} + +uint32 PCM::CX_MSR_PMON_BOX_CTL(uint32 Cbo) const +{ + if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) + { + return JKT_C0_MSR_PMON_BOX_CTL + ((JKTIVT_CBO_MSR_STEP)*Cbo); + + } else if(HASWELLX == cpu_model) + { + return HSX_C0_MSR_PMON_BOX_CTL + ((HSX_CBO_MSR_STEP)*Cbo); + } + return 0; +} + +uint32 PCM::getMaxNumOfCBoxes() const +{ + if(1) + { + /* + * There is one CBox per physical core. This calculation will get us + * the number of physical cores per socket which is the expected + * value to be returned. + */ + return num_phys_cores_per_socket; + } + return 0; +} + +void PCM::programCboOpcodeFilter(const uint32 opc, const uint32 cbo, SafeMsrHandle * msr) +{ + if(JAKETOWN == cpu_model) + { + msr->write(CX_MSR_PMON_BOX_FILTER(cbo), JKT_CBO_MSR_PMON_BOX_FILTER_OPC(opc)); + + } else if(IVYTOWN == cpu_model || HASWELLX == cpu_model) + { + msr->write(CX_MSR_PMON_BOX_FILTER1(cbo), IVTHSX_CBO_MSR_PMON_BOX_FILTER1_OPC(opc)); + } +} + +void PCM::programPCIeMissCounters(const PCM::PCIeEventCode event_, const uint32 tid_) +{ + programPCIeCounters(event_,tid_,1); +} + +void PCM::programPCIeCounters(const PCM::PCIeEventCode event_, const uint32 tid_, const uint32 miss_) +{ + for (int32 i = 0; (i < num_sockets) && MSR; ++i) + { + uint32 refCore = socketRefCore[i]; + TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux + + for(uint32 cbo = 0; cbo < getMaxNumOfCBoxes(); ++cbo) + { + // freeze enable + MSR[refCore]->write(CX_MSR_PMON_BOX_CTL(cbo), CBO_MSR_PMON_BOX_CTL_FRZ_EN); + // freeze + MSR[refCore]->write(CX_MSR_PMON_BOX_CTL(cbo), CBO_MSR_PMON_BOX_CTL_FRZ_EN + CBO_MSR_PMON_BOX_CTL_FRZ); + +#ifdef PCM_UNCORE_PMON_BOX_CHECK_STATUS + uint64 val = 0; + MSR[refCore]->read(CX_MSR_PMON_BOX_CTL(cbo), &val); + if ((val & UNCORE_PMON_BOX_CTL_VALID_BITS_MASK) != (CBO_MSR_PMON_BOX_CTL_FRZ_EN + CBO_MSR_PMON_BOX_CTL_FRZ)) + { + std::cerr << "ERROR: CBO counter programming seems not to work. "; + std::cerr << "C" << std::dec << cbo << "_MSR_PMON_BOX_CTL=0x" << std::hex << val << std::endl; + } +#endif + + programCboOpcodeFilter(event_, cbo, MSR[refCore]); + + if(HASWELLX == cpu_model && tid_ != 0) + MSR[refCore]->write(CX_MSR_PMON_BOX_FILTER(cbo), tid_); + + MSR[refCore]->write(CX_MSR_PMON_CTLY(cbo, 0), CBO_MSR_PMON_CTL_EN); + // TOR_INSERTS.OPCODE event + MSR[refCore]->write(CX_MSR_PMON_CTLY(cbo, 0), CBO_MSR_PMON_CTL_EN + CBO_MSR_PMON_CTL_EVENT(0x35) + (CBO_MSR_PMON_CTL_UMASK(1) | (miss_?CBO_MSR_PMON_CTL_UMASK(0x3):0ULL)) + (tid_?CBO_MSR_PMON_CTL_TID_EN:0ULL)); + + // reset counter values + MSR[refCore]->write(CX_MSR_PMON_BOX_CTL(cbo), CBO_MSR_PMON_BOX_CTL_FRZ_EN + CBO_MSR_PMON_BOX_CTL_FRZ + CBO_MSR_PMON_BOX_CTL_RST_COUNTERS); + + // unfreeze counters + MSR[refCore]->write(CX_MSR_PMON_BOX_CTL(cbo), CBO_MSR_PMON_BOX_CTL_FRZ_EN); + } + } +} + +PCIeCounterState PCM::getPCIeCounterState(const uint32 socket_) +{ + PCIeCounterState result; + + uint32 refCore = socketRefCore[socket_]; + TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux + + for(uint32 cbo=0; cbo < getMaxNumOfCBoxes(); ++cbo) + { + uint64 ctrVal = 0; + MSR[refCore]->read(CX_MSR_PMON_CTRY(cbo, 0), &ctrVal); + result.data += ctrVal; + } + return result; +} diff --git a/cpucounters.h b/cpucounters.h new file mode 100644 index 0000000..b89d74b --- /dev/null +++ b/cpucounters.h @@ -0,0 +1,2170 @@ +/* +Copyright (c) 2009-2013, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// Thomas Willhalm + +#ifndef CPUCOUNTERS_HEADER +#define CPUCOUNTERS_HEADER + +/*! \file cpucounters.h + \brief Main CPU counters header + + Include this header file if you want to access CPU counters (core and uncore - including memory controller chips and QPI) +*/ + +#define INTEL_PCM_VERSION "V2.8 (2014-12-18 12:52:39 +0100 ID=ba39a89)" + +#ifndef INTELPCM_API +#define INTELPCM_API +#endif + +#include "types.h" +#include "msr.h" +#include "pci.h" +#include "client_bw.h" +#include "width_extender.h" +#include +#include +#include +#include + +#ifdef PCM_USE_PERF +#include +#include +#include +#define PCM_PERF_COUNT_HW_REF_CPU_CYCLES (9) +#endif + +#ifndef _MSC_VER +#define NOMINMAX +#include +#include +#include +#include +#include +#endif + +class SystemCounterState; +class SocketCounterState; +class CoreCounterState; +class BasicCounterState; +class ServerUncorePowerState; +class PCM; + +/* + CPU performance monitoring routines + + A set of performance monitoring routines for recent Intel CPUs +*/ + +struct INTELPCM_API TopologyEntry // decribes a core +{ + int32 os_id; + int32 socket; + int32 core_id; + + TopologyEntry() : os_id(-1), socket(-1), core_id(-1) { } +}; + +//! Object to access uncore counters in a socket/processor with microarchitecture codename SandyBridge-EP (Jaketown) or Ivytown-EP or Ivytown-EX +class ServerPCICFGUncore +{ + int bus, groupnr; + PciHandleM ** imcHandles; + uint32 num_imc_channels; + + PciHandleM ** qpiLLHandles; + uint32 num_qpi_ports; + std::vector qpi_speed; + uint32 num_imc; + uint32 MCX_CHY_REGISTER_DEV_ADDR[2][4]; + uint32 MCX_CHY_REGISTER_FUNC_ADDR[2][4]; + uint32 QPI_PORTX_REGISTER_DEV_ADDR[3]; + uint32 QPI_PORTX_REGISTER_FUNC_ADDR[3]; + + static std::vector > socket2bus; + void initSocket2Bus(); + + ServerPCICFGUncore(); // forbidden + ServerPCICFGUncore(ServerPCICFGUncore &); // forbidden + PciHandleM * createIntelPerfMonDevice(uint32 groupnr, uint32 bus, uint32 dev, uint32 func, bool checkVendor = false); + +public: + //! \brief Initialize access data structures + //! \param socket_ socket id + //! \param pcm pointer to PCM instance + ServerPCICFGUncore(uint32 socket_, PCM * pcm); + //! \brief Program performance counters (disables programming power counters) + void program(); + //! \brief Get the number of integrated controller reads (in cache lines) + uint64 getImcReads(); + //! \brief Get the number of integrated controller writes (in cache lines) + uint64 getImcWrites(); + + //! \brief Get the number of incoming data flits to the socket through a port + //! \param port QPI port id + uint64 getIncomingDataFlits(uint32 port); + + //! \brief Get the number of outgoing data and non-data flits from the socket through a port + //! \param port QPI port id + uint64 getOutgoingDataNonDataFlits(uint32 port); + + virtual ~ServerPCICFGUncore(); + + //! \brief Program power counters (disables programming performance counters) + //! \param mc_profile memory controller measurement profile. See description of profiles in pcm-power.cpp + void program_power_metrics(int mc_profile); + + //! \brief Get number of QPI LL clocks on a QPI port + //! \param port QPI port number + uint64 getQPIClocks(uint32 port); + + //! \brief Get number cycles on a QPI port when the link was in a power saving half-lane mode + //! \param port QPI port number + uint64 getQPIL0pTxCycles(uint32 port); + //! \brief Get number cycles on a QPI port when the link was in a power saving shutdown mode + //! \param port QPI port number + uint64 getQPIL1Cycles(uint32 port); + //! \brief Get number DRAM channel cycles + //! \param channel channel number + uint64 getDRAMClocks(uint32 channel); + //! \brief Direct read of memory controller PMU counter (counter meaning depends on the programming: power/performance/etc) + //! \param channel channel number + //! \param counter counter number + uint64 getMCCounter(uint32 channel, uint32 counter); + //! \brief Direct read of QPI LL PMU counter (counter meaning depends on the programming: power/performance/etc) + //! \param port port number + //! \param counter counter number + uint64 getQPILLCounter(uint32 port, uint32 counter); + + //! \brief Freezes event counting + void freezeCounters(); + //! \brief Unfreezes event counting + void unfreezeCounters(); + + //! \brief Measures/computes the maximum theoretical QPI link bandwidth speed in GByte/seconds + uint64 computeQPISpeed(const uint32 ref_core, const int cpumodel); + + //! \brief Enable correct counting of various LLC events (with memory access perf penalty) + void enableJKTWorkaround(bool enable); + + //! \brief Returns the number of detected QPI ports + uint32 getNumQPIPorts() const { return num_qpi_ports; } + + //! \brief Returns the speed of the QPI link + uint64 getQPILinkSpeed(const uint32 linkNr) const { + return qpi_speed.empty() ? 0 : qpi_speed[linkNr]; + } + + //! \brief Print QPI Speeds + void reportQPISpeed() const + { + std::cerr.precision(1); + std::cerr << std::fixed; + for (uint32 i=0; i; +template class INTELPCM_API std::vector; +template class INTELPCM_API std::allocator; +template class INTELPCM_API std::vector; +template class INTELPCM_API std::allocator; +template class INTELPCM_API std::vector; +template class INTELPCM_API std::allocator; +#endif +/*! + \brief CPU Performance Monitor + + This singleton object needs to be instantiated for each process + before accessing counting and measuring routines +*/ +class INTELPCM_API PCM +{ + friend class BasicCounterState; + friend class UncoreCounterState; + PCM(); // forbidden to call directly because it is a singleton + + int32 cpu_family; + int32 cpu_model, original_cpu_model; + int32 threads_per_core; + int32 num_cores; + int32 num_sockets; + int32 num_phys_cores_per_socket; + int32 num_online_cores; + uint32 core_gen_counter_num_max; + uint32 core_gen_counter_num_used; + uint32 core_gen_counter_width; + uint32 core_fixed_counter_num_max; + uint32 core_fixed_counter_num_used; + uint32 core_fixed_counter_width; + uint32 uncore_gen_counter_num_max; + uint32 uncore_gen_counter_num_used; + uint32 uncore_gen_counter_width; + uint32 uncore_fixed_counter_num_max; + uint32 uncore_fixed_counter_num_used; + uint32 uncore_fixed_counter_width; + int32 perfmon_version; + int32 perfmon_config_anythread; + uint64 nominal_frequency; + uint64 max_qpi_speed; // in GBytes/second + uint32 L3ScalingFactor; + int32 pkgThermalSpecPower, pkgMinimumPower, pkgMaximumPower; + + std::vector topology; + std::string errorMessage; + + static PCM * instance; + bool allow_multiple_instances; + bool programmed_pmu; + SafeMsrHandle ** MSR; + ServerPCICFGUncore ** server_pcicfg_uncore; + uint32 PCU_MSR_PMON_BOX_CTL_ADDR, PCU_MSR_PMON_CTRX_ADDR[4]; + double joulesPerEnergyUnit; + std::vector snb_energy_status; + std::vector jkt_dram_energy_status; + + + ClientBW * clientBW; + CounterWidthExtender * clientImcReads; + CounterWidthExtender * clientImcWrites; + CounterWidthExtender * clientIoRequests; + + bool disable_JKT_workaround; + bool blocked; // track if time-driven counter update is running or not: PCM is blocked + + uint64 * coreCStateMsr; // MSR addresses of core C-state free-running counters + uint64 * pkgCStateMsr; // MSR addresses of package C-state free-running counters + +public: + enum { MAX_C_STATE = 10 }; // max C-state on Intel architecture + + //! \brief Returns true if the specified core C-state residency metric is supported + bool isCoreCStateResidencySupported(int state) + { + if (state == 0 || state == 1) + return true; + + return (coreCStateMsr != NULL && state <= MAX_C_STATE && coreCStateMsr[state] != 0); + } + + //! \brief Returns true if the specified package C-state residency metric is supported + bool isPackageCStateResidencySupported(int state) + { + return (pkgCStateMsr != NULL && state <= MAX_C_STATE && pkgCStateMsr[state] != 0); + } + + //! \brief Redirects output destination to provided file, instead of std::cout + void setOutput(const std::string filename); + + //! \brief Restores output, closes output file if opened + void restoreOutput(); + + //! \brief Set Run State. + // Arguments: + // -- 1 - program is running + // -- 0 -pgram is sleeping + void setRunState(int new_state) { run_state = new_state; } + + //! \brief Returns program's Run State. + // Results: + // -- 1 - program is running + // -- 0 -pgram is sleeping + int getRunState(void) { return run_state; } + + bool isBlocked(void) { return blocked; } + void setBlocked(const bool new_blocked) { blocked = new_blocked; } + + //! \brief Call it before program() to allow multiple running instances of PCM on the same system + void allowMultipleInstances() + { + allow_multiple_instances = true; + } + + //! Mode of programming (parameter in the program() method) + enum ProgramMode { + DEFAULT_EVENTS = 0, /*!< Default choice of events, the additional parameter is not needed and ignored */ + CUSTOM_CORE_EVENTS = 1, /*!< Custom set of core events specified in the parameter to the program method. The parameter must be a pointer to array of four \c CustomCoreEventDescription values */ + EXT_CUSTOM_CORE_EVENTS = 2, /*!< Custom set of core events specified in the parameter to the program method. The parameter must be a pointer to a \c ExtendedCustomCoreEventDescription data structure */ + INVALID_MODE /*!< Non-programmed mode */ + }; + + //! Return codes (e.g. for program(..) method) + enum ErrorCode { + Success = 0, + MSRAccessDenied = 1, + PMUBusy = 2, + UnknownError + }; + + /*! \brief Custom Core event description + + See "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: + System Programming Guide, Part 2" for the concrete values of the data structure fields, + e.g. Appendix A.2 "Performance Monitoring Events for Intel(r) Core(tm) Processor Family + and Xeon Processor Family" + */ + struct CustomCoreEventDescription + { + int32 event_number, umask_value; + }; + + /*! \brief Extended custom core event description + + In contrast to CustomCoreEventDescription supports configuration of all fields. + + See "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: + System Programming Guide, Part 2" for the concrete values of the data structure fields, + e.g. Appendix A.2 "Performance Monitoring Events for Intel(r) Core(tm) Processor Family + and Xeon Processor Family" + */ + struct ExtendedCustomCoreEventDescription + { + FixedEventControlRegister * fixedCfg; // if NULL, then default configuration performed for fixed counters + uint32 nGPCounters; // number of general purpose counters + EventSelectRegister * gpCounterCfg; // general purpose counters, if NULL, then default configuration performed for GP counters + uint64 OffcoreResponseMsrValue[2]; + }; + +private: + ProgramMode mode; + CustomCoreEventDescription coreEventDesc[4]; + + #ifdef _MSC_VER + HANDLE numInstancesSemaphore; // global semaphore that counts the number of PCM instances on the system + #else + // global semaphore that counts the number of PCM instances on the system + sem_t * numInstancesSemaphore; + #endif + + std::vector socketRefCore; + + bool canUsePerf; +#ifdef PCM_USE_PERF + std::vector< std::vector > perfEventHandle; + void readPerfData(uint32 core, std::vector & data); + + enum { + PERF_INST_RETIRED_ANY_POS = 0, + PERF_CPU_CLK_UNHALTED_THREAD_POS = 1, + PERF_CPU_CLK_UNHALTED_REF_POS = 2, + PERF_GEN_EVENT_0_POS = 3, + PERF_GEN_EVENT_1_POS = 4, + PERF_GEN_EVENT_2_POS = 5, + PERF_GEN_EVENT_3_POS = 6 + }; + + enum { + PERF_GROUP_LEADER_COUNTER = PERF_INST_RETIRED_ANY_POS + }; +#endif + std::ofstream *outfile; // output file stream + std::streambuf *backup_ofile; // backup of original output = cout + int run_state; // either running (1) or sleeping (0) + + bool PMUinUse(); + void cleanupPMU(); + void freeRMID(); + bool decrementInstanceSemaphore(); // returns true if it was the last instance + +#ifdef __APPLE__ + // OSX does not have sem_getvalue, so we must get the number of instances by a different method + uint32 getNumInstances(); + uint32 decrementNumInstances(); + uint32 incrementNumInstances(); +#endif + + + void computeQPISpeedBeckton(int core_nr); + void destroyMSR(); + void computeNominalFrequency(); + static bool isCPUModelSupported(int model_); + std::string getSupportedUarchCodenames() const; + std::string getUnsupportedMessage() const; + bool detectModel(); + bool checkModel(); + + void initCStateSupportTables(); + bool discoverSystemTopology(); + void printSystemTopology() const; + void initMSR(); + bool detectNominalFrequency(); + void initEnergyMonitoring(); + void initUncoreObjects(); + /*! + * \brief initializes each core with RMId for cache monitoring + * + * \returns nothing + */ + void initL3CacheOccupancyMonitoring(); + void programBecktonUncore(int core); + void programNehalemEPUncore(int core); + void enableJKTWorkaround(bool enable); + template + void readAndAggregateUncoreMCCounters(const uint32 socket, CounterStateType & counterState); + template + void readAndAggregateEnergyCounters(const uint32 socket, CounterStateType & counterState); + template + void readPackageThermalHeadroom(const uint32 socket, CounterStateType & counterState); + template + void readAndAggregatePackageCStateResidencies(SafeMsrHandle * msr, CounterStateType & result); + void readQPICounters(SystemCounterState & counterState); + void reportQPISpeed() const; + + uint32 CX_MSR_PMON_CTRY(uint32 Cbo, uint32 Ctr) const; + uint32 CX_MSR_PMON_BOX_FILTER(uint32 Cbo) const; + uint32 CX_MSR_PMON_BOX_FILTER1(uint32 Cbo) const; + uint32 CX_MSR_PMON_CTLY(uint32 Cbo, uint32 Ctl) const; + uint32 CX_MSR_PMON_BOX_CTL(uint32 Cbo) const; + uint32 getMaxNumOfCBoxes() const; + void programCboOpcodeFilter(const uint32 opc, const uint32 cbo, SafeMsrHandle * msr); + +public: + /*! + \brief checks if cache monitoring present + + \returns true or false + */ + bool L3CacheOccupancyMetricAvailable(); + + /*! + * \brief returns the max number of RMID supported by socket + * + * \returns maximum number of RMID supported by socket + */ + unsigned getMaxRMID() const; + + /*! + \brief Returns PCM object + + Returns PCM object. If the PCM has not been created before than + an instance is created. PCM is a singleton. + + \return Pointer to PCM object + */ + + + + static PCM * getInstance(); // the only way to get access + + /*! + \brief Checks the status of PCM object + + Call this method to check if PCM gained access to model specific registers. The method is deprecated, see program error code instead. + + \return true iff access to model specific registers works without problems + */ + bool good(); // true if access to CPU counters works + + /*! \brief Returns the error message + + Call this when good() returns false, otherwise return an empty string + */ + const std::string & getErrorMessage() const + { + return errorMessage; + } + + /*! \brief Programs performance counters + \param mode_ mode of programming, see ProgramMode definition + \param parameter_ optional parameter for some of programming modes + + Call this method before you start using the performance counting routines. + + \warning Using this routines with other tools that *program* Performance Monitoring + Units (PMUs) on CPUs is not recommended because PMU can not be shared. Tools that are known to + program PMUs: Intel(r) VTune(tm), Intel(r) Performance Tuning Utility (PTU). This code may make + VTune or PTU measurements invalid. VTune or PTU measurement may make measurement with this code invalid. Please enable either usage of these routines or VTune/PTU/etc. + */ + ErrorCode program(const ProgramMode mode_ = DEFAULT_EVENTS, const void * parameter_ = NULL); // program counters and start counting + + /*! \brief Programs uncore power/energy counters on microarchitectures codename SandyBridge-EP and IvyTown + \param mc_profile profile for integrated memory controller PMU. See possible profile values in pcm-power.cpp example + \param pcu_profile profile for power control unit PMU. See possible profile values in pcm-power.cpp example + \param freq_bands array of three integer values for core frequency band monitoring. See usage in pcm-power.cpp example + + Call this method before you start using the power counter routines on microarchitecture codename SandyBridge-EP + + \warning After this call the memory and QPI bandwidth counters on microarchitecture codename SandyBridge-EP will not work. + \warning Using this routines with other tools that *program* Performance Monitoring + Units (PMUs) on CPUs is not recommended because PMU can not be shared. Tools that are known to + program PMUs: Intel(r) VTune(tm), Intel(r) Performance Tuning Utility (PTU). This code may make + VTune or PTU measurements invalid. VTune or PTU measurement may make measurement with this code invalid. Please enable either usage of these routines or VTune/PTU/etc. + */ + ErrorCode programServerUncorePowerMetrics(int mc_profile, int pcu_profile, int * freq_bands = NULL); + + //! \brief Freezes uncore event counting (works only on microarchitecture codename SandyBridge-EP and IvyTown) + void freezeServerUncoreCounters(); + + //! \brief Unfreezes uncore event counting (works only on microarchitecture codename SandyBridge-EP and IvyTown) + void unfreezeServerUncoreCounters(); + + /*! \brief Reads the power/energy counter state of a socket (works only on microarchitecture codename SandyBridge-EP) + \param socket socket id + \return State of power counters in the socket + */ + ServerUncorePowerState getServerUncorePowerState(uint32 socket); + + /*! \brief Cleanups resources and stops performance counting + + One needs to call this method when your program finishes or/and you are not going to use the + performance counting routines anymore. +*/ + void cleanup(); + +/*! \brief Forces PMU reset + + If there is no chance to free up PMU from other applications you might try to call this method at your own risk. +*/ + void resetPMU(); + + /*! \brief Reads all counter states (including system, sockets and cores) + + \param systemState system counter state (return parameter) + \param socketStates socket counter states (return parameter) + \param coreStates core counter states (return parameter) + + */ + void getAllCounterStates(SystemCounterState & systemState, std::vector & socketStates, std::vector & coreStates); + + /*! \brief Return true if the core in online + + \param i OS core id + */ + bool isCoreOnline(int32 os_core_id) const; + + /*! \brief Reads the counter state of the system + + System consists of several sockets (CPUs). + Socket has a CPU in it. Socket (CPU) consists of several (logical) cores. + + \return State of counters in the entire system + */ + SystemCounterState getSystemCounterState(); + + /*! \brief Reads the counter state of a socket + \param socket socket id + \return State of counters in the socket + */ + SocketCounterState getSocketCounterState(uint32 socket); + + /*! \brief Reads the counter state of a (logical) core + + Be aware that during the measurement other threads may be scheduled on the same core by the operating system (this is called context-switching). The performance events caused by these threads will be counted as well. + + + \param core core id + \return State of counters in the core + */ + CoreCounterState getCoreCounterState(uint32 core); + + /*! \brief Reads number of logical cores in the system + \return Number of logical cores in the system + */ + uint32 getNumCores(); + + /*! \brief Reads number of online logical cores in the system + \return Number of online logical cores in the system + */ + uint32 getNumOnlineCores(); + + /*! \brief Reads number of sockets (CPUs) in the system + \return Number of sockets in the system + */ + uint32 getNumSockets(); + + /*! \brief Reads how many hardware threads has a physical core + "Hardware thread" is a logical core in a different terminology. + If Intel(r) Hyperthreading(tm) is enabled then this function returns 2. + \return Number of hardware threads per physical core + */ + uint32 getThreadsPerCore(); + + /*! \brief Checks if SMT (HyperThreading) is enabled. + \return true iff SMT (HyperThreading) is enabled. + */ + bool getSMT(); // returns true iff SMT ("Hyperthreading") is on + + /*! \brief Reads the nominal core frequency + \return Nominal frequency in Hz + */ + uint64 getNominalFrequency(); // in Hz + + /*! \brief runs CPUID.0xF.0x01 to get the L3 up scaling factor to calculate L3 Occupancy + * Scaling factor is returned in EBX register after running the CPU instruction + * \return L3 up scaling factor + */ + uint32 getL3ScalingFactor(); + + //! \brief Identifiers of supported CPU models + enum SupportedCPUModels + { + NEHALEM_EP = 26, + NEHALEM = 30, + ATOM = 28, + ATOM_2 = 53, + ATOM_CENTERTON = 54, + ATOM_BAYTRAIL = 55, + ATOM_AVOTON = 77, + CLARKDALE = 37, + WESTMERE_EP = 44, + NEHALEM_EX = 46, + WESTMERE_EX = 47, + SANDY_BRIDGE = 42, + JAKETOWN = 45, + IVY_BRIDGE = 58, + HASWELL = 60, + HASWELL_ULT = 69, + HASWELL_2 = 70, + IVYTOWN = 62, + HASWELLX = 63, + BROADWELL = 61, + END_OF_MODEL_LIST = 0x0ffff + }; + + //! \brief Reads CPU model id + //! \return CPU model ID + uint32 getCPUModel() { return cpu_model; } + + //! \brief Reads original CPU model id + //! \return CPU model ID + uint32 getOriginalCPUModel() { return original_cpu_model; } + + //! \brief Determines socket of given core + //! \param core_id core identifier + //! \return socket identifier + int32 getSocketId(uint32 core_id) + { + return topology[core_id].socket; + } + + //! \brief Returns the number of Intel(r) Quick Path Interconnect(tm) links per socket + //! \return number of QPI links per socket + uint64 getQPILinksPerSocket() const + { + switch (cpu_model) + { + case NEHALEM_EP: + case WESTMERE_EP: + case CLARKDALE: + if (num_sockets == 2) + return 2; + else + return 1; + case NEHALEM_EX: + case WESTMERE_EX: + return 4; + case JAKETOWN: + case IVYTOWN: + case HASWELLX: + return (server_pcicfg_uncore && server_pcicfg_uncore[0])?(server_pcicfg_uncore[0]->getNumQPIPorts()):0; + } + return 0; + } + + //! \brief Returns the number of detected integrated memory controllers per socket + uint32 getMCPerSocket() const + { + switch (cpu_model) + { + case NEHALEM_EP: + case WESTMERE_EP: + case CLARKDALE: + return 1; + case NEHALEM_EX: + case WESTMERE_EX: + return 2; + case JAKETOWN: + case IVYTOWN: + case HASWELLX: + return (server_pcicfg_uncore && server_pcicfg_uncore[0])?(server_pcicfg_uncore[0]->getNumMC()):0; + } + return 0; + } + + //! \brief Returns the total number of detected memory channels on all integrated memory controllers per socket + uint32 getMCChannelsPerSocket() const + { + switch (cpu_model) + { + case NEHALEM_EP: + case WESTMERE_EP: + case CLARKDALE: + return 3; + case NEHALEM_EX: + case WESTMERE_EX: + return 4; + case JAKETOWN: + case IVYTOWN: + case HASWELLX: + return (server_pcicfg_uncore && server_pcicfg_uncore[0])?(server_pcicfg_uncore[0]->getNumMCChannels()):0; + } + return 0; + } + + //! \brief Returns the max number of instructions per cycle + //! \return max number of instructions per cycle + uint32 getMaxIPC() const + { + switch (cpu_model) + { + case NEHALEM_EP: + case WESTMERE_EP: + case NEHALEM_EX: + case WESTMERE_EX: + case CLARKDALE: + case SANDY_BRIDGE: + case JAKETOWN: + case IVYTOWN: + case IVY_BRIDGE: + case HASWELL: + case HASWELLX: + case BROADWELL: + return 4; + case ATOM: + return 2; + } + return 0; + } + + //! \brief Returns the frequency of Power Control Unit + uint64 getPCUFrequency() const + { + switch (cpu_model) + { + case JAKETOWN: + case IVYTOWN: + return 800000000ULL; // 800 MHz + case HASWELLX: + return 1000000000ULL; // 1 GHz + } + return 0; + } + + //! \brief Return TSC timer value in time units + //! \param multiplier use 1 for seconds, 1000 for ms, 1000000 for mks, etc (default is 1000: ms) + //! \param core core to read on-chip TSC value (default is 0) + //! \return time counter value + uint64 getTickCount(uint64 multiplier = 1000 /* ms */, uint32 core = 0); + + //! \brief Return TSC timer value in time units using rdtscp instruction from current core + //! \param multiplier use 1 for seconds, 1000 for ms, 1000000 for mks, etc (default is 1000: ms) + //! \warning Processor support is required bit 27 of cpuid EDX must be set, for Windows, Visual Studio 2010 is required + //! \return time counter value + uint64 getTickCountRDTSCP(uint64 multiplier = 1000 /* ms */); + + + //! \brief Return QPI Link Speed in GBytes/second + //! \warning Works only for Nehalem-EX (Xeon 7500) and Xeon E7 and E5 processors + //! \return QPI Link Speed in GBytes/second + uint64 getQPILinkSpeed(uint32 socketNr, uint32 linkNr) const + { return hasPCICFGUncore() ? server_pcicfg_uncore[socketNr]->getQPILinkSpeed(linkNr) : max_qpi_speed; } + + //! \brief Returns how many joules are in an internal processor energy unit + double getJoulesPerEnergyUnit() const { return joulesPerEnergyUnit; } + + //! \brief Returns thermal specification power of the package domain in Watt + int32 getPackageThermalSpecPower() const { return pkgThermalSpecPower; } + + //! \brief Returns minimum power derived from electrical spec of the package domain in Watt + int32 getPackageMinimumPower() const { return pkgMinimumPower; } + + //! \brief Returns maximum power derived from electrical spec of the package domain in Watt + int32 getPackageMaximumPower() const { return pkgMaximumPower; } + + //! \brief Loads and initializes Winring0 third party library for access to processor model specific and PCI configuration registers + //! \return returns true in case of success + static bool initWinRing0Lib(); + + inline void disableJKTWorkaround() { disable_JKT_workaround = true; } + + enum PCIeEventCode + { + // PCIe read events (PCI devices reading from memory - application writes to disk/network/PCIe device) + PCIeRdCur = 0x19E, // PCIe read current (full cache line) + PCIeNSRd = 0x1E4, // PCIe non-snoop read (full cache line) + // PCIe write events (PCI devices writing to memory - application reads from disk/network/PCIe device) + PCIeWiLF = 0x194, // PCIe Write (non-allocating) (full cache line) + PCIeItoM = 0x19C, // PCIe Write (allocating) (full cache line) + PCIeNSWr = 0x1E5, // PCIe Non-snoop write (partial cache line) + PCIeNSWrF = 0x1E6, // PCIe Non-snoop write (full cache line) + // events shared by CPU and IO + RFO = 0x180, // Demand Data RFO; share the same code for CPU, use tid to filter PCIe only traffic + CRd = 0x181, // Demand Code Read + DRd = 0x182, // Demand Data Read + PRd = 0x187, // Partial Reads (UC) (MMIO Read) + WiL = 0x18F, // Write Invalidate Line - partial (MMIO write), PL: Not documented in HSX/IVT + ItoM = 0x1C8, // Request Invalidate Line; share the same code for CPU, use tid to filter PCIe only traffic + }; + + enum CBoEventTid + { + RFOtid = 0x3E, + ItoMtid = 0x3E, + }; + + //! \brief Program uncore PCIe monitoring event(s) + //! \param event_ a PCIe event to monitor + //! \param tid_ tid filter (PCM supports it only on Haswell server) + void programPCIeCounters(const PCIeEventCode event_, const uint32 tid_ = 0, const uint32 miss_ = 0); + void programPCIeMissCounters(const PCIeEventCode event_, const uint32 tid_ = 0); + + //! \brief Get the state of PCIe counter(s) + //! \param socket_ socket of the PCIe controller + //! \return State of PCIe counter(s) + PCIeCounterState getPCIeCounterState(const uint32 socket_); + + uint64 extractCoreGenCounterValue(uint64 val); + uint64 extractCoreFixedCounterValue(uint64 val); + uint64 extractUncoreGenCounterValue(uint64 val); + uint64 extractUncoreFixedCounterValue(uint64 val); + uint64 extractL3CacheOccupancy(uint64 val); + + //! \brief Get a string describing the codename of the processor microarchitecture + //! \param cpu_model_ cpu model (if no parameter provided the codename of the detected CPU is returned) + const char * getUArchCodename(int32 cpu_model_ = -1) const; + + //! \brief Get Brand string of processor + static std::string getCPUBrandString(); + + bool packageEnergyMetricsAvailable() const + { + return ( + cpu_model == PCM::JAKETOWN + || cpu_model == PCM::IVYTOWN + || cpu_model == PCM::SANDY_BRIDGE + || cpu_model == PCM::IVY_BRIDGE + || cpu_model == PCM::HASWELL + || original_cpu_model == PCM::ATOM_AVOTON + || cpu_model == PCM::HASWELLX + || cpu_model == PCM::BROADWELL + ); + } + + bool dramEnergyMetricsAvailable() const + { + return ( + cpu_model == PCM::JAKETOWN + || cpu_model == PCM::IVYTOWN + || cpu_model == PCM::HASWELLX + ); + } + + bool packageThermalMetricsAvailable() const + { + return packageEnergyMetricsAvailable(); + } + + bool outgoingQPITrafficMetricsAvailable() const + { + return ( + cpu_model == PCM::NEHALEM_EX + || cpu_model == PCM::WESTMERE_EX + || cpu_model == PCM::JAKETOWN + || cpu_model == PCM::IVYTOWN + || cpu_model == PCM::HASWELLX + ); + } + + bool qpiUtilizationMetricsAvailable() const + { + return outgoingQPITrafficMetricsAvailable(); + } + + bool memoryTrafficMetricsAvailable() const + { + return !( + cpu_model == PCM::ATOM + || cpu_model == PCM::CLARKDALE + ); + } + + bool memoryIOTrafficMetricAvailable() const + { + return ( + cpu_model == PCM::SANDY_BRIDGE + || cpu_model == PCM::IVY_BRIDGE + || cpu_model == PCM::HASWELL + || cpu_model == PCM::BROADWELL + ); + } + + bool hasBecktonUncore() const + { + return ( + cpu_model == PCM::NEHALEM_EX + || cpu_model == PCM::WESTMERE_EX + ); + } + bool hasPCICFGUncore() const // has PCICFG uncore PMON + { + return ( + cpu_model == PCM::JAKETOWN + || cpu_model == PCM::IVYTOWN + || cpu_model == PCM::HASWELLX + ); + } + + ~PCM(); +}; + +//! \brief Basic core counter state +//! +//! Intended only for derivation, but not for the direct use +class BasicCounterState +{ + friend class PCM; + template + friend double getExecUsage(const CounterStateType & before, const CounterStateType & after); + template + friend double getIPC(const CounterStateType & before, const CounterStateType & after); + template + friend double getAverageFrequency(const CounterStateType & before, const CounterStateType & after); + template + friend double getActiveAverageFrequency(const CounterStateType & before, const CounterStateType & after); + template + friend double getCyclesLostDueL3CacheMisses(const CounterStateType & before, const CounterStateType & after); + template + friend double getCyclesLostDueL2CacheMisses(const CounterStateType & before, const CounterStateType & after); + template + friend double getRelativeFrequency(const CounterStateType & before, const CounterStateType & after); + template + friend double getActiveRelativeFrequency(const CounterStateType & before, const CounterStateType & after); + template + friend double getL2CacheHitRatio(const CounterStateType & before, const CounterStateType & after); + template + friend double getL3CacheHitRatio(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getL3CacheMisses(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getL2CacheMisses(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getL2CacheHits(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getL3CacheOccupancy(const CounterStateType & now); + template + friend uint64 getCycles(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getInstructionsRetired(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getCycles(const CounterStateType & now); + template + friend uint64 getInstructionsRetired(const CounterStateType & now); + template + friend uint64 getL3CacheHitsNoSnoop(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getL3CacheHitsSnoop(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getL3CacheHits(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getNumberOfCustomEvents(int32 eventCounterNr, const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getInvariantTSC(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getRefCycles(const CounterStateType & before, const CounterStateType & after); + template + friend double getCoreCStateResidency(int state, const CounterStateType & before, const CounterStateType & after); +protected: + uint64 InstRetiredAny; + uint64 CpuClkUnhaltedThread; + uint64 CpuClkUnhaltedRef; + // dont put any additional fields between Event 0-Event 3 because getNumberOfCustomEvents assumes there are none + union { + uint64 L3Miss; + uint64 Event0; + uint64 ArchLLCMiss; + }; + union { + uint64 L3UnsharedHit; + uint64 Event1; + uint64 ArchLLCRef; + }; + union { + uint64 L2HitM; + uint64 Event2; + }; + union { + uint64 L2Hit; + uint64 Event3; + }; + uint64 InvariantTSC; // invariant time stamp counter + uint64 CStateResidency[PCM::MAX_C_STATE + 1]; + int32 ThermalHeadroom; + uint64 L3Occupancy; + void readAndAggregate(SafeMsrHandle *); +public: + BasicCounterState() : + InstRetiredAny(0) + , CpuClkUnhaltedThread(0) + , CpuClkUnhaltedRef(0) + , L3Miss(0) + , L3UnsharedHit(0) + , L2HitM(0) + , L2Hit(0) + , InvariantTSC(0) + , ThermalHeadroom(PCM_INVALID_THERMAL_HEADROOM) + , L3Occupancy(0) + { + memset(&(CStateResidency[0]), 0, sizeof(CStateResidency)); + } + virtual ~BasicCounterState() { } + + BasicCounterState & operator += (const BasicCounterState & o) + { + InstRetiredAny += o.InstRetiredAny; + CpuClkUnhaltedThread += o.CpuClkUnhaltedThread; + CpuClkUnhaltedRef += o.CpuClkUnhaltedRef; + Event0 += o.Event0; + Event1 += o.Event1; + Event2 += o.Event2; + Event3 += o.Event3; + InvariantTSC += o.InvariantTSC; + for(int i=0; i <= PCM::MAX_C_STATE ;++i) + CStateResidency[i] += o.CStateResidency[i]; + // ThermalHeadroom is not accumulative + L3Occupancy += o.L3Occupancy; + return *this; + } + + //! Returns current thermal headroom below TjMax + int32 getThermalHeadroom() const { return ThermalHeadroom; } +}; + +/*! \brief Returns QPI LL clock ticks + \param port QPI port number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getQPIClocks(uint32 port, const CounterStateType & before, const CounterStateType & after) +{ + return after.QPIClocks[port] - before.QPIClocks[port]; +} + + +template +int32 getThermalHeadroom(const CounterStateType & /* before */, const CounterStateType & after) +{ + return after.getThermalHeadroom(); +} + +/*! \brief Returns the number of QPI cycles in power saving half-lane mode + \param port QPI port number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getQPIL0pTxCycles(uint32 port, const CounterStateType & before, const CounterStateType & after) +{ + return after.QPIL0pTxCycles[port] - before.QPIL0pTxCycles[port]; +} + +/*! \brief Returns the number of QPI cycles in power saving shutdown mode + \param port QPI port number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getQPIL1Cycles(uint32 port, const CounterStateType & before, const CounterStateType & after) +{ + return after.QPIL1Cycles[port] - before.QPIL1Cycles[port]; +} + +/*! \brief Returns the ratio of QPI cycles in power saving half-lane mode + \param port QPI port number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return 0..1 - ratio of QPI cycles in power saving half-lane mode +*/ +template +double getNormalizedQPIL0pTxCycles(uint32 port, const CounterStateType & before, const CounterStateType & after) +{ + return double(getQPIL0pTxCycles(port,before,after))/double(getQPIClocks(port,before,after)); +} + +/*! \brief Returns the ratio of QPI cycles in power saving shutdown mode + \param port QPI port number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return 0..1 - ratio of QPI cycles in power saving shutdown mode +*/ +template +double getNormalizedQPIL1Cycles(uint32 port, const CounterStateType & before, const CounterStateType & after) +{ + return double(getQPIL1Cycles(port,before,after))/double(getQPIClocks(port,before,after)); +} + +/*! \brief Returns DRAM clock ticks + \param channel DRAM channel number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getDRAMClocks(uint32 channel, const CounterStateType & before, const CounterStateType & after) +{ + return after.DRAMClocks[channel] - before.DRAMClocks[channel]; +} + +/*! \brief Direct read of memory controller PMU counter (counter meaning depends on the programming: power/performance/etc) + \param counter counter number + \param channel channel number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getMCCounter(uint32 channel, uint32 counter, const CounterStateType & before, const CounterStateType & after) +{ + return after.MCCounter[channel][counter] - before.MCCounter[channel][counter]; +} + +/*! \brief Direct read of power control unit PMU counter (counter meaning depends on the programming: power/performance/etc) + \param counter counter number + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getPCUCounter(uint32 counter, const CounterStateType & before, const CounterStateType & after) +{ + return after.PCUCounter[counter] - before.PCUCounter[counter]; +} + +/*! \brief Returns clock ticks of power control unit + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getPCUClocks(const CounterStateType & before, const CounterStateType & after) +{ + return getPCUCounter(0,before,after); +} + +/*! \brief Returns energy consumed by processor, exclusing DRAM (measured in internal units) + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getConsumedEnergy(const CounterStateType & before, const CounterStateType & after) +{ + return after.PackageEnergyStatus - before.PackageEnergyStatus; +} + +/*! \brief Returns energy consumed by DRAM (measured in internal units) + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +uint64 getDRAMConsumedEnergy(const CounterStateType & before, const CounterStateType & after) +{ + return after.DRAMEnergyStatus - before.DRAMEnergyStatus; +} + +/*! \brief Returns Joules consumed by processor (excluding DRAM) + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +double getConsumedJoules(const CounterStateType & before, const CounterStateType & after) +{ + PCM * m = PCM::getInstance(); + if(!m) return -1.; + + return double(getConsumedEnergy(before,after))*m->getJoulesPerEnergyUnit(); +} + +/*! \brief Returns Joules consumed by DRAM + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment +*/ +template +double getDRAMConsumedJoules(const CounterStateType & before, const CounterStateType & after) +{ + PCM * m = PCM::getInstance(); + if(!m) return -1.; + double dram_joules_per_energy_unit; + + if(PCM::HASWELLX == m->getCPUModel()) { +/* as described in sections 5.3.2 (DRAM_POWER_INFO) and 5.3.3 (DRAM_ENERGY_STATUS) of + * Volume 2 (Registers) of + * Intel Xeon E5-1600 v3 and Intel Xeon E5-2600 v3 (Haswell-EP) Datasheet (Ref 330784-001, Sept.2014) + * ENERGY_UNIT for DRAM domain is fixed to 15.3 uJ for server Haswell processors. + */ + dram_joules_per_energy_unit=0.0000153; + } else { +/* for all other processors (including Haswell client/mobile SKUs) the ENERGY_UNIT for DRAM domain + * should be read from PACKAGE_POWER_SKU register (usually value around ~61uJ) + */ + dram_joules_per_energy_unit=m->getJoulesPerEnergyUnit(); + } + return double(getDRAMConsumedEnergy(before,after))*dram_joules_per_energy_unit; +} + + +//! \brief Basic uncore counter state +//! +//! Intended only for derivation, but not for the direct use +class UncoreCounterState +{ + friend class PCM; + template + friend uint64 getBytesReadFromMC(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getBytesWrittenToMC(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getIORequestBytesFromMC(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getConsumedEnergy(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getDRAMConsumedEnergy(const CounterStateType & before, const CounterStateType & after); + template + friend double getPackageCStateResidency(int state, const CounterStateType & before, const CounterStateType & after); +protected: + uint64 UncMCFullWrites; + uint64 UncMCNormalReads; + uint64 UncMCIORequests; + uint64 PackageEnergyStatus; + uint64 DRAMEnergyStatus; + uint64 CStateResidency[PCM::MAX_C_STATE + 1]; + void readAndAggregate(SafeMsrHandle *); +public: + UncoreCounterState() : + UncMCFullWrites(0) + , UncMCNormalReads(0) + , UncMCIORequests(0) + , PackageEnergyStatus(0) + , DRAMEnergyStatus(0) + { + memset(&(CStateResidency[0]), 0, sizeof(CStateResidency)); + } + virtual ~UncoreCounterState() { } + + UncoreCounterState & operator += (const UncoreCounterState & o) + { + UncMCFullWrites += o.UncMCFullWrites; + UncMCNormalReads += o.UncMCNormalReads; + UncMCIORequests += o.UncMCIORequests; + PackageEnergyStatus += o.PackageEnergyStatus; + DRAMEnergyStatus += o.DRAMEnergyStatus; + for(int i=0; i <= PCM::MAX_C_STATE ;++i) + CStateResidency[i] += o.CStateResidency[i]; + return *this; + } +}; + + +//! \brief Server uncore power counter state +//! +class ServerUncorePowerState: public UncoreCounterState +{ + uint64 QPIClocks[3], QPIL0pTxCycles[3], QPIL1Cycles[3]; + uint64 DRAMClocks[8]; + uint64 MCCounter[8][4];// channel X counter + uint64 PCUCounter[4]; + int32 PackageThermalHeadroom; + uint64 InvariantTSC; // invariant time stamp counter + friend class PCM; + template + friend uint64 getQPIClocks(uint32 port, const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getQPIL0pTxCycles(uint32 port, const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getQPIL1Cycles(uint32 port, const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getDRAMClocks(uint32 channel, const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getMCCounter(uint32 channel, uint32 counter, const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getPCUCounter(uint32 counter, const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getConsumedEnergy(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getDRAMConsumedEnergy(const CounterStateType & before, const CounterStateType & after); + template + friend uint64 getInvariantTSC(const CounterStateType & before, const CounterStateType & after); +public: + //! Returns current thermal headroom below TjMax + int32 getPackageThermalHeadroom() const { return PackageThermalHeadroom; } + ServerUncorePowerState() : + PackageThermalHeadroom(0) + , InvariantTSC(0) + { + memset(&(QPIClocks[0]), 0, 3*sizeof(uint64)); + memset(&(QPIL0pTxCycles[0]), 0, 3*sizeof(uint64)); + memset(&(QPIL1Cycles[0]), 0, 3*sizeof(uint64)); + memset(&(DRAMClocks[0]), 0, 8*sizeof(uint64)); + memset(&(PCUCounter[0]), 0, 4*sizeof(uint64)); + for(int i=0;i<8;++i) + memset(&(MCCounter[i][0]), 0, 4*sizeof(uint64)); + } +}; + +//! \brief (Logical) core-wide counter state +class CoreCounterState : public BasicCounterState +{ + friend class PCM; + +public: +}; + +//! \brief Socket-wide counter state +class SocketCounterState : public BasicCounterState, public UncoreCounterState +{ + friend class PCM; + +protected: + void readAndAggregate(SafeMsrHandle * handle) + { + BasicCounterState::readAndAggregate(handle); + UncoreCounterState::readAndAggregate(handle); + } + +public: + void accumulateCoreState(const CoreCounterState & o) + { + BasicCounterState::operator += (o); + } +}; + +//! \brief System-wide counter state +class SystemCounterState : public BasicCounterState, public UncoreCounterState +{ + friend class PCM; + std::vector > incomingQPIPackets; + std::vector > outgoingQPIIdleFlits; + std::vector > outgoingQPIDataNonDataFlits; + uint64 uncoreTSC; + +protected: + void readAndAggregate(SafeMsrHandle * handle) + { + BasicCounterState::readAndAggregate(handle); + UncoreCounterState::readAndAggregate(handle); + } + +public: + friend uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after); + friend uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & now); + friend double getOutgoingQPILinkUtilization(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after); + friend uint64 getOutgoingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after); + friend uint64 getOutgoingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & now); + SystemCounterState() : + uncoreTSC(0) + { + PCM * m = PCM::getInstance(); + incomingQPIPackets.resize(m->getNumSockets(), + std::vector((uint32)m->getQPILinksPerSocket(), 0)); + outgoingQPIIdleFlits.resize(m->getNumSockets(), + std::vector((uint32)m->getQPILinksPerSocket(), 0)); + outgoingQPIDataNonDataFlits.resize(m->getNumSockets(), + std::vector((uint32)m->getQPILinksPerSocket(), 0)); + } + + void accumulateSocketState(const SocketCounterState & o) + { + if (&o != NULL) // security check requirement + { + BasicCounterState::operator += (o); + UncoreCounterState::operator += (o); + } + } +}; + +/*! \brief Reads the counter state of the system + + Helper function. Uses PCM object to access counters. + + System consists of several sockets (CPUs). + Socket has a CPU in it. Socket (CPU) consists of several (logical) cores. + + \return State of counters in the entire system +*/ +INTELPCM_API SystemCounterState getSystemCounterState(); + +/*! \brief Reads the counter state of a socket + + Helper function. Uses PCM object to access counters. + + \param socket socket id + \return State of counters in the socket +*/ +INTELPCM_API SocketCounterState getSocketCounterState(uint32 socket); + +/*! \brief Reads the counter state of a (logical) core + + Helper function. Uses PCM object to access counters. + + \param core core id + \return State of counters in the core +*/ +INTELPCM_API CoreCounterState getCoreCounterState(uint32 core); + + +/*! \brief Computes average number of retired instructions per core cycle (IPC) + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return IPC +*/ +template +double getIPC(const CounterStateType & before, const CounterStateType & after) // instructions per cycle +{ + int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; + if (clocks != 0) + return double(after.InstRetiredAny - before.InstRetiredAny) / double(clocks); + return -1; +} + + +/*! \brief Computes the number of retired instructions + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return number of retired instructions +*/ +template +uint64 getInstructionsRetired(const CounterStateType & before, const CounterStateType & after) // instructions +{ + return after.InstRetiredAny - before.InstRetiredAny; +} + +/*! \brief Computes average number of retired instructions per time intervall + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return usage +*/ +template +double getExecUsage(const CounterStateType & before, const CounterStateType & after) // usage +{ + int64 timer_clocks = after.InvariantTSC - before.InvariantTSC; + if (timer_clocks != 0) + return double(after.InstRetiredAny - before.InstRetiredAny) / double(timer_clocks); + return -1; +} + +/*! \brief Computes the number of retired instructions + + \param now Current CPU counter state + \return number of retired instructions +*/ +template +uint64 getInstructionsRetired(const CounterStateType & now) // instructions +{ + return now.InstRetiredAny; +} + +/*! \brief Computes the number core clock cycles when signal on a specific core is running (not halted) + + Returns number of used cycles (halted cyles are not counted). + The counter does not advance in the following conditions: + - an ACPI C-state is other than C0 for normal operation + - HLT + - STPCLK+ pin is asserted + - being throttled by TM1 + - during the frequency switching phase of a performance state transition + + The performance counter for this event counts across performance state + transitions using different core clock frequencies + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return number core clock cycles +*/ +template +uint64 getCycles(const CounterStateType & before, const CounterStateType & after) // clocks +{ + return after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; +} + +/*! \brief Computes the number of reference clock cycles while clock signal on the core is running + + The reference clock operates at a fixed frequency, irrespective of core + frequency changes due to performance state transitions. See Intel(r) Software + Developer's Manual for more details + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return number core clock cycles +*/ +template +uint64 getRefCycles(const CounterStateType & before, const CounterStateType & after) // clocks +{ + return after.CpuClkUnhaltedRef - before.CpuClkUnhaltedRef; +} + +/*! \brief Computes the number executed core clock cycles + + Returns number of used cycles (halted cyles are not counted). + + \param now Current CPU counter state + \return number core clock cycles +*/ +template +uint64 getCycles(const CounterStateType & now) // clocks +{ + return now.CpuClkUnhaltedThread; +} + +/*! \brief Computes average number of retired instructions per core cycle for the entire system combining instruction counts from logical cores to corresponding physical cores + + Use this metric to evaluate IPC improvement between SMT(Hyperthreading) on and SMT off. + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return IPC +*/ +inline double getCoreIPC(const SystemCounterState & before, const SystemCounterState & after) // instructions per cycle +{ + double ipc = getIPC(before, after); + PCM * m = PCM::getInstance(); + if (ipc >= 0. && m && (m->getNumCores() == m->getNumOnlineCores())) + return ipc * double(m->getThreadsPerCore()); + return -1; +} + + + + +/*! \brief Computes average number of retired instructions per time intervall for the entire system combining instruction counts from logical cores to corresponding physical cores + + Use this metric to evaluate cores utilization improvement between SMT(Hyperthreading) on and SMT off. + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return usage +*/ +inline double getTotalExecUsage(const SystemCounterState & before, const SystemCounterState & after) // usage +{ + double usage = getExecUsage(before, after); + PCM * m = PCM::getInstance(); + if (usage >= 0. && m && (m->getNumCores() == m->getNumOnlineCores())) + return usage * double(m->getThreadsPerCore()); + return -1; +} + +/*! \brief Computes average core frequency also taking Intel Turbo Boost technology into account + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return frequency in Hz +*/ +template +double getAverageFrequency(const CounterStateType & before, const CounterStateType & after) // in Hz +{ + int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; + int64 timer_clocks = after.InvariantTSC - before.InvariantTSC; + PCM * m = PCM::getInstance(); + if (timer_clocks != 0 && m) + return double(m->getNominalFrequency()) * double(clocks) / double(timer_clocks); + return -1; +} + +/*! \brief Computes average core frequency when not in powersaving C0-state (also taking Intel Turbo Boost technology into account) + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return frequency in Hz +*/ +template +double getActiveAverageFrequency(const CounterStateType & before, const CounterStateType & after) // in Hz +{ + int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; + int64 ref_clocks = after.CpuClkUnhaltedRef - before.CpuClkUnhaltedRef; + PCM * m = PCM::getInstance(); + if (ref_clocks != 0 && m) + return double(m->getNominalFrequency()) * double(clocks) / double(ref_clocks); + return -1; +} + +/*! \brief Computes average core frequency also taking Intel Turbo Boost technology into account + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return Fraction of nominal frequency +*/ +template +double getRelativeFrequency(const CounterStateType & before, const CounterStateType & after) // fraction of nominal frequency +{ + int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; + int64 timer_clocks = after.InvariantTSC - before.InvariantTSC; + if (timer_clocks != 0) + return double(clocks) / double(timer_clocks); + return -1; +} + +/*! \brief Computes average core frequency when not in powersaving C0-state (also taking Intel Turbo Boost technology into account) + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return Fraction of nominal frequency (if >1.0 then Turbo was working during the measurement) +*/ +template +double getActiveRelativeFrequency(const CounterStateType & before, const CounterStateType & after) // fraction of nominal frequency +{ + int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; + int64 ref_clocks = after.CpuClkUnhaltedRef - before.CpuClkUnhaltedRef; + if (ref_clocks != 0) + return double(clocks) / double(ref_clocks); + return -1; +} + +/*! \brief Estimates how many core cycles were potentially lost due to L3 cache misses. + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return ratio that is usually beetween 0 and 1 ; in some cases could be >1.0 due to a lower memory latency estimation +*/ +template +double getCyclesLostDueL3CacheMisses(const CounterStateType & before, const CounterStateType & after) // 0.0 - 1.0 +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) return -1; + int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; + if (clocks != 0) + { + return 180. * double(after.L3Miss - before.L3Miss) / double(clocks); + } + return -1; +} + +/*! \brief Estimates how many core cycles were potentially lost due to missing L2 cache but still hitting L3 cache + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \warning Currently not supported on Intel(R) Atom(tm) processor + \return ratio that is usually beetween 0 and 1 ; in some cases could be >1.0 due to a lower access latency estimation +*/ +template +double getCyclesLostDueL2CacheMisses(const CounterStateType & before, const CounterStateType & after) // 0.0 - 1.0 +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) return -1; + int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; + if (clocks != 0) + { + double L3UnsharedHit = (double)(after.L3UnsharedHit - before.L3UnsharedHit); + double L2HitM = (double)(after.L2HitM - before.L2HitM); + return (35. * L3UnsharedHit + 74. * L2HitM) / double(clocks); + } + return -1; +} + +/*! \brief Computes L2 cache hit ratio + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return value between 0 and 1 +*/ +template +double getL2CacheHitRatio(const CounterStateType & before, const CounterStateType & after) // 0.0 - 1.0 +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) + { + uint64 L2Miss = after.ArchLLCMiss - before.ArchLLCMiss; + uint64 L2Ref = after.ArchLLCRef - before.ArchLLCRef; + if (L2Ref) return 1. - (double(L2Miss) / double(L2Ref)); + return 1; + } + uint64 L3Miss = after.L3Miss - before.L3Miss; + uint64 L3UnsharedHit = after.L3UnsharedHit - before.L3UnsharedHit; + uint64 L2HitM = after.L2HitM - before.L2HitM; + uint64 L2Hit = after.L2Hit - before.L2Hit; + uint64 hits = L2Hit; + uint64 all = L2Hit + L2HitM + L3UnsharedHit + L3Miss; + if (all) return double(hits) / double(all); + + return 1; +} + +/*! \brief Computes L3 cache hit ratio + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return value between 0 and 1 +*/ +template +double getL3CacheHitRatio(const CounterStateType & before, const CounterStateType & after) // 0.0 - 1.0 +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) return -1; + + uint64 L3Miss = after.L3Miss - before.L3Miss; + uint64 L3UnsharedHit = after.L3UnsharedHit - before.L3UnsharedHit; + uint64 L2HitM = after.L2HitM - before.L2HitM; + uint64 hits = L3UnsharedHit + L2HitM; + uint64 all = L2HitM + L3UnsharedHit + L3Miss; + if (all) return double(hits) / double(all); + + return 1; +} + +/*! \brief Computes number of L3 cache misses + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return number of misses +*/ +template +uint64 getL3CacheMisses(const CounterStateType & before, const CounterStateType & after) +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) return 0; + return after.L3Miss - before.L3Miss; +} + +/*! \brief Computes number of L2 cache misses + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return number of misses +*/ +template +uint64 getL2CacheMisses(const CounterStateType & before, const CounterStateType & after) +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) + { + return after.ArchLLCMiss - before.ArchLLCMiss; + } + uint64 L3Miss = after.L3Miss - before.L3Miss; + uint64 L3UnsharedHit = after.L3UnsharedHit - before.L3UnsharedHit; + uint64 L2HitM = after.L2HitM - before.L2HitM; + return L2HitM + L3UnsharedHit + L3Miss; +} + +/*! \brief Computes number of L2 cache hits + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return number of hits +*/ +template +uint64 getL2CacheHits(const CounterStateType & before, const CounterStateType & after) +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) + { + uint64 L2Miss = after.ArchLLCMiss - before.ArchLLCMiss; + uint64 L2Ref = after.ArchLLCRef - before.ArchLLCRef; + return L2Ref - L2Miss; + } + return after.L2Hit - before.L2Hit; +} + +/*! \brief Computes L3 Cache Occupancy + +*/ +template +uint64 getL3CacheOccupancy(const CounterStateType & now) +{ + + return now.L3Occupancy ; + +} + +/*! \brief Computes number of L3 cache hits where no snooping in sibling L2 caches had to be done + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return number of hits +*/ +template +uint64 getL3CacheHitsNoSnoop(const CounterStateType & before, const CounterStateType & after) +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) return 0; + return after.L3UnsharedHit - before.L3UnsharedHit; +} + +/*! \brief Computes number of L3 cache hits where snooping in sibling L2 caches had to be done + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return number of hits +*/ +template +uint64 getL3CacheHitsSnoop(const CounterStateType & before, const CounterStateType & after) +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) return 0; + return after.L2HitM - before.L2HitM; +} + + +/*! \brief Computes total number of L3 cache hits + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) + \return number of hits +*/ +template +uint64 getL3CacheHits(const CounterStateType & before, const CounterStateType & after) +{ + if (PCM::getInstance()->getCPUModel() == PCM::ATOM) return 0; + return getL3CacheHitsSnoop(before, after) + getL3CacheHitsNoSnoop(before, after); +} + +/*! \brief Computes number of invariant time stamp counter ticks + + This counter counts irrespectively of C-, P- or T-states + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return number of time stamp counter ticks +*/ +template +uint64 getInvariantTSC(const CounterStateType & before, const CounterStateType & after) +{ + return after.InvariantTSC - before.InvariantTSC; +} + +/*! \brief Computes residency in the core C-state + + \param state C-state + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return residence ratio (0..1): 0 - 0%, 1.0 - 100% +*/ +template +inline double getCoreCStateResidency(int state, const CounterStateType & before, const CounterStateType & after) +{ + const double tsc = double(getInvariantTSC(before,after)); + + if(state == 0) return double(getRefCycles(before,after))/tsc; + + if(state == 1) + { + PCM * m = PCM::getInstance(); + double result = 1.0 - double(getRefCycles(before,after))/tsc; // 1.0 - cC0 + for(int i = 2; i <= PCM::MAX_C_STATE; ++i) + if(m->isCoreCStateResidencySupported(state)) + result -= (after.BasicCounterState::CStateResidency[i] - before.BasicCounterState::CStateResidency[i])/tsc; + + if(result < 0.) result = 0.; // fix counter dissynchronization + else if(result > 1.) result = 1.; // fix counter dissynchronization + + return result; + } + return (after.BasicCounterState::CStateResidency[state] - before.BasicCounterState::CStateResidency[state])/tsc; +} + +/*! \brief Computes residency in the package C-state + + \param state C-state + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return residence ratio (0..1): 0 - 0%, 1.0 - 100% +*/ +template +inline double getPackageCStateResidency(int state, const CounterStateType & before, const CounterStateType & after) +{ + return double(after.UncoreCounterState::CStateResidency[state] - before.UncoreCounterState::CStateResidency[state])/double(getInvariantTSC(before,after)); +} + + +/*! \brief Computes number of bytes read from DRAM memory controllers + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return Number of bytes +*/ +template +uint64 getBytesReadFromMC(const CounterStateType & before, const CounterStateType & after) +{ + return (after.UncMCNormalReads - before.UncMCNormalReads) * 64; +} + +/*! \brief Computes number of bytes written to DRAM memory controllers + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return Number of bytes +*/ +template +uint64 getBytesWrittenToMC(const CounterStateType & before, const CounterStateType & after) +{ + return (after.UncMCFullWrites - before.UncMCFullWrites) * 64; +} + +/*! \brief Computes number of bytes of read/write requests from all IO sources + + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return Number of bytes +*/ +template +uint64 getIORequestBytesFromMC(const CounterStateType & before, const CounterStateType & after) +{ + return (after.UncMCIORequests - before.UncMCIORequests) * 64; +} + +/*! \brief Returns the number of occured custom core events + + Read number of events programmed with the \c CUSTOM_CORE_EVENTS + + \param eventCounterNr Event/counter number (value from 0 to 3) + \param before CPU counter state before the experiment + \param after CPU counter state after the experiment + \return Number of bytes +*/ +template +uint64 getNumberOfCustomEvents(int32 eventCounterNr, const CounterStateType & before, const CounterStateType & after) +{ + return ((&after.Event0)[eventCounterNr] - (&before.Event0)[eventCounterNr]); +} + +/*! \brief Get estimation of QPI data traffic per incoming QPI link + + Returns an estimation of number of data bytes transferred to a socket over Intel(r) Quick Path Interconnect + + \param socketNr socket identifier + \param linkNr linkNr + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return Number of bytes +*/ +inline uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) +{ + uint64 b = before.incomingQPIPackets[socketNr][linkNr]; + uint64 a = after.incomingQPIPackets[socketNr][linkNr]; + // prevent overflows due to counter dissynchronisation + return (a > b) ? (64 * (a - b)) : 0; +} + +/*! \brief Get data utilization of incoming QPI link (0..1) + + Returns an estimation of utilization of QPI link by data traffic transferred to a socket over Intel(r) Quick Path Interconnect + + \param socketNr socket identifier + \param linkNr linkNr + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return utilization (0..1) +*/ +inline double getIncomingQPILinkUtilization(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) +{ + PCM * m = PCM::getInstance(); + if (!(m->qpiUtilizationMetricsAvailable())) return 0.; + + const double bytes = (double)getIncomingQPILinkBytes(socketNr, linkNr, before, after); + const uint64 max_speed = m->getQPILinkSpeed(socketNr, linkNr); + const double max_bytes = (double)(double(max_speed) * double(getInvariantTSC(before, after) / double(m->getNumCores())) / double(m->getNominalFrequency())); + return bytes / max_bytes; +} + +/*! \brief Get utilization of outgoing QPI link (0..1) + + Returns an estimation of utilization of QPI link by (data+nondata) traffic transferred from a socket over Intel(r) Quick Path Interconnect + + \param socketNr socket identifier + \param linkNr linkNr + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return utilization (0..1) +*/ +inline double getOutgoingQPILinkUtilization(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) +{ + PCM * m = PCM::getInstance(); + + if(m->hasBecktonUncore()) + { + const uint64 b = before.outgoingQPIIdleFlits[socketNr][linkNr]; + const uint64 a = after.outgoingQPIIdleFlits[socketNr][linkNr]; + // prevent overflows due to counter dissynchronisation + const double idle_flits = (double)((a > b) ? (a - b) : 0); + const uint64 bTSC = before.uncoreTSC; + const uint64 aTSC = after.uncoreTSC; + const double tsc = (double)((aTSC > bTSC) ? (aTSC - bTSC) : 0); + if(idle_flits > tsc) return 0.; // prevent oveflows due to potential counter dissynchronization + + return (1. - (idle_flits / tsc)); + } else if(m->hasPCICFGUncore()) + { + const uint64 b = before.outgoingQPIDataNonDataFlits[socketNr][linkNr]; + const uint64 a = after.outgoingQPIDataNonDataFlits[socketNr][linkNr]; + // prevent overflows due to counter dissynchronisation + const double flits = (double)((a > b) ? (a - b) : 0); + const double max_flits = ((double(getInvariantTSC(before, after))*double(m->getQPILinkSpeed(socketNr, linkNr))/(2.0*4.0))/double(m->getNominalFrequency()))/double(m->getNumCores()); + if(flits > max_flits) return 1.; // prevent oveflows due to potential counter dissynchronization + return (flits / max_flits); + } + + return 0; +} + +/*! \brief Get estimation of QPI (data+nondata) traffic per outgoing QPI link + + Returns an estimation of number of data bytes transferred from a socket over Intel(r) Quick Path Interconnect + + \param socketNr socket identifier + \param linkNr linkNr + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return Number of bytes +*/ +inline uint64 getOutgoingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) +{ + PCM * m = PCM::getInstance(); + if (!(m->outgoingQPITrafficMetricsAvailable())) return 0; + + const double util = getOutgoingQPILinkUtilization(socketNr, linkNr, before, after); + const double max_bytes = (double(m->getQPILinkSpeed(socketNr, linkNr)) * double(getInvariantTSC(before, after) / double(m->getNumCores())) / double(m->getNominalFrequency())); + + return (uint64)(max_bytes * util); +} + + +/*! \brief Get estimation of total QPI data traffic + + Returns an estimation of number of data bytes transferred to all sockets over all Intel(r) Quick Path Interconnect links + + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return Number of bytes +*/ +inline uint64 getAllIncomingQPILinkBytes(const SystemCounterState & before, const SystemCounterState & after) +{ + PCM * m = PCM::getInstance(); + const uint32 ns = m->getNumSockets(); + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + uint64 sum = 0; + + for (uint32 s = 0; s < ns; ++s) + for (uint32 q = 0; q < qpiLinks; ++q) + sum += getIncomingQPILinkBytes(s, q, before, after); + + return sum; +} + +/*! \brief Get estimation of total QPI data+nondata traffic + + Returns an estimation of number of data and non-data bytes transferred from all sockets over all Intel(r) Quick Path Interconnect links + + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return Number of bytes +*/ +inline uint64 getAllOutgoingQPILinkBytes(const SystemCounterState & before, const SystemCounterState & after) +{ + PCM * m = PCM::getInstance(); + const uint32 ns = m->getNumSockets(); + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + uint64 sum = 0; + + for (uint32 s = 0; s < ns; ++s) + for (uint32 q = 0; q < qpiLinks; ++q) + sum += getOutgoingQPILinkBytes(s, q, before, after); + + return sum; +} + + +/*! \brief Return current value of the counter of QPI data traffic per incoming QPI link + + Returns the number of incoming data bytes to a socket over Intel(r) Quick Path Interconnect + + \param socketNr socket identifier + \param linkNr linkNr + \param now Current System CPU counter state + \return Number of bytes +*/ +inline uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & now) +{ + return 64 * now.incomingQPIPackets[socketNr][linkNr]; +} + +/*! \brief Get estimation of total QPI data traffic for this socket + + Returns an estimation of number of bytes transferred to this sockets over all Intel(r) Quick Path Interconnect links on this socket + + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return Number of bytes +*/ +inline uint64 getSocketIncomingQPILinkBytes(uint32 socketNr, const SystemCounterState & now) +{ + PCM * m = PCM::getInstance(); + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + uint64 sum = 0; + + for (uint32 q = 0; q < qpiLinks; ++q) + sum += getIncomingQPILinkBytes(socketNr, q, now); + + return sum; +} + +/*! \brief Get estimation of Socket QPI data traffic + + Returns an estimation of number of data bytes transferred to all sockets over all Intel(r) Quick Path Interconnect links + + \param now System CPU counter state + \return Number of bytes +*/ +inline uint64 getAllIncomingQPILinkBytes(const SystemCounterState & now) +{ + PCM * m = PCM::getInstance(); + const uint32 ns = m->getNumSockets(); + uint64 sum = 0; + + for (uint32 s = 0; s < ns; ++s) + sum += getSocketIncomingQPILinkBytes(s, now); + return sum; +} + + +/*! \brief Get QPI data to Memory Controller traffic ratio + + Ideally for NUMA-optmized programs the ratio should be close to 0. + + \param before System CPU counter state before the experiment + \param after System CPU counter state after the experiment + \return Ratio +*/ + +inline double getQPItoMCTrafficRatio(const SystemCounterState & before, const SystemCounterState & after) +{ + const uint64 totalQPI = getAllIncomingQPILinkBytes(before, after); + const uint64 memTraffic = getBytesReadFromMC(before, after) + getBytesWrittenToMC(before, after); + return double(totalQPI) / double(memTraffic); +} + +//! \brief Returns the raw count of PCIe events +//! \param before PCIe counter state before the experiment +//! \param after PCIe counter state after the experiment +inline uint64 getNumberOfEvents(PCIeCounterState before, PCIeCounterState after) +{ + return after.data - before.data; +} + +#endif diff --git a/freegetopt/ChangeLog b/freegetopt/ChangeLog new file mode 100644 index 0000000..1801ec1 --- /dev/null +++ b/freegetopt/ChangeLog @@ -0,0 +1,35 @@ +2003-10-25 Mark K. Kim {getopt*cbreak.org} + + * *: freegetopt 0.11 released. + +2003-10-25 Mark K. Kim {getopt*cbreak.org} + + * ChangeLog: ChangeLog file added. + + * LICENSE: License file added. License is now officially BSD. + + * README: Updated to reflect the new license. CVS tracking data + added. + + * getopt.h, getopt.c: License text added to the beginning of + each file. CVS tracking data added. + + * test.c: License text added to the beginning of the file. + CVS tracking data added. + +2003-10-20 Mark K. Kim {getopt*cbreak.org} + + * *: Sourceforge project approval. Name changed to freegetopt. + License is now officially BSD. + +2003-09-29 Jon Higdon {jhigdon*nni.com} + + * test.c: GCC 3.x gives compound statement warning. Fixed. + +2003.01.12 Mark K. Kim {getopt*cbreak.org} + + * *: Getopt 1.0 released! Supports only short options, but + I think it's fully compatible with GNU getopt except for the + value of optind while getopt is still running. The value + of optind is same after getopt finishes running, however. + diff --git a/freegetopt/LICENSE b/freegetopt/LICENSE new file mode 100644 index 0000000..99efb42 --- /dev/null +++ b/freegetopt/LICENSE @@ -0,0 +1,33 @@ +Free Getopt +Copyright (c)2002-2003 Mark K. Kim +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the original author of this software nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + diff --git a/freegetopt/README b/freegetopt/README new file mode 100644 index 0000000..f8e9350 --- /dev/null +++ b/freegetopt/README @@ -0,0 +1,43 @@ +Free Getopt + +****************************************************************************** +Please read the file LICENSE for the terms of use and distribution of this +software. +****************************************************************************** + +"getopt" is a library that allows a parsing of arguments passed +to a program. This is a useful library used in many software. +There are many versions of the getopt library available, two +popular versions being the BSD getopt and the GNU getopt. + +BSD getopt is somewhat old, dated, and isn't very user-friendly. +The GNU getopt is great, except the user license doesn't let you +statically link the library to a proprietary software. This +is usually not a problem on modern operating systems that allow +dynamic links to libraries, but sometimes you just gotta link +the library statically for one reason or another. That's where +Free Getopt steps in. + +Functionally, this getopt library is equivalent to GNU's getopt +library (the short option version, not the long one) in almost +every aspect. The only exception is how the "optind" variable +increments. Apparently due to different algorithms used by my +program and the GNU getopt, the "optind" changes quite differently +between our two software. I personally find my algorithm to be +quite elegant; I couldn't tell you about the GNU version since +I never looked at its source. + +GNU's getopt_long support is in progress. + +This library was deliberately left in non-library (not in +*.lib, *.so, or *.a) form because it's most likely to be +statically-linked in various platforms, and linking it +directly from source is probably the most straight-forward +way to use the software in any platform. + +I hope you find this software useful. + +Mark K. Kim + +$Header: /cvsroot/freegetopt/freegetopt/README,v 1.2 2003/10/26 03:10:19 vindaci Exp $ + diff --git a/freegetopt/getopt.c b/freegetopt/getopt.c new file mode 100644 index 0000000..58a9047 --- /dev/null +++ b/freegetopt/getopt.c @@ -0,0 +1,252 @@ +/***************************************************************************** +* getopt.c - competent and free getopt library. +* $Header: /cvsroot/freegetopt/freegetopt/getopt.c,v 1.2 2003/10/26 03:10:20 vindaci Exp $ +* +* Copyright (c)2002-2003 Mark K. Kim +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* * Neither the original author of this software nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +* DAMAGE. +*/ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include "getopt.h" + + +//static const char* ID = "$Id: getopt.c,v 1.2 2003/10/26 03:10:20 vindaci Exp $"; + + +char* optarg = NULL; +int optind = 0; +int opterr = 1; +int optopt = '?'; + + +static char** prev_argv = NULL; /* Keep a copy of argv and argc to */ +static int prev_argc = 0; /* tell if getopt params change */ +static int argv_index = 0; /* Option we're checking */ +static int argv_index2 = 0; /* Option argument we're checking */ +static int opt_offset = 0; /* Index into compounded "-option" */ +static int dashdash = 0; /* True if "--" option reached */ +static int nonopt = 0; /* How many nonopts we've found */ + +static void increment_index() +{ + /* Move onto the next option */ + if(argv_index < argv_index2) + { + while(prev_argv[++argv_index] && prev_argv[argv_index][0] != '-' + && argv_index < argv_index2+1); + } + else argv_index++; + opt_offset = 1; +} + + +/* +* Permutes argv[] so that the argument currently being processed is moved +* to the end. +*/ +static int permute_argv_once() +{ + /* Movability check */ + if(argv_index + nonopt >= prev_argc) return 1; + /* Move the current option to the end, bring the others to front */ + else + { + char* tmp = prev_argv[argv_index]; + + /* Move the data */ + memmove(&prev_argv[argv_index], &prev_argv[argv_index+1], + sizeof(char**) * (prev_argc - argv_index - 1)); + prev_argv[prev_argc - 1] = tmp; + + nonopt++; + return 0; + } +} + + +int getopt(int argc, char** argv, char* optstr) +{ + int c = 0; + + /* If we have new argv, reinitialize */ + if(prev_argv != argv || prev_argc != argc) + { + /* Initialize variables */ + prev_argv = argv; + prev_argc = argc; + argv_index = 1; + argv_index2 = 1; + opt_offset = 1; + dashdash = 0; + nonopt = 0; + } + + /* Jump point in case we want to ignore the current argv_index */ + getopt_top: + + /* Misc. initializations */ + optarg = NULL; + + /* Dash-dash check */ + if(argv[argv_index] && !strcmp(argv[argv_index], "--")) + { + dashdash = 1; + increment_index(); + } + + /* If we're at the end of argv, that's it. */ + if(argv[argv_index] == NULL) + { + c = -1; + } + /* Are we looking at a string? Single dash is also a string */ + else if(dashdash || argv[argv_index][0] != '-' || !strcmp(argv[argv_index], "-")) + { + /* If we want a string... */ + if(optstr[0] == '-') + { + c = 1; + optarg = argv[argv_index]; + increment_index(); + } + /* If we really don't want it (we're in POSIX mode), we're done */ + else if(optstr[0] == '+' || getenv("POSIXLY_CORRECT")) + { + c = -1; + + /* Everything else is a non-opt argument */ + nonopt = argc - argv_index; + } + /* If we mildly don't want it, then move it back */ + else + { + if(!permute_argv_once()) goto getopt_top; + else c = -1; + } + } + /* Otherwise we're looking at an option */ + else + { + char* opt_ptr = NULL; + + /* Grab the option */ + c = argv[argv_index][opt_offset++]; + + /* Is the option in the optstr? */ + if(optstr[0] == '-') opt_ptr = strchr(optstr+1, c); + else opt_ptr = strchr(optstr, c); + /* Invalid argument */ + if(!opt_ptr) + { + if(opterr) + { + fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); + } + + optopt = c; + c = '?'; + + /* Move onto the next option */ + increment_index(); + } + /* Option takes argument */ + else if(opt_ptr[1] == ':') + { + /* ie, -oARGUMENT, -xxxoARGUMENT, etc. */ + if(argv[argv_index][opt_offset] != '\0') + { + optarg = &argv[argv_index][opt_offset]; + increment_index(); + } + /* ie, -o ARGUMENT (only if it's a required argument) */ + else if(opt_ptr[2] != ':') + { + /* One of those "you're not expected to understand this" moment */ + if(argv_index2 < argv_index) argv_index2 = argv_index; + while(argv[++argv_index2] && argv[argv_index2][0] == '-'); + optarg = argv[argv_index2]; + + /* Don't cross into the non-option argument list */ + if(argv_index2 + nonopt >= prev_argc) optarg = NULL; + + /* Move onto the next option */ + increment_index(); + } + else + { + /* Move onto the next option */ + increment_index(); + } + + /* In case we got no argument for an option with required argument */ + if(optarg == NULL && opt_ptr[2] != ':') + { + optopt = c; + c = '?'; + + if(opterr) + { + fprintf(stderr,"%s: option requires an argument -- %c\n", + argv[0], optopt); + } + } + } + /* Option does not take argument */ + else + { + /* Next argv_index */ + if(argv[argv_index][opt_offset] == '\0') + { + increment_index(); + } + } + } + + /* Calculate optind */ + if(c == -1) + { + optind = argc - nonopt; + } + else + { + optind = argv_index; + } + + return c; +} + + +/* vim:ts=3 +*/ diff --git a/freegetopt/getopt.h b/freegetopt/getopt.h new file mode 100644 index 0000000..0b78650 --- /dev/null +++ b/freegetopt/getopt.h @@ -0,0 +1,63 @@ +/***************************************************************************** +* getopt.h - competent and free getopt library. +* $Header: /cvsroot/freegetopt/freegetopt/getopt.h,v 1.2 2003/10/26 03:10:20 vindaci Exp $ +* +* Copyright (c)2002-2003 Mark K. Kim +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* * Neither the original author of this software nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +* DAMAGE. +*/ +#ifndef GETOPT_H_ +#define GETOPT_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern char* optarg; +extern int optind; +extern int opterr; +extern int optopt; + +int getopt(int argc, char** argv, char* optstr); + + +#ifdef __cplusplus +} +#endif + + +#endif /* GETOPT_H_ */ + + +/* vim:ts=3 +*/ diff --git a/gen_new_win_project.sh b/gen_new_win_project.sh new file mode 100644 index 0000000..6e1115d --- /dev/null +++ b/gen_new_win_project.sh @@ -0,0 +1,15 @@ + +DIRNAME=PCM-NUMA_Win +UTILNAME=pcm-numa + +rm -rf $DIRNAME + +mkdir $DIRNAME +cp PCM-Power_Win/stdafx.h $DIRNAME/stdafx.h +cp PCM-Power_Win/stdafx.cpp $DIRNAME/stdafx.cpp + +sed 's/pcm-power/'$UTILNAME'/g' PCM-Power_Win/pcm-power-win.cpp > $DIRNAME/$UTILNAME-win.cpp + +sed 's/pcm-power/'$UTILNAME'/g' PCM-Power_Win/pcm-power-win.vcproj > $DIRNAME/$UTILNAME-win.vcproj + + diff --git a/intelpcm.so/Makefile b/intelpcm.so/Makefile new file mode 100644 index 0000000..7ad56a0 --- /dev/null +++ b/intelpcm.so/Makefile @@ -0,0 +1,15 @@ +# Copyright (c) 2014 Intel Corporation + +all: libintelpcm.so + +OPT= -g -O3 +CXXFLAGS+= -Wall -fPIC $(OPT) +vpath %.cpp .. + +libintelpcm.so: msr.o cpucounters.o pci.o client_bw.o + $(CXX) $(CXXFLAGS) -shared $^ -lpthread -o $@ + +clean: + rm -rf *.x *.o *~ *.so + + diff --git a/memoptest.cpp b/memoptest.cpp new file mode 100644 index 0000000..4dde7c4 --- /dev/null +++ b/memoptest.cpp @@ -0,0 +1,147 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// + +#include "cpucounters.h" +#include +#include +#include +#include +#include +#include +#include + +using std::cout; +using std::endl; + +inline double my_timestamp() +{ + struct timeval tp; + gettimeofday(&tp, NULL); + return double(tp.tv_sec) + tp.tv_usec / 1000000.; +} + +struct T +{ + int key[1]; + int data[3]; + + T() { } + T(int a) { key[0] = a; } + + bool operator == (const T & k) const + { + return k.key[0] == key[0]; + } +}; + + +template +void write_intensive_task(Y * p, Y * e, int value) +{ + __m128i i = _mm_set_epi32(value, value, value, value); + +#if 0 + while (p != e) + { + *p = value; + ++p; + } +#else + while (p != e) + { + _mm_store_si128((__m128i *)p++, i); + } +#endif +} + +template +void stream_write_task(Y * p, Y * e, int value) +{ + __m128i i = _mm_set_epi32(value, value, value, value); + + while (p != e) + { + _mm_stream_si128((__m128i *)p++, i); + } +} + +template +void read_intensive_task(Y * p, Y * e, int value) +{ + std::find(p, e, -1); +} + + +int main(int argc, char * argv[]) +{ + assert((argc > 1) && "Need operation type as parameter: 0 - read, 1 - write, 2 - streaming write "); + int op = atoi(argv[1]); + T * vector; + int nelements = 13000000; + vector = new T[nelements]; + + int i = 0; + + cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB" << std::endl; + + for ( ; i < nelements; ++i) + { + vector[i].key[0] = 10; + } + + double before_ts, after_ts; + + + while (1) + { + before_ts = my_timestamp(); + switch (op) + { + case 1: + cout << "Writing memory" << std::endl; + break; + case 0: + cout << "Reading memory" << std::endl; + break; + default: + cout << "Streaming to memory" << std::endl; + } + + int niter = 32; + i = niter; + int r = rand(); + while (i--) + { + switch (op) + { + case 1: + write_intensive_task(vector, vector + nelements, r); + break; + case 0: + read_intensive_task(vector, vector + nelements, r); + break; + default: + stream_write_task(vector, vector + nelements, r); + } + + after_ts = my_timestamp(); + } + cout << "Bandwidth: " << (sizeof(T) * nelements * niter) / ((after_ts - before_ts) * 1024 * 1024) << " MByte/sec" << endl; + } + + delete[] vector; + + return 0; +} diff --git a/msr.cpp b/msr.cpp new file mode 100644 index 0000000..cf94be9 --- /dev/null +++ b/msr.cpp @@ -0,0 +1,234 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// Austen Ott +// Jim Harris (FreeBSD) + +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif +#include "types.h" +#include "msr.h" +#include + +#ifdef _MSC_VER + +#include +#include "utils.h" +#include "Winmsrdriver\win7\msrstruct.h" +#include "winring0/OlsDef.h" +#include "winring0/OlsApiInitExt.h" + +extern HMODULE hOpenLibSys; + +// here comes an implementatation for Windows +MsrHandle::MsrHandle(uint32 cpu) : cpu_id(cpu) +{ + hDriver = CreateFile(L"\\\\.\\RDMSR", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (hDriver == INVALID_HANDLE_VALUE && hOpenLibSys == NULL) + throw std::exception(); +} + +MsrHandle::~MsrHandle() +{ + if(hDriver != INVALID_HANDLE_VALUE) CloseHandle(hDriver); +} + +int32 MsrHandle::write(uint64 msr_number, uint64 value) +{ + if(hDriver != INVALID_HANDLE_VALUE) + { + MSR_Request req; + ULONG64 result; + DWORD reslength = 0; + req.core_id = cpu_id; + req.msr_address = msr_number; + req.write_value = value; + BOOL status = DeviceIoControl(hDriver, IO_CTL_MSR_WRITE, &req, sizeof(MSR_Request), &result, sizeof(uint64), &reslength, NULL); + assert(status && "Error in DeviceIoControl"); + return reslength; + } + + cvt_ds cvt; + cvt.ui64 = value; + + #ifdef COMPILE_FOR_WINDOWS_7 + ThreadGroupTempAffinity affinity(cpu_id); + BOOL status = Wrmsr((DWORD)msr_number, cvt.ui32.low, cvt.ui32.high); + #else + BOOL status = WrmsrTx((DWORD)msr_number, cvt.ui32.low, cvt.ui32.high,(1UL << cpu_id)); + #endif + + return status?sizeof(uint64):0; +} + +int32 MsrHandle::read(uint64 msr_number, uint64 * value) +{ + if(hDriver != INVALID_HANDLE_VALUE) + { + MSR_Request req; + // ULONG64 result; + DWORD reslength = 0; + req.core_id = cpu_id; + req.msr_address = msr_number; + BOOL status = DeviceIoControl(hDriver, IO_CTL_MSR_READ, &req, sizeof(MSR_Request), value, sizeof(uint64), &reslength, NULL); + assert(status && "Error in DeviceIoControl"); + return reslength; + } + + cvt_ds cvt; + cvt.ui64 = 0; + + #ifdef COMPILE_FOR_WINDOWS_7 + ThreadGroupTempAffinity affinity(cpu_id); + BOOL status = Rdmsr((DWORD)msr_number, &(cvt.ui32.low), &(cvt.ui32.high)); + #else + BOOL status = RdmsrTx((DWORD)msr_number, &(cvt.ui32.low), &(cvt.ui32.high), (1UL << cpu_id)); + #endif + + if(status) *value = cvt.ui64; + + return status?sizeof(uint64):0; +} + +#elif __APPLE__ +// OSX Version + +MSRAccessor * MsrHandle::driver = NULL; +int MsrHandle::num_handles = 0; + +MsrHandle::MsrHandle(uint32 cpu) +{ + cpu_id = cpu; + if(!driver) + { + driver = new MSRAccessor(); + MsrHandle::num_handles = 1; + } + else + { + MsrHandle::num_handles++; + } +} + +MsrHandle::~MsrHandle() +{ + MsrHandle::num_handles--; + if(MsrHandle::num_handles == 0) + { + delete driver; + driver = NULL; + } +} + +int32 MsrHandle::write(uint64 msr_number, uint64 value) +{ + return driver->write(cpu_id, msr_number, value); +} + +int32 MsrHandle::read(uint64 msr_number, uint64 * value){ + return driver->read(cpu_id, msr_number, value); +} + +int32 MsrHandle::buildTopology(uint32 num_cores, void* ptr){ + return driver->buildTopology(num_cores, ptr); +} + +uint32 MsrHandle::getNumInstances(){ + return driver->getNumInstances(); +} + +uint32 MsrHandle::incrementNumInstances(){ + return driver->incrementNumInstances(); +} + +uint32 MsrHandle::decrementNumInstances(){ + return driver->decrementNumInstances(); +} + +#elif (defined __FreeBSD__) + +#include +#include +MsrHandle::MsrHandle(uint32 cpu) : fd(-1), cpu_id(cpu) +{ + char path[200]; + sprintf(path, "/dev/cpuctl%d", cpu); + int handle = ::open(path, O_RDWR); + if (handle < 0) throw std::exception(); + fd = handle; +} + +MsrHandle::~MsrHandle() +{ + if (fd >= 0) ::close(fd); +} + +int32 MsrHandle::write(uint64 msr_number, uint64 value) +{ + cpuctl_msr_args_t args; + + args.msr = msr_number; + args.data = value; + return ::ioctl(fd, CPUCTL_WRMSR, &args); +} + +int32 MsrHandle::read(uint64 msr_number, uint64 * value) +{ + cpuctl_msr_args_t args; + int32 ret; + + args.msr = msr_number; + ret = ::ioctl(fd, CPUCTL_RDMSR, &args); + if (!ret) *value = args.data; + return ret; +} + +#else +// here comes a Linux version +MsrHandle::MsrHandle(uint32 cpu) : fd(-1), cpu_id(cpu) +{ + char * path = new char[200]; + sprintf(path, "/dev/cpu/%d/msr", cpu); + int handle = ::open(path, O_RDWR); + if(handle < 0) + { // try Android msr device path + sprintf(path, "/dev/msr%d", cpu); + handle = ::open(path, O_RDWR); + } + delete[] path; + if (handle < 0) throw std::exception(); + fd = handle; +} + +MsrHandle::~MsrHandle() +{ + if (fd >= 0) ::close(fd); +} + +int32 MsrHandle::write(uint64 msr_number, uint64 value) +{ + return ::pwrite(fd, (const void *)&value, sizeof(uint64), msr_number); +} + +int32 MsrHandle::read(uint64 msr_number, uint64 * value) +{ + return ::pread(fd, (void *)value, sizeof(uint64), msr_number); +} + +#endif diff --git a/msr.h b/msr.h new file mode 100644 index 0000000..fb34238 --- /dev/null +++ b/msr.h @@ -0,0 +1,145 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// Austen Ott + +#ifndef CPUCounters_MSR_H +#define CPUCounters_MSR_H + +/*! \file msr.h + \brief Low level interface to access hardware model specific registers + + Implemented and tested for Linux and 64-bit Windows 7 +*/ + +#include "types.h" + +#ifdef _MSC_VER +#include "windows.h" +#elif __APPLE__ +#include +#endif + + +class MsrHandle +{ +#ifdef _MSC_VER + HANDLE hDriver; +#elif __APPLE__ + static MSRAccessor* driver; + static int num_handles; +#else + int32 fd; +#endif + uint32 cpu_id; + MsrHandle(); // forbidden + MsrHandle(MsrHandle &); // forbidden + +public: + MsrHandle(uint32 cpu); + int32 read(uint64 msr_number, uint64 * value); + int32 write(uint64 msr_number, uint64 value); + int32 getCoreId() { return cpu_id; } +#ifdef __APPLE__ + int32 buildTopology(uint32 num_cores, void*); + uint32 getNumInstances(); + uint32 incrementNumInstances(); + uint32 decrementNumInstances(); +#endif + virtual ~MsrHandle(); +}; + +class SafeMsrHandle +{ + MsrHandle * pHandle; + + SafeMsrHandle(SafeMsrHandle &); // forbidden + + public: + SafeMsrHandle() : pHandle(NULL) {} + + SafeMsrHandle(uint32 core_id) + { + pHandle = new MsrHandle(core_id); + } + + int32 read(uint64 msr_number, uint64 * value) + { + if(pHandle) + return pHandle->read(msr_number, value); + + *value = 0; + + return sizeof(uint64); + } + + int32 write(uint64 msr_number, uint64 value) + { + if(pHandle) + return pHandle->write(msr_number, value); + + return sizeof(uint64); + } + int32 getCoreId() + { + if(pHandle) + return pHandle->getCoreId(); + + throw std::exception(); + return -1; + } +#ifdef __APPLE__ + int32 buildTopology(uint32 num_cores, void* p) + { + if(pHandle) + return pHandle->buildTopology(num_cores,p); + + throw std::exception(); + return 0; + } + uint32 getNumInstances() + { + if(pHandle) + return pHandle->getNumInstances(); + + throw std::exception(); + return 0; + } + uint32 incrementNumInstances() + { + if(pHandle) + return pHandle->incrementNumInstances(); + + throw std::exception(); + return 0; + } + uint32 decrementNumInstances() + { + if(pHandle) + return pHandle->decrementNumInstances(); + + throw std::exception(); + return 0; + } +#endif + virtual ~SafeMsrHandle() + { + if(pHandle) + { + delete pHandle; + pHandle = NULL; + } + } +}; + +#endif diff --git a/msrtest.cpp b/msrtest.cpp new file mode 100644 index 0000000..df6e289 --- /dev/null +++ b/msrtest.cpp @@ -0,0 +1,93 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// Austen Ott + +#include +#include +#include +#include "msr.h" + +#define NUM_CORES 16 + + +int main() +{ + uint32 i = 0; + uint32 res; + MsrHandle * cpu_msr[NUM_CORES]; + + for (i = 0; i < NUM_CORES; ++i) + { + cpu_msr[i] = new MsrHandle(i); + assert(cpu_msr >= 0); + + FixedEventControlRegister ctrl_reg; + res = cpu_msr[i]->read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); + assert(res >= 0); + + ctrl_reg.fields.os0 = 1; + ctrl_reg.fields.usr0 = 1; + ctrl_reg.fields.any_thread0 = 0; + ctrl_reg.fields.enable_pmi0 = 0; + + ctrl_reg.fields.os1 = 1; + ctrl_reg.fields.usr1 = 1; + ctrl_reg.fields.any_thread1 = 0; + ctrl_reg.fields.enable_pmi1 = 0; + + ctrl_reg.fields.os2 = 1; + ctrl_reg.fields.usr2 = 1; + ctrl_reg.fields.any_thread2 = 0; + ctrl_reg.fields.enable_pmi2 = 0; + + res = cpu_msr[i]->write(IA32_CR_FIXED_CTR_CTRL, ctrl_reg.value); + assert(res >= 0); + + // start counting + uint64 value = (1ULL << 0) + (1ULL << 1) + (1ULL << 2) + (1ULL << 3) + (1ULL << 32) + (1ULL << 33) + (1ULL << 34); + res = cpu_msr[i]->write(IA32_CR_PERF_GLOBAL_CTRL, value); + assert(res >= 0); + } + uint64 counters_before[NUM_CORES][3]; + uint64 counters_after[NUM_CORES][3]; + + for (i = 0; i < NUM_CORES; ++i) + { + res = cpu_msr[i]->read(INST_RETIRED_ANY_ADDR, &counters_before[i][0]); + assert(res >= 0); + res = cpu_msr[i]->read(CPU_CLK_UNHALTED_THREAD_ADDR, &counters_before[i][1]); + assert(res >= 0); + res = cpu_msr[i]->read(CPU_CLK_UNHALTED_REF_ADDR, &counters_before[i][2]); + assert(res >= 0); + } + //sleep for some time + ::sleep(1); + for (i = 0; i < NUM_CORES; ++i) + { + res = cpu_msr[i]->read(INST_RETIRED_ANY_ADDR, &counters_after[i][0]); + assert(res >= 0); + res = cpu_msr[i]->read(CPU_CLK_UNHALTED_THREAD_ADDR, &counters_after[i][1]); + assert(res >= 0); + res = cpu_msr[i]->read(CPU_CLK_UNHALTED_REF_ADDR, &counters_after[i][2]); + assert(res >= 0); + } + for (i = 0; i < NUM_CORES; ++i) + delete cpu_msr[i]; + for (i = 0; i < NUM_CORES; ++i) + std::cout << "Core " << i << + "\t Instructions: " << (counters_after[i][0] - counters_before[i][0]) << + "\t Cycles: " << (counters_after[i][1] - counters_before[i][1]) << + "\t IPC: " << double(counters_after[i][0] - counters_before[i][0]) / double(counters_after[i][1] - counters_before[i][1]) << std::endl; + +} diff --git a/pci.cpp b/pci.cpp new file mode 100644 index 0000000..76eebb1 --- /dev/null +++ b/pci.cpp @@ -0,0 +1,645 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev, +// Pat Fay +// Austen Ott +// Jim Harris (FreeBSD) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pci.h" + +#ifndef _MSC_VER +#include +#include +#endif + +#ifdef _MSC_VER + +#include +#include "Winmsrdriver\win7\msrstruct.h" +#include "winring0/OlsDef.h" +#include "winring0/OlsApiInitExt.h" + +extern HMODULE hOpenLibSys; + +PciHandle::PciHandle(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) : + bus(bus_), + device(device_), + function(function_), + pciAddress(PciBusDevFunc(bus_, device_, function_)) +{ + if(groupnr_ != 0) + { + std::cerr << "Non-zero PCI group segments are not supported in PCM/Windows"<> 16) & 0xffff; + + //if (vendor_id == PCM_INTEL_PCI_VENDOR_ID) { + if (vendor_id != 0xffff && device_id != 0xffff) { + return true; + } else { + return false; + } +} + +int32 PciHandle::read32(uint64 offset, uint32 * value) +{ + uint32_t pci_address = FORM_PCI_ADDR(bus, device, function, (uint32_t)offset); + return PCIDriver_read32(pci_address, value); +} + +int32 PciHandle::write32(uint64 offset, uint32 value) +{ + uint32_t pci_address = FORM_PCI_ADDR(bus, device, function, (uint32_t)offset); + return PCIDriver_write32(pci_address, value); +} + +int32 PciHandle::read64(uint64 offset, uint64 * value) +{ + uint32_t pci_address = FORM_PCI_ADDR(bus, device, function, (uint32_t)offset); + return PCIDriver_read64(pci_address, value); +} + +PciHandle::~PciHandle() +{ + +} + +#elif (defined __FreeBSD__) + +#include + +PciHandle::PciHandle(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) : + fd(-1), + bus(bus_), + device(device_), + function(function_) +{ + + if(groupnr_ != 0) + { + std::cout << "ERROR: non-zero PCI segment groupnr is not supported in this PciHandle implementation" << std::endl; + throw std::exception(); + } + + int handle = ::open("/dev/pci", O_RDWR); + if (handle < 0) throw std::exception(); + fd = handle; +} + +bool PciHandle::exists(uint32 bus_, uint32 device_, uint32 function_) +{ + struct pci_conf_io pc; + struct pci_match_conf pattern; + struct pci_conf conf[4]; + int fd; + + fd = ::open("/dev/pci", O_RDWR, 0); + if (fd < 0) return false; + + bzero(&pc, sizeof(pc)); + + pattern.pc_sel.pc_bus = bus_; + pattern.pc_sel.pc_dev = device_; + pattern.pc_sel.pc_func = function_; + pattern.flags = (pci_getconf_flags)(PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC); + + pc.pat_buf_len = sizeof(pattern); + pc.patterns = &pattern; + pc.num_patterns = 1; + pc.match_buf_len = sizeof(conf); + pc.matches = conf; + + if (ioctl(fd, PCIOCGETCONF, &pc)) return false; + + if (pc.status != PCI_GETCONF_LAST_DEVICE) return false; + + if (pc.num_matches > 0) return true; + + return false; +} + +int32 PciHandle::read32(uint64 offset, uint32 * value) +{ + struct pci_io pi; + int ret; + + pi.pi_sel.pc_domain = 0; + pi.pi_sel.pc_bus = bus; + pi.pi_sel.pc_dev = device; + pi.pi_sel.pc_func = function; + pi.pi_reg = offset; + pi.pi_width = 4; + + ret = ioctl(fd, PCIOCREAD, &pi); + + if (!ret) *value = pi.pi_data; + + return ret; +} + +int32 PciHandle::write32(uint64 offset, uint32 value) +{ + struct pci_io pi; + + pi.pi_sel.pc_domain = 0; + pi.pi_sel.pc_bus = bus; + pi.pi_sel.pc_dev = device; + pi.pi_sel.pc_func = function; + pi.pi_reg = offset; + pi.pi_width = 4; + pi.pi_data = value; + + return ioctl(fd, PCIOCWRITE, &pi); +} + +int32 PciHandle::read64(uint64 offset, uint64 * value) +{ + struct pci_io pi; + int32 ret; + + pi.pi_sel.pc_domain = 0; + pi.pi_sel.pc_bus = bus; + pi.pi_sel.pc_dev = device; + pi.pi_sel.pc_func = function; + pi.pi_reg = offset; + pi.pi_width = 4; + + ret = ioctl(fd, PCIOCREAD, &pi); + if (ret) return ret; + + *value = pi.pi_data; + + pi.pi_reg += 4; + + ret = ioctl(fd, PCIOCREAD, &pi); + if (ret) return ret; + + *value += ((uint64)pi.pi_data << 32); + return 0; +} + +PciHandle::~PciHandle() +{ + if (fd >= 0) ::close(fd); +} + +#else + + +// Linux implementation + +PciHandle::PciHandle(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) : + fd(-1), + bus(bus_), + device(device_), + function(function_) +{ + + if(groupnr_ != 0) + { + std::cout << "ERROR: non-zero PCI segment groupnr is not supported in this PciHandle implementation" << std::endl; + throw std::exception(); + } + + std::ostringstream path(std::ostringstream::out); + + path << std::hex << "/proc/bus/pci/" << std::setw(2) << std::setfill('0') << bus << "/" << std::setw(2) << std::setfill('0') << device << "." << function; + + // std::cout << path.str().c_str() << std::endl; + + int handle = ::open(path.str().c_str(), O_RDWR); + if (handle < 0) throw std::exception(); + fd = handle; + + // std::cout << "Opened "<< path.str().c_str() << " on handle "<< fd << std::endl; +} + + +bool PciHandle::exists(uint32 bus_, uint32 device_, uint32 function_) +{ + std::ostringstream path(std::ostringstream::out); + + path << std::hex << "/proc/bus/pci/" << std::setw(2) << std::setfill('0') << bus_ << "/" << std::setw(2) << std::setfill('0') << device_ << "." << function_; + + int handle = ::open(path.str().c_str(), O_RDWR); + + if (handle < 0) return false; + + ::close(handle); + + return true; +} + +int32 PciHandle::read32(uint64 offset, uint32 * value) +{ + return ::pread(fd, (void *)value, sizeof(uint32), offset); +} + +int32 PciHandle::write32(uint64 offset, uint32 value) +{ + return ::pwrite(fd, (const void *)&value, sizeof(uint32), offset); +} + +int32 PciHandle::read64(uint64 offset, uint64 * value) +{ + return ::pread(fd, (void *)value, sizeof(uint64), offset); +} + +PciHandle::~PciHandle() +{ + if (fd >= 0) ::close(fd); +} + +#ifndef PCM_USE_PCI_MM_LINUX + +PciHandleM::PciHandleM(uint32 bus_, uint32 device_, uint32 function_) : + fd(-1), + bus(bus_), + device(device_), + function(function_), + base_addr(0) +{ + int handle = ::open("/dev/mem", O_RDWR); + if (handle < 0) throw std::exception(); + fd = handle; + + int mcfg_handle = ::open("/sys/firmware/acpi/tables/MCFG", O_RDONLY); + + if (mcfg_handle < 0) throw std::exception(); + + int32 result = ::pread(mcfg_handle, (void *)&base_addr, sizeof(uint64), 44); + + if (result != sizeof(uint64)) + { + ::close(mcfg_handle); + throw std::exception(); + } + + unsigned char max_bus = 0; + + result = ::pread(mcfg_handle, (void *)&max_bus, sizeof(unsigned char),55); + + if (result != sizeof(unsigned char)) + { + ::close(mcfg_handle); + throw std::exception(); + } + + ::close(mcfg_handle); + + if(bus > (unsigned)max_bus) + { + std::cout << "WARNING: Requested bus number "<< bus<< " is larger than the max bus number " << (unsigned)max_bus << std::endl; + throw std::exception(); + } + + // std::cout << "PCI config base addr: "<< std::hex << base_addr<< std::endl; + + base_addr += (bus * 1024 * 1024 + device * 32 * 1024 + function * 4 * 1024); +} + + +bool PciHandleM::exists(uint32 /* bus_*/, uint32 /* device_ */, uint32 /* function_ */) +{ + int handle = ::open("/dev/mem", O_RDWR); + + if (handle < 0) return false; + + ::close(handle); + + handle = ::open("/sys/firmware/acpi/tables/MCFG", O_RDONLY); + + if (handle < 0) return false; + + ::close(handle); + + return true; +} + +int32 PciHandleM::read32(uint64 offset, uint32 * value) +{ + return ::pread(fd, (void *)value, sizeof(uint32), offset + base_addr); +} + +int32 PciHandleM::write32(uint64 offset, uint32 value) +{ + return ::pwrite(fd, (const void *)&value, sizeof(uint32), offset + base_addr); +} + +int32 PciHandleM::read64(uint64 offset, uint64 * value) +{ + return ::pread(fd, (void *)value, sizeof(uint64), offset + base_addr); +} + +PciHandleM::~PciHandleM() +{ + if (fd >= 0) ::close(fd); +} + +#endif // PCM_USE_PCI_MM_LINUX + +// mmaped I/O version + +MCFGHeader PciHandleMM::mcfgHeader; +std::vector PciHandleMM::mcfgRecords; + +const std::vector & PciHandleMM::getMCFGRecords() +{ + readMCFG(); + return mcfgRecords; +} + +void PciHandleMM::readMCFG() +{ + if(mcfgRecords.size() > 0 ) + return; // already initialized + + const char * path = "/sys/firmware/acpi/tables/MCFG"; + int mcfg_handle = ::open(path, O_RDONLY); + + if (mcfg_handle < 0) + { + std::cerr << "PCM Error: Cannot open " << path << std::endl; + throw std::exception(); + } + + ssize_t read_bytes = ::read(mcfg_handle, (void *)&mcfgHeader, sizeof(MCFGHeader)); + + if(read_bytes == 0) + { + std::cerr << "PCM Error: Cannot read " << path << std::endl; + throw std::exception(); + } + + const unsigned segments = mcfgHeader.nrecords(); +#ifdef PCM_DEBUG + mcfgHeader.print(); + std::cout << "PCM Debug: total segments: "<= 0) ::close(fd); +} + + +#endif diff --git a/pci.h b/pci.h new file mode 100644 index 0000000..d90db21 --- /dev/null +++ b/pci.h @@ -0,0 +1,160 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// Pat Fay +// Jim Harris (FreeBSD) + + +#ifndef CPUCounters_PCI_H +#define CPUCounters_PCI_H + +/*! \file pci.h + \brief Low level interface to access PCI configuration space + +*/ + +#include "types.h" + +#ifdef _MSC_VER +#include "windows.h" +#else +#include +#endif + +#ifdef __APPLE__ +#include "PCIDriverInterface.h" +#endif + +#include + +#define PCM_USE_PCI_MM_LINUX + +class PciHandle +{ +#ifdef _MSC_VER + HANDLE hDriver; +#else + int32 fd; +#endif + + uint32 bus; + uint32 device; + uint32 function; +#ifdef _MSC_VER + DWORD pciAddress; +#endif + + PciHandle(); // forbidden + PciHandle(PciHandle &); // forbidden + +public: + PciHandle(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_); + + static bool exists(uint32 bus_, uint32 device_, uint32 function_); + + int32 read32(uint64 offset, uint32 * value); + int32 write32(uint64 offset, uint32 value); + + int32 read64(uint64 offset, uint64 * value); + + virtual ~PciHandle(); +}; + +#ifdef _MSC_VER +typedef PciHandle PciHandleM; +#elif __APPLE__ +// This may need to change if it can be implemented for OSX +typedef PciHandle PciHandleM; +#elif __FreeBSD__ +typedef PciHandle PciHandleM; +#else + +// read/write PCI config space using physical memory +class PciHandleM +{ +#ifdef _MSC_VER + +#else + int32 fd; +#endif + + uint32 bus; + uint32 device; + uint32 function; + uint64 base_addr; + + PciHandleM(); // forbidden + PciHandleM(PciHandleM &); // forbidden + +public: + PciHandleM(uint32 bus_, uint32 device_, uint32 function_); + + static bool exists(uint32 bus_, uint32 device_, uint32 function_); + + int32 read32(uint64 offset, uint32 * value); + int32 write32(uint64 offset, uint32 value); + + int32 read64(uint64 offset, uint64 * value); + + virtual ~PciHandleM(); +}; + +#ifndef _MSC_VER + +// read/write PCI config space using physical memory using mmaped file I/O +class PciHandleMM +{ + int32 fd; + char * mmapAddr; + + uint32 bus; + uint32 device; + uint32 function; + uint64 base_addr; + +#ifdef __linux__ + static MCFGHeader mcfgHeader; + static std::vector mcfgRecords; + static void readMCFG(); +#endif + + PciHandleMM(); // forbidden + PciHandleMM(PciHandleM &); // forbidden + +public: + PciHandleMM(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_); + + static bool exists(uint32 bus_, uint32 device_, uint32 function_); + + int32 read32(uint64 offset, uint32 * value); + int32 write32(uint64 offset, uint32 value); + + int32 read64(uint64 offset, uint64 * value); + + virtual ~PciHandleMM(); + +#ifdef __linux__ + static const std::vector & getMCFGRecords(); +#endif +}; + +#ifdef PCM_USE_PCI_MM_LINUX +#define PciHandleM PciHandleMM +#endif + +#endif // _MSC_VER + +#endif + + +#endif diff --git a/pcm-memory.cpp b/pcm-memory.cpp new file mode 100644 index 0000000..be02053 --- /dev/null +++ b/pcm-memory.cpp @@ -0,0 +1,532 @@ +/* + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Patrick Lu +// increased max sockets to 256 - Thomas Willhalm + + +/*! \file pcm-memory.cpp + \brief Example of using CPU counters: implements a performance counter monitoring utility for memory controller channels + */ +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include +#ifdef _MSC_VER +#pragma warning(disable : 4996) // for sprintf +#include +#include "../PCM_Win/windriver.h" +#else +#include +#include +#include // for gettimeofday() +#endif +#include +#include +#include +#include +#include +#include +#include +#include "cpucounters.h" +#include "utils.h" + +//Programmable iMC counter +#define READ 0 +#define WRITE 1 +#define PARTIAL 2 +#define PCM_DELAY_DEFAULT 1.0 // in seconds +#define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs +#define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration + +using namespace std; + +void print_help(const string prog_name) +{ + cerr << endl << " Usage: " << endl << " " << prog_name + << " --help | [delay] [options] [-- external_program [external_program_options]]" << endl; + cerr << " => time interval to sample performance counters." << endl; + cerr << " If not specified, or 0, with external program given" << endl; + cerr << " will read counters only after external program finishes" << endl; + cerr << " Supported are: " << endl; + cerr << " -h | --help | /h => print this help and exit" << endl; + cerr << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or" << endl + << " to a file, in case filename is provided" << endl; +#ifdef _MSC_VER + cerr << " --uninstallDriver | --installDriver=> (un)install driver" << endl; +#endif + cerr << " Examples:" << endl; + cerr << " " << prog_name << " 1 => print counters every second without core and socket output" << endl; + cerr << " " << prog_name << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format" << endl; + cerr << " " << prog_name << " /csv 5 2>/dev/null => one sampe every 5 seconds, and discard all diagnostic output" << endl; + cerr << endl; +} + +void display_bandwidth(float *iMC_Rd_socket_chan, float *iMC_Wr_socket_chan, float *iMC_Rd_socket, float *iMC_Wr_socket, uint32 numSockets, uint32 num_imc_channels, uint64 *partial_write) +{ + float sysRead = 0.0, sysWrite = 0.0; + uint32 skt = 0; + cout.setf(ios::fixed); + cout.precision(2); + + while(skt < numSockets) + { + if(!(skt % 2) && ((skt+1) < numSockets)) //This is even socket, and it has at least one more socket which can be displayed together + { + cout << "\ + \r---------------------------------------||---------------------------------------\n\ + \r-- Socket "<tm_year << '-' << 1+tt->tm_mon << '-' << tt->tm_mday << ';' + << tt->tm_hour << ':' << tt->tm_min << ':' << tt->tm_sec << ';'; + + + float sysRead = 0.0, sysWrite = 0.0; + + cout.setf(ios::fixed); + cout.precision(2); + + for (uint32 skt=0; skt < numSockets; ++skt) + { + for(uint64 channel = 0; channel < num_imc_channels; ++channel) + { + if(iMC_Rd_socket_chan[skt*num_imc_channels+channel] < 0.0 && iMC_Wr_socket_chan[skt*num_imc_channels+channel] < 0.0) //If the channel read neg. value, the channel is not working; skip it. + continue; + cout <getMCChannelsPerSocket(); + float iMC_Rd_socket_chan[max_sockets][max_imc_channels]; + float iMC_Wr_socket_chan[max_sockets][max_imc_channels]; + float iMC_Rd_socket[max_sockets]; + float iMC_Wr_socket[max_sockets]; + uint64 partial_write[max_sockets]; + + for(uint32 skt = 0; skt < m->getNumSockets(); ++skt) + { + iMC_Rd_socket[skt] = 0.0; + iMC_Wr_socket[skt] = 0.0; + partial_write[skt] = 0; + + for(uint32 channel = 0; channel < max_imc_channels; ++channel) + { + if(getMCCounter(channel,READ,uncState1[skt],uncState2[skt]) == 0.0 && getMCCounter(channel,WRITE,uncState1[skt],uncState2[skt]) == 0.0) //In case of JKT-EN, there are only three channels. Skip one and continue. + { + iMC_Rd_socket_chan[skt][channel] = -1.0; + iMC_Wr_socket_chan[skt][channel] = -1.0; + continue; + } + + iMC_Rd_socket_chan[skt][channel] = (float) (getMCCounter(channel,READ,uncState1[skt],uncState2[skt]) * 64 / 1000000.0 / (elapsedTime/1000.0)); + iMC_Wr_socket_chan[skt][channel] = (float) (getMCCounter(channel,WRITE,uncState1[skt],uncState2[skt]) * 64 / 1000000.0 / (elapsedTime/1000.0)); + + iMC_Rd_socket[skt] += iMC_Rd_socket_chan[skt][channel]; + iMC_Wr_socket[skt] += iMC_Wr_socket_chan[skt][channel]; + + partial_write[skt] += (uint64) (getMCCounter(channel,PARTIAL,uncState1[skt],uncState2[skt]) / (elapsedTime/1000.0)); + } + } + + if (csv) { + if (csvheader) { + display_bandwidth_csv_header(iMC_Rd_socket_chan[0], iMC_Wr_socket_chan[0], iMC_Rd_socket, iMC_Wr_socket, m->getNumSockets(), max_imc_channels, partial_write); + csvheader = false; + } + display_bandwidth_csv(iMC_Rd_socket_chan[0], iMC_Wr_socket_chan[0], iMC_Rd_socket, iMC_Wr_socket, m->getNumSockets(), max_imc_channels, partial_write, elapsedTime); + } else { + display_bandwidth(iMC_Rd_socket_chan[0], iMC_Wr_socket_chan[0], iMC_Rd_socket, iMC_Wr_socket, m->getNumSockets(), max_imc_channels, partial_write); + } +} + +int main(int argc, char * argv[]) +{ + set_signal_handlers(); + +#ifdef PCM_FORCE_SILENT + null_stream nullStream1, nullStream2; + std::cout.rdbuf(&nullStream1); + std::cerr.rdbuf(&nullStream2); +#endif + +#ifdef _MSC_VER + TCHAR driverPath[1040]; // length for current directory + "\\msr.sys" + GetCurrentDirectory(1024, driverPath); + wcscat_s(driverPath, 1040, L"\\msr.sys"); +#endif + + cerr << endl; + cerr << " Intel(r) Performance Counter Monitor: Memory Bandwidth Monitoring Utility " << INTEL_PCM_VERSION << endl; + cerr << endl; + cerr << " Copyright (c) 2009-2014 Intel Corporation" << endl; + cerr << " This utility measures memory bandwidth per channel in real-time" << endl; + cerr << endl; + + double delay = -1.0; + bool csv = false, csvheader=false; + char * sysCmd = NULL; + char ** sysArgv = NULL; + long diff_usec = 0; // deviation of clock is useconds between measurements + int calibrated = PCM_CALIBRATION_INTERVAL - 2; // keeps track is the clock calibration needed + string program = string(argv[0]); + + PCM * m = PCM::getInstance(); + + if(argc > 1) do + { + argv++; + argc--; + if (strncmp(*argv, "--help", 6) == 0 || + strncmp(*argv, "-h", 2) == 0 || + strncmp(*argv, "/h", 2) == 0) + { + print_help(program); + exit(EXIT_FAILURE); + } + else + if (strncmp(*argv, "-csv",4) == 0 || + strncmp(*argv, "/csv",4) == 0) + { + csv = true; + csvheader = true; + string cmd = string(*argv); + size_t found = cmd.find('=',4); + if (found != string::npos) { + string filename = cmd.substr(found+1); + if (!filename.empty()) { + m->setOutput(filename); + } + } + continue; + } +#ifdef _MSC_VER + else + if (strncmp(*argv, "--uninstallDriver", 17) == 0) + { + Driver tmpDrvObject; + tmpDrvObject.uninstall(); + cerr << "msr.sys driver has been uninstalled. You might need to reboot the system to make this effective." << endl; + exit(EXIT_SUCCESS); + } + else + if (strncmp(*argv, "--installDriver", 15) == 0) + { + Driver tmpDrvObject; + if (!tmpDrvObject.start(driverPath)) + { + cerr << "Can not access CPU counters" << endl; + cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program" << endl; + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } +#endif + else + if (strncmp(*argv, "--", 2) == 0) + { + argv++; + sysCmd = *argv; + sysArgv = argv; + break; + } + else + { + // any other options positional that is a floating point number is treated as , + // while the other options are ignored with a warning issues to stderr + double delay_input; + std::istringstream is_str_stream(*argv); + is_str_stream >> noskipws >> delay_input; + if(is_str_stream.eof() && !is_str_stream.fail()) { + delay = delay_input; + } else { + cerr << "WARNING: unknown command-line option: \"" << *argv << "\". Ignoring it." << endl; + print_help(program); + exit(EXIT_FAILURE); + } + continue; + } + } while(argc > 1); // end of command line partsing loop + + m->disableJKTWorkaround(); + PCM::ErrorCode status = m->program(); + switch (status) + { + case PCM::Success: + break; + case PCM::MSRAccessDenied: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (no MSR or PCI CFG space access)." << endl; + exit(EXIT_FAILURE); + case PCM::PMUBusy: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Performance Monitoring Unit is occupied by other application). Try to stop the application that uses PMU." << endl; + cerr << "Alternatively you can try to reset PMU configuration at your own risk. Try to reset? (y/n)" << endl; + char yn; + std::cin >> yn; + if ('y' == yn) + { + m->resetPMU(); + cerr << "PMU configuration has been reset. Try to rerun the program again." << endl; + } + exit(EXIT_FAILURE); + default: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Unknown error)." << endl; + exit(EXIT_FAILURE); + } + + cerr << "\nDetected "<< m->getCPUBrandString() << " \"Intel(r) microarchitecture codename "<getUArchCodename()<<"\""<hasPCICFGUncore()) + { + cerr << "Jaketown, Ivytown or Haswell Server CPU is required for this tool!" << endl; + if(m->memoryTrafficMetricsAvailable()) + cerr << "For processor-level memory bandwidth statistics please use pcm.x" << endl; + exit(EXIT_FAILURE); + } + + if(m->getNumSockets() > max_sockets) + { + cerr << "Only systems with up to "<getNumSockets()]; + ServerUncorePowerState * AfterState = new ServerUncorePowerState[m->getNumSockets()]; + uint64 BeforeTime = 0, AfterTime = 0; + + if ( (sysCmd != NULL) && (delay<=0.0) ) { + // in case external command is provided in command line, and + // delay either not provided (-1) or is zero + m->setBlocked(true); + } else { + m->setBlocked(false); + } + + if (csv) { + if( delay<=0.0 ) delay = PCM_DELAY_DEFAULT; + } else { + // for non-CSV mode delay < 1.0 does not make a lot of practical sense: + // hard to read from the screen, or + // in case delay is not provided in command line => set default + if( ((delay<1.0) && (delay>0.0)) || (delay<=0.0) ) delay = PCM_DELAY_DEFAULT; + } + + cerr << "Update every "<getNumSockets(); ++i) + BeforeState[i] = m->getServerUncorePowerState(i); + + BeforeTime = m->getTickCount(); + + if( sysCmd != NULL ) { + MySystem(sysCmd, sysArgv); + } + + while(1) + { + if(!csv) cout << std::flush; + int delay_ms = int(delay * 1000); + int calibrated_delay_ms = delay_ms; +#ifdef _MSC_VER + // compensate slow Windows console output + if(AfterTime) delay_ms -= (int)(m->getTickCount() - BeforeTime); + if(delay_ms < 0) delay_ms = 0; +#else + // compensation of delay on Linux/UNIX + // to make the samling interval as monotone as possible + struct timeval start_ts, end_ts; + if(calibrated == 0) { + gettimeofday(&end_ts, NULL); + diff_usec = (end_ts.tv_sec-start_ts.tv_sec)*1000000.0+(end_ts.tv_usec-start_ts.tv_usec); + calibrated_delay_ms = delay_ms - diff_usec/1000.0; + } +#endif + + MySleepMs(calibrated_delay_ms); + +#ifndef _MSC_VER + calibrated = (calibrated + 1) % PCM_CALIBRATION_INTERVAL; + if(calibrated == 0) { + gettimeofday(&start_ts, NULL); + } +#endif + + AfterTime = m->getTickCount(); + for(uint32 i=0; igetNumSockets(); ++i) + AfterState[i] = m->getServerUncorePowerState(i); + + if (!csv) { + cout << "Time elapsed: "<isBlocked() ) { + // in case PCM was blocked after spawning child application: break monitoring loop here + break; + } + } + + delete[] BeforeState; + delete[] AfterState; + + exit(EXIT_SUCCESS); +} diff --git a/pcm-msr.cpp b/pcm-msr.cpp new file mode 100644 index 0000000..61b449b --- /dev/null +++ b/pcm-msr.cpp @@ -0,0 +1,120 @@ +/* +Copyright (c) 2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// written by Roman Dementiev +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include "cpucounters.h" +#ifdef _MSC_VER +#pragma warning(disable : 4996) // for sprintf +#include +#include "../PCM_Win/windriver.h" +#else +#include +#endif +#include +#include +#include +#include +#ifdef _MSC_VER +#include "freegetopt/getopt.h" +#endif + +uint64 read_number(char * str) +{ + std::istringstream stream(str); + if(strstr(str,"x")) stream >> std::hex ; + uint64 result = 0; + stream >> result; + return result; +} + +void print_usage(const char * progname) +{ + std::cout << "Usage "<= argc) + { + print_usage(argv[0]); + return -1; + } + + msr = (int) read_number(argv[optind]); + + #ifdef _MSC_VER + // Increase the priority a bit to improve context switching delays on Windows + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); + + TCHAR driverPath[1032]; + GetCurrentDirectory(1024, driverPath); + wcscat_s(driverPath, 1032, L"\\msr.sys"); + + // WARNING: This driver code (msr.sys) is only for testing purposes, not for production use + Driver drv; + // drv.stop(); // restart driver (usually not needed) + if (!drv.start(driverPath)) + { + std::cout << "Can not load MSR driver." << std::endl; + std::cout << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program" << std::endl; + return -1; + } + #endif + + MsrHandle h(core); + if(!dec) std::cout << std::hex << std::showbase; + if(write) + { + std::cout << " Writing "<< value << " to MSR "<< msr << " on core "<< core << std::endl; + h.write(msr,value); + } + value = 0; + h.read(msr,&value); + std::cout << " Read value "<< value << " from MSR "<< msr << " on core "<< core << "\n" << std::endl; +} diff --git a/pcm-numa.cpp b/pcm-numa.cpp new file mode 100644 index 0000000..7262384 --- /dev/null +++ b/pcm-numa.cpp @@ -0,0 +1,348 @@ +/* + Copyright (c) 2009-2013, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Roman Dementiev + + +/*! \file pcm-numa.cpp + \brief Example of using CPU counters: implements a performance counter monitoring utility for NUMA (remote and local memory accesses counting). Example for programming offcore response events +*/ +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include +#ifdef _MSC_VER +#pragma warning(disable : 4996) // for sprintf +#include +#include "../PCM_Win/windriver.h" +#else +#include +#include +#include // for gettimeofday() +#endif +#include +#include +#include +#include +#include +#include +#include +#include "cpucounters.h" +#include "utils.h" +#ifdef _MSC_VER +#include "freegetopt/getopt.h" +#endif + +#include +#define PCM_DELAY_DEFAULT 1.0 // in seconds +#define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs +#define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration + +using namespace std; + +void print_usage(const string progname) +{ + cerr << endl << " Usage: " << endl << " " << progname + << " --help | [delay] [options] [-- external_program [external_program_options]]" << endl; + cerr << " => time interval to sample performance counters." << endl; + cerr << " If not specified, or 0, with external program given" << endl; + cerr << " will read counters only after external program finishes" << endl; + cerr << " Supported are: " << endl; + cerr << " -h | --help | /h => print this help and exit" << endl; + cerr << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or" << endl + << " to a file, in case filename is provided" << endl; + cerr << " Examples:" << endl; + cerr << " " << progname << " 1 => print counters every second without core and socket output" << endl; + cerr << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format" << endl; + cerr << " " << progname << " /csv 5 2>/dev/null => one sampe every 5 seconds, and discard all diagnostic output" << endl; + cerr << endl; +} + +template +void print_stats(const StateType & BeforeState, const StateType & AfterState, bool csv) +{ + uint64 cycles = getCycles(BeforeState, AfterState); + uint64 instr = getInstructionsRetired(BeforeState, AfterState); + + if(csv) + { + cout << double(instr)/double(cycles) << ","; + cout << instr << ","; + cout << cycles << ","; + } + else + { + cout << double(instr)/double(cycles) << " "; + cout << unit_format(instr) << " "; + cout << unit_format(cycles) << " "; + } + + for(int i=0;i<2;++i) + if(!csv) + cout << unit_format(getNumberOfCustomEvents(i, BeforeState, AfterState)) << " "; + else + cout << getNumberOfCustomEvents(i, BeforeState, AfterState)<<","; + + cout << "\n"; +} + + +int main(int argc, char * argv[]) +{ + set_signal_handlers(); + +#ifdef PCM_FORCE_SILENT + null_stream nullStream1, nullStream2; + std::cout.rdbuf(&nullStream1); + std::cerr.rdbuf(&nullStream2); +#endif + + cerr << endl; + cerr << " Intel(r) Performance Counter Monitor: NUMA monitoring utility "<< endl; + cerr << endl; + cerr << " Copyright (c) 2013-2014 Intel Corporation" << endl; + cerr << endl; + + double delay = -1.0; + char *sysCmd = NULL; + char **sysArgv = NULL; + bool csv = false; + long diff_usec = 0; // deviation of clock is useconds between measurements + int calibrated = PCM_CALIBRATION_INTERVAL - 2; // keeps track is the clock calibration needed + string program = string(argv[0]); + + PCM * m = PCM::getInstance(); + + if(argc > 1) do + { + argv++; + argc--; + if (strncmp(*argv, "--help", 6) == 0 || + strncmp(*argv, "-h", 2) == 0 || + strncmp(*argv, "/h", 2) == 0) + { + print_usage(program); + exit(EXIT_FAILURE); + } + else + if (strncmp(*argv, "-csv",4) == 0 || + strncmp(*argv, "/csv",4) == 0) + { + csv = true; + string cmd = string(*argv); + size_t found = cmd.find('=',4); + if (found != string::npos) { + string filename = cmd.substr(found+1); + if (!filename.empty()) { + m->setOutput(filename); + } + } + continue; + } + else + if (strncmp(*argv, "--", 2) == 0) + { + argv++; + sysCmd = *argv; + sysArgv = argv; + break; + } + else + { + // any other options positional that is a floating point number is treated as , + // while the other options are ignored with a warning issues to stderr + double delay_input; + std::istringstream is_str_stream(*argv); + is_str_stream >> noskipws >> delay_input; + if(is_str_stream.eof() && !is_str_stream.fail()) { + delay = delay_input; + } else { + cerr << "WARNING: unknown command-line option: \"" << *argv << "\". Ignoring it." << endl; + print_usage(program); + exit(EXIT_FAILURE); + } + continue; + } + } while(argc > 1); // end of command line partsing loop + + EventSelectRegister def_event_select_reg; + def_event_select_reg.value = 0; + def_event_select_reg.fields.usr = 1; + def_event_select_reg.fields.os = 1; + def_event_select_reg.fields.enable = 1; + + PCM::ExtendedCustomCoreEventDescription conf; + conf.fixedCfg = NULL; // default + conf.nGPCounters = 4; + switch(m->getCPUModel()) + { + case PCM::WESTMERE_EX: + conf.OffcoreResponseMsrValue[0] = 0x40FF; // OFFCORE_RESPONSE.ANY_REQUEST.LOCAL_DRAM: Offcore requests satisfied by the local DRAM + conf.OffcoreResponseMsrValue[1] = 0x20FF; // OFFCORE_RESPONSE.ANY_REQUEST.REMOTE_DRAM: Offcore requests satisfied by a remote DRAM + break; + case PCM::JAKETOWN: + case PCM::IVYTOWN: + conf.OffcoreResponseMsrValue[0] = 0x780400000 | 0x08FFF ; // OFFCORE_RESPONSE.*.LOCAL_DRAM + conf.OffcoreResponseMsrValue[1] = 0x7ff800000 | 0x08FFF ; // OFFCORE_RESPONSE.*.REMOTE_DRAM + break; + case PCM::HASWELLX: + conf.OffcoreResponseMsrValue[0] = 0x600400000 | 0x08FFF ; // OFFCORE_RESPONSE.*.LOCAL_DRAM + conf.OffcoreResponseMsrValue[1] = 0x67f800000 | 0x08FFF ; // OFFCORE_RESPONSE.*.REMOTE_DRAM + break; + default: + cerr << "pcm-numa tool does not support your processor currently." << endl; + exit(EXIT_FAILURE); + } + EventSelectRegister regs[4]; + conf.gpCounterCfg = regs; + for(int i=0;i<4;++i) + regs[i] = def_event_select_reg; + + regs[0].fields.event_select = 0xB7; // OFFCORE_RESPONSE 0 event + regs[0].fields.umask = 0x01; + regs[1].fields.event_select = 0xBB; // OFFCORE_RESPONSE 1 event + regs[1].fields.umask = 0x01; + + PCM::ErrorCode status = m->program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf); + switch (status) + { + case PCM::Success: + break; + case PCM::MSRAccessDenied: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (no MSR or PCI CFG space access)." << endl; + exit(EXIT_FAILURE); + case PCM::PMUBusy: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Performance Monitoring Unit is occupied by other application). Try to stop the application that uses PMU." << endl; + cerr << "Alternatively you can try to reset PMU configuration at your own risk. Try to reset? (y/n)" << endl; + char yn; + std::cin >> yn; + if ('y' == yn) + { + m->resetPMU(); + cerr << "PMU configuration has been reset. Try to rerun the program again." << endl; + } + exit(EXIT_FAILURE); + default: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Unknown error)." << endl; + exit(EXIT_FAILURE); + } + + cerr << "\nDetected "<< m->getCPUBrandString() << " \"Intel(r) microarchitecture codename "<getUArchCodename()<<"\""<getNumCores(); + std::vector BeforeState, AfterState; + std::vector DummySocketStates; + + if ( (sysCmd != NULL) && (delay<=0.0) ) { + // in case external command is provided in command line, and + // delay either not provided (-1) or is zero + m->setBlocked(true); + } else { + m->setBlocked(false); + } + + if (csv) { + if( delay<=0.0 ) delay = PCM_DELAY_DEFAULT; + } else { + // for non-CSV mode delay < 1.0 does not make a lot of practical sense: + // hard to read from the screen, or + // in case delay is not provided in command line => set default + if( ((delay<1.0) && (delay>0.0)) || (delay<=0.0) ) delay = PCM_DELAY_DEFAULT; + } + + cerr << "Update every "<getTickCount(); + m->getAllCounterStates(SysBeforeState, DummySocketStates, BeforeState); + + if( sysCmd != NULL ) { + MySystem(sysCmd, sysArgv); + } + + while(1) + { + if(!csv) cout << std::flush; + int delay_ms = int(delay * 1000); + int calibrated_delay_ms = delay_ms; +#ifdef _MSC_VER + // compensate slow Windows console output + if(AfterTime) delay_ms -= (int)(m->getTickCount() - BeforeTime); + if(delay_ms < 0) delay_ms = 0; +#else + // compensation of delay on Linux/UNIX + // to make the samling interval as monotone as possible + struct timeval start_ts, end_ts; + if(calibrated == 0) { + gettimeofday(&end_ts, NULL); + diff_usec = (end_ts.tv_sec-start_ts.tv_sec)*1000000.0+(end_ts.tv_usec-start_ts.tv_usec); + calibrated_delay_ms = delay_ms - diff_usec/1000.0; + } +#endif + + MySleepMs(calibrated_delay_ms); + +#ifndef _MSC_VER + calibrated = (calibrated + 1) % PCM_CALIBRATION_INTERVAL; + if(calibrated == 0) { + gettimeofday(&start_ts, NULL); + } +#endif + AfterTime = m->getTickCount(); + m->getAllCounterStates(SysAfterState, DummySocketStates, AfterState); + + cout << "Time elapsed: "<isBlocked() ) { + // in case PCM was blocked after spawning child application: break monitoring loop here + break; + } + } + + exit(EXIT_SUCCESS); +} + diff --git a/pcm-pcie.cpp b/pcm-pcie.cpp new file mode 100644 index 0000000..cdd9847 --- /dev/null +++ b/pcm-pcie.cpp @@ -0,0 +1,896 @@ +/* + Copyright (c) 2009-2013, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Patrick Lu + + +/*! \file pcm-pcie.cpp + \brief Example of using uncore CBo counters: implements a performance counter monitoring utility for monitoring PCIe bandwidth + */ +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include +#ifdef _MSC_VER +#pragma warning(disable : 4996) // for sprintf +#include +#include "../PCM_Win/windriver.h" +#else +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "cpucounters.h" +#include "utils.h" + +#define PCM_DELAY_DEFAULT 1.0 // in seconds +#define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs +#define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration +typedef struct +{ + // PCIe read events (PCI devices reading from memory) + uint64 PCIeRdCur; // PCIe read current + uint64 PCIeNSRd; // PCIe non-snoop read + // PCIe write events (PCI devices writing to memory) + uint64 PCIeWiLF; // PCIe Write (non-allocating) + uint64 PCIeItoM; // PCIe Write (allocating) + uint64 PCIeNSWr; // PCIe Non-snoop write (partial) + uint64 PCIeNSWrF; // PCIe Non-snoop write (full) + // events shared by CPU and IO + uint64 RFO; // Demand Data RFO [PCIe write partial cache line] + uint64 CRd; // Demand Code Read + uint64 DRd; // Demand Data read + uint64 PRd; // Partial Reads (UC) [MMIO Read] + uint64 WiL; // Write Invalidate Line - partial [MMIO write], PL: Not documented in HSX/IVT + uint64 ItoM; // Request Invalidate Line [PCIe write full cache line] +} PCIeEvents_t; + +typedef struct +{ + PCIeEvents_t total; + PCIeEvents_t miss; + PCIeEvents_t hit; +}sample_t; + +PCIeEvents_t aggregate_sample; +uint32 num_events = (sizeof(PCIeEvents_t)/sizeof(uint64)); + +using namespace std; + +const uint32 max_sockets = 4; +void getPCIeEvents(PCM *m, PCM::PCIeEventCode opcode, uint32 delay_ms, sample_t *sample, const uint32 tid=0); + +void print_events() +{ + cerr << " PCIe event definitions (each event counts as a transfer): \n"; + cerr << " PCIe read events (PCI devices reading from memory - application writes to disk/network/PCIe device):\n"; + cerr << " PCIePRd - PCIe UC read transfer (partial cache line)\n"; + cerr << " PCIeRdCur* - PCIe read current transfer (full cache line)\n"; + cerr << " On Haswell Server PCIeRdCur counts both full/partial cache lines\n"; + cerr << " RFO* - Demand Data RFO\n"; + cerr << " CRd* - Demand Code Read\n"; + cerr << " DRd - Demand Data Read\n"; + cerr << " PCIeNSWr - PCIe Non-snoop write transfer (partial cache line)\n"; + cerr << " PRd - MMIO Read [Haswell Server only: PL verify this on IVT] (Partial Cache Line)\n"; + cerr << " PCIe write events (PCI devices writing to memory - application reads from disk/network/PCIe device):\n"; + cerr << " PCIeWiLF - PCIe Write transfer (non-allocating) (full cache line)\n"; + cerr << " PCIeItoM - PCIe Write transfer (allocating) (full cache line)\n"; + cerr << " PCIeNSWr - PCIe Non-snoop write transfer (partial cache line)\n"; + cerr << " PCIeNSWrF - PCIe Non-snoop write transfer (full cache line)\n"; + cerr << " ItoM - PCIe write full cache line\n"; + cerr << " RFO - PCIe parial Write\n"; + cerr << " WiL - MMIO Write (Full/Partial)\n\n"; + cerr << " * - NOTE: Depending on the configuration of your BIOS, this tool may report '0' if the message\n"; + cerr << " has not been selected.\n\n"; +} + +void print_usage(const string progname) +{ + cerr << endl << " Usage: " << endl << " " << progname + << " --help | [delay] [options] [-- external_program [external_program_options]]" << endl; + cerr << " => time interval to sample performance counters." << endl; + cerr << " If not specified, or 0, with external program given" << endl; + cerr << " will read counters only after external program finishes" << endl; + cerr << " Supported are: " << endl; + cerr << " -h | --help | /h => print this help and exit" << endl; + cerr << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or" << endl + << " to a file, in case filename is provided" << endl; + cerr << " -B => Estimate PCIe B/W (in Bytes/sec) by multiplying" << endl; + cerr << " the number of transfers by the cache line size (=64 bytes)." << endl; + cerr << " It overestimates the bandwidth under traffic with many partial cache line transfers." << endl; + cerr << endl; + print_events(); + cerr << endl; + cerr << " Examples:" << endl; + cerr << " " << progname << " 1 => print counters every second without core and socket output" << endl; + cerr << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format" << endl; + cerr << " " << progname << " /csv 5 2>/dev/null => one sampe every 5 seconds, and discard all diagnostic output" << endl; + cerr << endl; +} + + +int main(int argc, char * argv[]) +{ + set_signal_handlers(); + +#ifdef PCM_FORCE_SILENT + null_stream nullStream1, nullStream2; + std::cout.rdbuf(&nullStream1); + std::cerr.rdbuf(&nullStream2); +#endif + + cerr << endl; + cerr << " Intel(r) Performance Counter Monitor: PCIe Bandwidth Monitoring Utility "<< endl; + cerr << endl; + cerr << " Copyright (c) 2013-2014 Intel Corporation" << endl; + cerr << " This utility measures PCIe bandwidth in real-time" << endl; + cerr << endl; + print_events(); + + double delay = -1.0; + bool csv = false; + bool print_bandwidth = false; + bool print_additional_info = false; + char * sysCmd = NULL; + char ** sysArgv = NULL; + string program = string(argv[0]); + + PCM * m = PCM::getInstance(); + + if(argc > 1) do + { + argv++; + argc--; + if (strncmp(*argv, "--help", 6) == 0 || + strncmp(*argv, "-h", 2) == 0 || + strncmp(*argv, "/h", 2) == 0) + { + print_usage(program); + exit(EXIT_FAILURE); + } + else + if (strncmp(*argv, "-csv",4) == 0 || + strncmp(*argv, "/csv",4) == 0) + { + csv = true; + string cmd = string(*argv); + size_t found = cmd.find('=',4); + if (found != string::npos) { + string filename = cmd.substr(found+1); + if (!filename.empty()) { + m->setOutput(filename); + } + } + continue; + } + else + if (strncmp(*argv, "-B", 2) == 0 || + strncmp(*argv, "/b", 2) == 0) + { + print_bandwidth = true; + continue; + } + else + if (strncmp(*argv, "-e", 2) == 0 ) + { + print_additional_info = true; + continue; + } + else + if (strncmp(*argv, "--", 2) == 0) + { + argv++; + sysCmd = *argv; + sysArgv = argv; + break; + } + else + { + // any other options positional that is a floating point number is treated as , + // while the other options are ignored with a warning issues to stderr + double delay_input; + std::istringstream is_str_stream(*argv); + is_str_stream >> noskipws >> delay_input; + if(is_str_stream.eof() && !is_str_stream.fail()) { + delay = delay_input; + } else { + cerr << "WARNING: unknown command-line option: \"" << *argv << "\". Ignoring it." << endl; + print_usage(program); + exit(EXIT_FAILURE); + } + continue; + } + } while(argc > 1); // end of command line partsing loop + + m->disableJKTWorkaround(); + PCM::ErrorCode status = m->program(); + switch (status) + { + case PCM::Success: + break; + case PCM::MSRAccessDenied: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (no MSR or PCI CFG space access)." << endl; + exit(EXIT_FAILURE); + case PCM::PMUBusy: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Performance Monitoring Unit is occupied by other application). Try to stop the application that uses PMU." << endl; + cerr << "Alternatively you can try to reset PMU configuration at your own risk. Try to reset? (y/n)" << endl; + char yn; + std::cin >> yn; + if ('y' == yn) + { + m->resetPMU(); + cerr << "PMU configuration has been reset. Try to rerun the program again." << endl; + } + exit(EXIT_FAILURE); + default: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Unknown error)." << endl; + exit(EXIT_FAILURE); + } + + cerr << "\nDetected "<< m->getCPUBrandString() << " \"Intel(r) microarchitecture codename "<getUArchCodename()<<"\""<hasPCICFGUncore())) + { + cerr << "Jaketown, Ivytown, Haswell Server CPU is required for this tool! Program aborted" << endl; + exit(EXIT_FAILURE); + } + + if(m->getNumSockets() > max_sockets) + { + cerr << "Only systems with up to "<getNumCores() != m->getNumOnlineCores()) + { + cerr << "Core offlining is not supported yet. Program aborted" << endl; + exit(EXIT_FAILURE); + } + + if ( (sysCmd != NULL) && (delay<=0.0) ) { + // in case external command is provided in command line, and + // delay either not provided (-1) or is zero + m->setBlocked(true); + } else { + m->setBlocked(false); + } + + if (csv) { + if( delay<=0.0 ) delay = PCM_DELAY_DEFAULT; + } else { + // for non-CSV mode delay < 1.0 does not make a lot of practical sense: + // hard to read from the screen, or + // in case delay is not provided in command line => set default + if( ((delay<1.0) && (delay>0.0)) || (delay<=0.0) ) delay = PCM_DELAY_DEFAULT; + } + + cerr << "Update every "<getCPUModel() == PCM::HASWELLX) // Haswell Server + { + for(i=0;iPCIeRdCur, delay_ms, sample); + getPCIeEvents(m, m->RFO, delay_ms, sample,m->RFOtid); + getPCIeEvents(m, m->CRd, delay_ms, sample); + getPCIeEvents(m, m->DRd, delay_ms, sample); + getPCIeEvents(m, m->ItoM, delay_ms, sample,m->ItoMtid); + getPCIeEvents(m, m->PRd, delay_ms, sample); + getPCIeEvents(m, m->WiL, delay_ms, sample); + } + + if(csv) + if(print_bandwidth) + cout << "Skt,PCIeRdCur,RFO,CRd,DRd,ItoM,PRd,WiL,PCIe Rd (B),PCIe Wr (B)\n"; + else + cout << "Skt,PCIeRdCur,RFO,CRd,DRd,ItoM,PRd,WiL\n"; + else + if(print_bandwidth) + cout << "Skt | PCIeRdCur | RFO | CRd | DRd | ItoM | PRd | WiL | PCIe Rd (B) | PCIe Wr (B)\n"; + else + cout << "Skt | PCIeRdCur | RFO | CRd | DRd | ItoM | PRd | WiL\n"; + + //report extrapolated read and write PCIe bandwidth per socket using the data from the sample + for(i=0; igetNumSockets(); ++i) + { + if(csv) + { + cout << i; + cout << "," << sample[i].total.PCIeRdCur; + cout << "," << sample[i].total.RFO; + cout << "," << sample[i].total.CRd; + cout << "," << sample[i].total.DRd; + cout << "," << sample[i].total.ItoM; + cout << "," << sample[i].total.PRd; + cout << "," << sample[i].total.WiL; + if(print_bandwidth) + { + cout << "," << ((sample[i].total.PCIeRdCur + sample[i].total.RFO + sample[i].total.CRd + sample[i].total.DRd)*64ULL); + cout << "," << ((sample[i].total.ItoM + sample[i].total.RFO)*64ULL); + } + cout << " (Total)\n"; + + cout << i; + cout << "," << sample[i].miss.PCIeRdCur; + cout << "," << sample[i].miss.RFO; + cout << "," << sample[i].miss.CRd; + cout << "," << sample[i].miss.DRd; + cout << "," << sample[i].miss.ItoM; + cout << "," << sample[i].miss.PRd; + cout << "," << sample[i].miss.WiL; + if(print_bandwidth) + { + cout << "," << ((sample[i].miss.PCIeRdCur + sample[i].miss.RFO + sample[i].miss.CRd + sample[i].miss.DRd)*64ULL); + cout << "," << ((sample[i].miss.ItoM + sample[i].miss.RFO)*64ULL); + } + cout << " (Miss)\n"; + + cout << i; + cout << "," << sample[i].hit.PCIeRdCur; + cout << "," << sample[i].hit.RFO; + cout << "," << sample[i].hit.CRd; + cout << "," << sample[i].hit.DRd; + cout << "," << sample[i].hit.ItoM; + cout << "," << sample[i].hit.PRd; + cout << "," << sample[i].hit.WiL; + if(print_bandwidth) + { + cout << "," << ((sample[i].hit.PCIeRdCur + sample[i].hit.RFO + sample[i].hit.CRd + sample[i].hit.DRd)*64ULL); + cout << "," << ((sample[i].hit.ItoM + sample[i].hit.RFO)*64ULL); + } + cout << " (Hit)\n"; + + + } + else + { + cout << " " << i; + cout << " " << unit_format(sample[i].total.PCIeRdCur); + cout << " " << unit_format(sample[i].total.RFO); + cout << " " << unit_format(sample[i].total.CRd); + cout << " " << unit_format(sample[i].total.DRd); + cout << " " << unit_format(sample[i].total.ItoM); + cout << " " << unit_format(sample[i].total.PRd); + cout << " " << unit_format(sample[i].total.WiL); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].total.PCIeRdCur + sample[i].total.RFO + sample[i].total.CRd + sample[i].total.DRd)*64ULL); + cout << " " << unit_format((sample[i].total.ItoM + sample[i].total.RFO)*64ULL); + } + cout << " (Total)\n"; + + cout << " " << i; + cout << " " << unit_format(sample[i].miss.PCIeRdCur); + cout << " " << unit_format(sample[i].miss.RFO); + cout << " " << unit_format(sample[i].miss.CRd); + cout << " " << unit_format(sample[i].miss.DRd); + cout << " " << unit_format(sample[i].miss.ItoM); + cout << " " << unit_format(sample[i].miss.PRd); + cout << " " << unit_format(sample[i].miss.WiL); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].miss.PCIeRdCur + sample[i].miss.RFO + sample[i].miss.CRd + sample[i].miss.DRd)*64ULL); + cout << " " << unit_format((sample[i].miss.ItoM + sample[i].miss.RFO)*64ULL); + } + cout << " (Miss)\n"; + + cout << " " << i; + cout << " " << unit_format(sample[i].hit.PCIeRdCur); + cout << " " << unit_format(sample[i].hit.RFO); + cout << " " << unit_format(sample[i].hit.CRd); + cout << " " << unit_format(sample[i].hit.DRd); + cout << " " << unit_format(sample[i].hit.ItoM); + cout << " " << unit_format(sample[i].hit.PRd); + cout << " " << unit_format(sample[i].hit.WiL); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].hit.PCIeRdCur + sample[i].hit.RFO + sample[i].hit.CRd + sample[i].hit.DRd)*64ULL); + cout << " " << unit_format((sample[i].hit.ItoM + sample[i].hit.RFO)*64ULL); + } + cout << " (Hit)\n"; + } + } + if(!csv) + { + if(print_bandwidth) + cout << "----------------------------------------------------------------------------------------------------\n"; + else + cout << "-----------------------------------------------------------------------\n"; + cout << " * "; + cout << " " << unit_format(aggregate_sample.PCIeRdCur); + cout << " " << unit_format(aggregate_sample.RFO); + cout << " " << unit_format(aggregate_sample.CRd); + cout << " " << unit_format(aggregate_sample.DRd); + cout << " " << unit_format(aggregate_sample.ItoM); + cout << " " << unit_format(aggregate_sample.PRd); + cout << " " << unit_format(aggregate_sample.WiL); + if(print_bandwidth) + { + cout << " " << unit_format((aggregate_sample.PCIeRdCur + aggregate_sample.CRd + aggregate_sample.DRd + aggregate_sample.RFO)*64ULL); + cout << " " << unit_format((aggregate_sample.ItoM + aggregate_sample.RFO)*64ULL); + } + cout << " (Aggregate)\n\n"; + } + } + else // Ivytown and Older Architectures + { + for(i=0;iPCIeRdCur, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeNSRd, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeWiLF, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeItoM, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeNSWr, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeNSWrF, delay_ms, sample,0); + } + + if(csv) + if(print_bandwidth) + cout << "Skt,PCIeRdCur,PCIeNSRd,PCIeWiLF,PCIeItoM,PCIeNSWr,PCIeNSWrF,PCIe Rd (B),PCIe Wr (B)\n"; + else + cout << "Skt,PCIeRdCur,PCIeNSRd,PCIeWiLF,PCIeItoM,PCIeNSWr,PCIeNSWrF\n"; + else + if(print_bandwidth) + cout << "Skt | PCIeRdCur | PCIeNSRd | PCIeWiLF | PCIeItoM | PCIeNSWr | PCIeNSWrF | PCIe Rd (B) | PCIe Wr (B)\n"; + else + cout << "Skt | PCIeRdCur | PCIeNSRd | PCIeWiLF | PCIeItoM | PCIeNSWr | PCIeNSWrF\n"; + + //report extrapolated read and write PCIe bandwidth per socket using the data from the sample + for(i=0; igetNumSockets(); ++i) + { + if(csv) + { + cout << i; + cout << "," << sample[i].total.PCIeRdCur; + cout << "," << sample[i].total.PCIeNSWr; + cout << "," << sample[i].total.PCIeWiLF; + cout << "," << sample[i].total.PCIeItoM; + cout << "," << sample[i].total.PCIeNSWr; + cout << "," << sample[i].total.PCIeNSWrF; + if(print_bandwidth) + { + cout << "," << ((sample[i].total.PCIeRdCur+ sample[i].total.PCIeNSWr)*64ULL); + cout << "," << ((sample[i].total.PCIeWiLF+sample[i].total.PCIeItoM+sample[i].total.PCIeNSWr+sample[i].total.PCIeNSWrF)*64ULL); + } + cout << " (Total)\n"; + + cout << i; + cout << "," << sample[i].miss.PCIeRdCur; + cout << "," << sample[i].miss.PCIeNSWr; + cout << "," << sample[i].miss.PCIeWiLF; + cout << "," << sample[i].miss.PCIeItoM; + cout << "," << sample[i].miss.PCIeNSWr; + cout << "," << sample[i].miss.PCIeNSWrF; + if(print_bandwidth) + { + cout << "," << ((sample[i].miss.PCIeRdCur+ sample[i].miss.PCIeNSWr)*64ULL); + cout << "," << ((sample[i].miss.PCIeWiLF+sample[i].miss.PCIeItoM+sample[i].miss.PCIeNSWr+sample[i].miss.PCIeNSWrF)*64ULL); + } + cout << " (Miss)\n"; + + cout << i; + cout << "," << sample[i].hit.PCIeRdCur; + cout << "," << sample[i].hit.PCIeNSWr; + cout << "," << sample[i].hit.PCIeWiLF; + cout << "," << sample[i].hit.PCIeItoM; + cout << "," << sample[i].hit.PCIeNSWr; + cout << "," << sample[i].hit.PCIeNSWrF; + if(print_bandwidth) + { + cout << "," << ((sample[i].hit.PCIeRdCur+ sample[i].hit.PCIeNSWr)*64ULL); + cout << "," << ((sample[i].hit.PCIeWiLF+sample[i].hit.PCIeItoM+sample[i].hit.PCIeNSWr+sample[i].hit.PCIeNSWrF)*64ULL); + } + cout << " (Hit)\n"; + + + + + } + else + { + cout << " " << i; + cout << " " << unit_format(sample[i].total.PCIeRdCur); + cout << " " << unit_format(sample[i].total.PCIeNSWr); + cout << " " << unit_format(sample[i].total.PCIeWiLF); + cout << " " << unit_format(sample[i].total.PCIeItoM); + cout << " " << unit_format(sample[i].total.PCIeNSWr); + cout << " " << unit_format(sample[i].total.PCIeNSWrF); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].total.PCIeRdCur+ sample[i].total.PCIeNSWr)*64ULL); + cout << " " << unit_format((sample[i].total.PCIeWiLF+sample[i].total.PCIeItoM+sample[i].total.PCIeNSWr+sample[i].total.PCIeNSWrF)*64ULL); + } + cout << " (Total)\n"; + + cout << " " << i; + cout << " " << unit_format(sample[i].miss.PCIeRdCur); + cout << " " << unit_format(sample[i].miss.PCIeNSWr); + cout << " " << unit_format(sample[i].miss.PCIeWiLF); + cout << " " << unit_format(sample[i].miss.PCIeItoM); + cout << " " << unit_format(sample[i].miss.PCIeNSWr); + cout << " " << unit_format(sample[i].miss.PCIeNSWrF); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].miss.PCIeRdCur+ sample[i].miss.PCIeNSWr)*64ULL); + cout << " " << unit_format((sample[i].miss.PCIeWiLF+sample[i].miss.PCIeItoM+sample[i].miss.PCIeNSWr+sample[i].miss.PCIeNSWrF)*64ULL); + } + cout << " (Miss)\n"; + + cout << " " << i; + cout << " " << unit_format(sample[i].hit.PCIeRdCur); + cout << " " << unit_format(sample[i].hit.PCIeNSWr); + cout << " " << unit_format(sample[i].hit.PCIeWiLF); + cout << " " << unit_format(sample[i].hit.PCIeItoM); + cout << " " << unit_format(sample[i].hit.PCIeNSWr); + cout << " " << unit_format(sample[i].hit.PCIeNSWrF); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].hit.PCIeRdCur+ sample[i].hit.PCIeNSWr)*64ULL); + cout << " " << unit_format((sample[i].hit.PCIeWiLF+sample[i].hit.PCIeItoM+sample[i].hit.PCIeNSWr+sample[i].hit.PCIeNSWrF)*64ULL); + } + cout << " (Hit)\n"; + } + } + if(!csv) + { + if(print_bandwidth) + cout << "----------------------------------------------------------------------------------------------------------------\n"; + else + cout << "-----------------------------------------------------------------------------------\n"; + cout << " * "; + cout << " " << unit_format(aggregate_sample.PCIeRdCur); + cout << " " << unit_format(aggregate_sample.PCIeNSWr); + cout << " " << unit_format(aggregate_sample.PCIeWiLF); + cout << " " << unit_format(aggregate_sample.PCIeItoM); + cout << " " << unit_format(aggregate_sample.PCIeNSWr); + cout << " " << unit_format(aggregate_sample.PCIeNSWrF); + if(print_bandwidth) + { + cout << " " << unit_format((aggregate_sample.PCIeRdCur+ aggregate_sample.PCIeNSWr)*64ULL); + cout << " " << unit_format((aggregate_sample.PCIeWiLF+aggregate_sample.PCIeItoM+aggregate_sample.PCIeNSWr+aggregate_sample.PCIeNSWrF)*64ULL); + } + } + cout << " (Aggregate)\n\n"; + } + if ( m->isBlocked() ) { + // in case PCM was blocked after spawning child application: break monitoring loop here + break; + } + } + + } + + + // default case + else if ( print_additional_info == false) + { + + while(1) + { + MySleepMs(delay_ms); + memset(sample,0,sizeof(sample)); + memset(&aggregate_sample,0,sizeof(aggregate_sample)); + + if(m->getCPUModel() == PCM::HASWELLX) // Haswell Server + { + for(i=0;iPCIeRdCur, delay_ms, sample); + getPCIeEvents(m, m->RFO, delay_ms, sample,m->RFOtid); + getPCIeEvents(m, m->CRd, delay_ms, sample); + getPCIeEvents(m, m->DRd, delay_ms, sample); + getPCIeEvents(m, m->ItoM, delay_ms, sample,m->ItoMtid); + getPCIeEvents(m, m->PRd, delay_ms, sample); + getPCIeEvents(m, m->WiL, delay_ms, sample); + } + + if(csv) + if(print_bandwidth) + cout << "Skt,PCIeRdCur,RFO,CRd,DRd,ItoM,PRd,WiL,PCIe Rd (B),PCIe Wr (B)\n"; + else + cout << "Skt,PCIeRdCur,RFO,CRd,DRd,ItoM,PRd,WiL\n"; + else + if(print_bandwidth) + cout << "Skt | PCIeRdCur | RFO | CRd | DRd | ItoM | PRd | WiL | PCIe Rd (B) | PCIe Wr (B)\n"; + else + cout << "Skt | PCIeRdCur | RFO | CRd | DRd | ItoM | PRd | WiL\n"; + + //report extrapolated read and write PCIe bandwidth per socket using the data from the sample + for(i=0; igetNumSockets(); ++i) + { + if(csv) + { + cout << i; + cout << "," << sample[i].total.PCIeRdCur; + cout << "," << sample[i].total.RFO; + cout << "," << sample[i].total.CRd; + cout << "," << sample[i].total.DRd; + cout << "," << sample[i].total.ItoM; + cout << "," << sample[i].total.PRd; + cout << "," << sample[i].total.WiL; + if(print_bandwidth) + { + cout << "," << ((sample[i].total.PCIeRdCur + sample[i].total.RFO + sample[i].total.CRd + sample[i].total.DRd)*64ULL); + cout << "," << ((sample[i].total.ItoM + sample[i].total.RFO)*64ULL); + } + cout << "\n"; + } + else + { + cout << " " << i; + cout << " " << unit_format(sample[i].total.PCIeRdCur); + cout << " " << unit_format(sample[i].total.RFO); + cout << " " << unit_format(sample[i].total.CRd); + cout << " " << unit_format(sample[i].total.DRd); + cout << " " << unit_format(sample[i].total.ItoM); + cout << " " << unit_format(sample[i].total.PRd); + cout << " " << unit_format(sample[i].total.WiL); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].total.PCIeRdCur + sample[i].total.RFO + sample[i].total.CRd + sample[i].total.DRd)*64ULL); + cout << " " << unit_format((sample[i].total.ItoM + sample[i].total.RFO)*64ULL); + } + cout << "\n"; + } + } + if(!csv) + { + if(print_bandwidth) + cout << "----------------------------------------------------------------------------------------------------\n"; + else + cout << "-----------------------------------------------------------------------\n"; + cout << " * "; + cout << " " << unit_format(aggregate_sample.PCIeRdCur); + cout << " " << unit_format(aggregate_sample.RFO); + cout << " " << unit_format(aggregate_sample.CRd); + cout << " " << unit_format(aggregate_sample.DRd); + cout << " " << unit_format(aggregate_sample.ItoM); + cout << " " << unit_format(aggregate_sample.PRd); + cout << " " << unit_format(aggregate_sample.WiL); + if(print_bandwidth) + { + cout << " " << unit_format((aggregate_sample.PCIeRdCur + aggregate_sample.CRd + aggregate_sample.DRd + aggregate_sample.RFO)*64ULL); + cout << " " << unit_format((aggregate_sample.ItoM + aggregate_sample.RFO)*64ULL); + } + cout << "\n\n"; + } + } + else // Ivytown and Older Architectures + { + for(i=0;iPCIeRdCur, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeNSRd, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeWiLF, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeItoM, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeNSWr, delay_ms, sample,0); + getPCIeEvents(m, m->PCIeNSWrF, delay_ms, sample,0); + } + + if(csv) + if(print_bandwidth) + cout << "Skt,PCIeRdCur,PCIeNSRd,PCIeWiLF,PCIeItoM,PCIeNSWr,PCIeNSWrF,PCIe Rd (B),PCIe Wr (B)\n"; + else + cout << "Skt,PCIeRdCur,PCIeNSRd,PCIeWiLF,PCIeItoM,PCIeNSWr,PCIeNSWrF\n"; + else + if(print_bandwidth) + cout << "Skt | PCIeRdCur | PCIeNSRd | PCIeWiLF | PCIeItoM | PCIeNSWr | PCIeNSWrF | PCIe Rd (B) | PCIe Wr (B)\n"; + else + cout << "Skt | PCIeRdCur | PCIeNSRd | PCIeWiLF | PCIeItoM | PCIeNSWr | PCIeNSWrF\n"; + + //report extrapolated read and write PCIe bandwidth per socket using the data from the sample + for(i=0; igetNumSockets(); ++i) + { + if(csv) + { + cout << i; + cout << "," << sample[i].total.PCIeRdCur; + cout << "," << sample[i].total.PCIeNSWr; + cout << "," << sample[i].total.PCIeWiLF; + cout << "," << sample[i].total.PCIeItoM; + cout << "," << sample[i].total.PCIeNSWr; + cout << "," << sample[i].total.PCIeNSWrF; + if(print_bandwidth) + { + cout << "," << ((sample[i].total.PCIeRdCur+ sample[i].total.PCIeNSWr)*64ULL); + cout << "," << ((sample[i].total.PCIeWiLF+sample[i].total.PCIeItoM+sample[i].total.PCIeNSWr+sample[i].total.PCIeNSWrF)*64ULL); + } + cout << "\n"; + } + else + { + cout << " " << i; + cout << " " << unit_format(sample[i].total.PCIeRdCur); + cout << " " << unit_format(sample[i].total.PCIeNSWr); + cout << " " << unit_format(sample[i].total.PCIeWiLF); + cout << " " << unit_format(sample[i].total.PCIeItoM); + cout << " " << unit_format(sample[i].total.PCIeNSWr); + cout << " " << unit_format(sample[i].total.PCIeNSWrF); + if(print_bandwidth) + { + cout << " " << unit_format((sample[i].total.PCIeRdCur+ sample[i].total.PCIeNSWr)*64ULL); + cout << " " << unit_format((sample[i].total.PCIeWiLF+sample[i].total.PCIeItoM+sample[i].total.PCIeNSWr+sample[i].total.PCIeNSWrF)*64ULL); + } + cout << "\n"; + } + } + if(!csv) + { + if(print_bandwidth) + cout << "----------------------------------------------------------------------------------------------------------------\n"; + else + cout << "-----------------------------------------------------------------------------------\n"; + cout << " * "; + cout << " " << unit_format(aggregate_sample.PCIeRdCur); + cout << " " << unit_format(aggregate_sample.PCIeNSWr); + cout << " " << unit_format(aggregate_sample.PCIeWiLF); + cout << " " << unit_format(aggregate_sample.PCIeItoM); + cout << " " << unit_format(aggregate_sample.PCIeNSWr); + cout << " " << unit_format(aggregate_sample.PCIeNSWrF); + if(print_bandwidth) + { + cout << " " << unit_format((aggregate_sample.PCIeRdCur+ aggregate_sample.PCIeNSWr)*64ULL); + cout << " " << unit_format((aggregate_sample.PCIeWiLF+aggregate_sample.PCIeItoM+aggregate_sample.PCIeNSWr+aggregate_sample.PCIeNSWrF)*64ULL); + } + } + cout << "\n\n"; + } + if ( m->isBlocked() ) { + // in case PCM was blocked after spawning child application: break monitoring loop here + break; + } + } + + } + + // ================================== End Printing Output ================================== + + exit(EXIT_SUCCESS); +} + +void getPCIeEvents(PCM *m, PCM::PCIeEventCode opcode, uint32 delay_ms, sample_t *sample, const uint32 tid) +{ + PCIeCounterState * before = new PCIeCounterState[m->getNumSockets()]; + PCIeCounterState * after = new PCIeCounterState[m->getNumSockets()]; + PCIeCounterState * before2 = new PCIeCounterState[m->getNumSockets()]; + PCIeCounterState * after2 = new PCIeCounterState[m->getNumSockets()]; + uint32 i; + + m->programPCIeCounters(opcode, tid); + for(i=0; igetNumSockets(); ++i) + before[i] = m->getPCIeCounterState(i); + MySleepUs(delay_ms*1000); + for(i=0; igetNumSockets(); ++i) + after[i] = m->getPCIeCounterState(i); + + m->programPCIeMissCounters(opcode, tid); + for(i=0; igetNumSockets(); ++i) + before2[i] = m->getPCIeCounterState(i); + MySleepUs(delay_ms*1000); + for(i=0; igetNumSockets(); ++i) + after2[i] = m->getPCIeCounterState(i); + + for(i=0; igetNumSockets(); ++i) + { + switch(opcode) + { + case PCM::PCIeRdCur: + sample[i].total.PCIeRdCur += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.PCIeRdCur += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.PCIeRdCur += (sample[i].total.PCIeRdCur > sample[i].miss.PCIeRdCur) ? sample[i].total.PCIeRdCur - sample[i].miss.PCIeRdCur : 0; + aggregate_sample.PCIeRdCur += sample[i].total.PCIeRdCur; + break; + case PCM::PCIeNSRd: + sample[i].total.PCIeNSRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.PCIeNSRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.PCIeNSRd += (sample[i].total.PCIeNSRd > sample[i].miss.PCIeNSRd) ? sample[i].total.PCIeNSRd - sample[i].miss.PCIeNSRd : 0; + aggregate_sample.PCIeNSRd += sample[i].total.PCIeNSRd; + break; + case PCM::PCIeWiLF: + sample[i].total.PCIeWiLF += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.PCIeWiLF += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.PCIeWiLF += (sample[i].total.PCIeWiLF > sample[i].miss.PCIeWiLF) ? sample[i].total.PCIeWiLF - sample[i].miss.PCIeWiLF : 0; + aggregate_sample.PCIeWiLF += sample[i].total.PCIeWiLF; + break; + case PCM::PCIeItoM: + sample[i].total.PCIeItoM += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.PCIeItoM += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.PCIeItoM += (sample[i].total.PCIeItoM > sample[i].miss.PCIeItoM) ? sample[i].total.PCIeItoM - sample[i].miss.PCIeItoM : 0; + aggregate_sample.PCIeItoM += sample[i].total.PCIeItoM; + break; + case PCM::PCIeNSWr: + sample[i].total.PCIeNSWr += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.PCIeNSWr += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.PCIeNSWr += (sample[i].total.PCIeNSWr > sample[i].miss.PCIeNSWr) ? sample[i].total.PCIeNSWr - sample[i].miss.PCIeNSWr : 0; + aggregate_sample.PCIeItoM += sample[i].total.PCIeItoM; + break; + case PCM::PCIeNSWrF: + sample[i].total.PCIeNSWrF += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.PCIeNSWrF += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.PCIeNSWrF += (sample[i].total.PCIeNSWrF > sample[i].miss.PCIeNSWrF) ? sample[i].total.PCIeNSWrF - sample[i].miss.PCIeNSWrF : 0; + aggregate_sample.PCIeNSWrF += sample[i].total.PCIeNSWrF; + break; + case PCM::RFO: + if(tid == PCM::RFOtid) //Use tid to filter only PCIe traffic + { + sample[i].total.RFO += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.RFO += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.RFO += (sample[i].total.RFO > sample[i].miss.RFO) ? sample[i].total.RFO - sample[i].miss.RFO : 0; + aggregate_sample.RFO += sample[i].total.RFO; + } + break; + case PCM::ItoM: + if(tid == PCM::ItoMtid) //Use tid to filter only PCIe traffic + { + sample[i].total.ItoM += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.ItoM += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.ItoM += (sample[i].total.ItoM > sample[i].miss.ItoM) ? sample[i].total.ItoM - sample[i].miss.ItoM : 0; + aggregate_sample.ItoM += sample[i].total.ItoM; + } + break; + case PCM::WiL: + sample[i].total.WiL += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.WiL += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.WiL += (sample[i].total.WiL > sample[i].miss.WiL) ? sample[i].total.WiL - sample[i].miss.WiL : 0; + aggregate_sample.WiL += sample[i].total.WiL; + break; + case PCM::PRd: + sample[i].total.PRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.PRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after[i]); + sample[i].hit.PRd += (sample[i].total.PRd > sample[i].miss.PRd) ? sample[i].total.PRd - sample[i].miss.PRd : 0; + aggregate_sample.PRd += sample[i].total.PRd; + break; + case PCM::CRd: + sample[i].total.CRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.CRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.CRd += (sample[i].total.CRd > sample[i].miss.CRd) ? sample[i].total.CRd - sample[i].miss.CRd : 0; + aggregate_sample.CRd += sample[i].total.CRd; + break; + case PCM::DRd: + sample[i].total.DRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before[i], after[i]); + sample[i].miss.DRd += (sizeof(PCIeEvents_t)/sizeof(uint64)) * getNumberOfEvents(before2[i], after2[i]); + sample[i].hit.DRd += (sample[i].total.DRd > sample[i].miss.DRd) ? sample[i].total.DRd - sample[i].miss.DRd : 0; + aggregate_sample.DRd += sample[i].total.DRd; + break; + } + } + + delete[] before; + delete[] after; +} diff --git a/pcm-power.cpp b/pcm-power.cpp new file mode 100644 index 0000000..5393eda --- /dev/null +++ b/pcm-power.cpp @@ -0,0 +1,516 @@ +/* +Copyright (c) 2009-2014, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// written by Roman Dementiev +// added PPD cycles by Thomas Willhalm + +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include "cpucounters.h" +#ifdef _MSC_VER +#pragma warning(disable : 4996) // for sprintf +#include +#include "../PCM_Win/windriver.h" +#else +#include +#include +#include // for gettimeofday() +#endif +#include +#include +#include +#ifdef _MSC_VER +#include "freegetopt/getopt.h" +#endif +#include "utils.h" + +#define PCM_DELAY_DEFAULT 1.0 // in seconds +#define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs +#define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration + +using namespace std; + +int getFirstRank(int imc_profile) +{ + return imc_profile*2; +} +int getSecondRank(int imc_profile) +{ + return (imc_profile*2)+1; +} + +double getCKEOffResidency(uint32 channel, uint32 rank, const ServerUncorePowerState & before, const ServerUncorePowerState & after) +{ + return double(getMCCounter(channel,(rank&1)?2:0,before,after))/double(getDRAMClocks(channel,before,after)); +} + +int64 getCKEOffAverageCycles(uint32 channel, uint32 rank, const ServerUncorePowerState & before, const ServerUncorePowerState & after) +{ + uint64 div = getMCCounter(channel,(rank&1)?3:1,before,after); + if(div) + return getMCCounter(channel,(rank&1)?2:0,before,after)/div; + + return -1; +} + +int64 getCyclesPerTransition(uint32 channel, uint32 rank, const ServerUncorePowerState & before, const ServerUncorePowerState & after) +{ + uint64 div = getMCCounter(channel,(rank&1)?3:1,before,after); + if(div) + return getDRAMClocks(channel,before,after)/div; + + return -1; +} + +uint64 getSelfRefreshCycles(uint32 channel, const ServerUncorePowerState & before, const ServerUncorePowerState & after) +{ + return getMCCounter(channel,0,before,after); +} + +uint64 getSelfRefreshTransitions(uint32 channel, const ServerUncorePowerState & before, const ServerUncorePowerState & after) +{ + return getMCCounter(channel,1,before,after); +} + +uint64 getPPDCycles(uint32 channel, const ServerUncorePowerState & before, const ServerUncorePowerState & after) +{ + return getMCCounter(channel,2,before,after); +} + +double getNormalizedPCUCounter(uint32 counter, const ServerUncorePowerState & before, const ServerUncorePowerState & after) +{ + return double(getPCUCounter(counter, before, after))/double(getPCUClocks(before,after)); +} + +double getNormalizedPCUCounter(uint32 counter, const ServerUncorePowerState & before, const ServerUncorePowerState & after, PCM * m) +{ + const uint64 PCUClocks = (m->getPCUFrequency()*getInvariantTSC(before, after))/m->getNominalFrequency(); + //std::cout << "PCM Debug: PCU clocks "<< PCUClocks << std::endl; + return double(getPCUCounter(counter, before, after))/double(PCUClocks); +} + +int default_freq_band[3] = {12,20,40}; +int freq_band[3]; + +void print_usage(const string progname) +{ + cerr << endl << " Usage: " << endl << " " << progname + << " --help | [delay] [options] [-- external_program [external_program_options]]" << endl; + cerr << " => time interval to sample performance counters." << endl; + cerr << " If not specified, or 0, with external program given" << endl; + cerr << " will read counters only after external program finishes" << endl; + cerr << " Supported are: " << endl; + cerr << " -h | --help | /h => print this help and exit" << endl; +// cerr << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or" << endl +// << " to a file, in case filename is provided" << endl; + cerr << " [-m imc_profile] [-p pcu_profile] [-a freq_band0] [-b freq_band1] [-c freq_band2]" << endl << endl; + cerr << " Where: imc_profile, pcu_profile, freq_band0, freq_band1 and freq_band2 are the following:" << endl; + cerr << " - profile (counter group) for IMC PMU. Possible values are: 0,1,2,3,4,-1 \n"; + cerr << " profile 0 - rank 0 and rank 1 residencies (default) \n"; + cerr << " profile 1 - rank 2 and rank 3 residencies \n"; + cerr << " profile 2 - rank 4 and rank 5 residencies \n"; + cerr << " profile 3 - rank 6 and rank 7 residencies \n"; + cerr << " profile 4 - self-refresh residencies \n"; + cerr << " profile -1 - omit IMC PMU output\n"; + cerr << " - profile (counter group) for PCU PMU. Possible values are: 0,1,2,3,4,5,-1 \n"; + cerr << " profile 0 - frequency residencies (default) \n"; + cerr << " profile 1 - core C-state residencies. The unit is the number of physical cores on the socket who were in C0, C3 or C6 during the measurement interval (e.g. 'C0 residency is 3.5' means on average 3.5 physical cores were resident in C0 state)\n"; + cerr << " profile 2 - Prochot (throttled) residencies and thermal frequency limit cycles \n"; + cerr << " profile 3 - {Thermal,Power,Clipped} frequency limit cycles \n"; + cerr << " profile 4 - {OS,Power,Clipped} frequency limit cycles \n"; + cerr << " profile 5 - frequency transition statistics \n"; + cerr << " profile 6 - package C-states residency and transition statistics \n"; + cerr << " profile 7 - UFS transition statistics (1) \n"; + cerr << " profile 8 - UFS transition statistics (2) \n"; + cerr << " profile -1 - omit PCU PMU output\n"; + cerr << " - frequency minumum for band 0 for PCU frequency residency profile [in 100MHz units] (default is "<< + default_freq_band[0] <<"= "<< 100*default_freq_band[0] <<"MHz)\n"; + cerr << " - frequency minumum for band 1 for PCU frequency residency profile [in 100MHz units] (default is "<< + default_freq_band[1] <<"= "<< 100*default_freq_band[1]<<"MHz)\n"; + cerr << " - frequency minumum for band 2 for PCU frequency residency profile [in 100MHz units] (default is "<< + default_freq_band[2] <<"= "<< 100*default_freq_band[2]<<"MHz)\n"; + cerr << endl; +} + +int main(int argc, char * argv[]) +{ + set_signal_handlers(); + + std::cerr << "\n Intel(r) Performance Counter Monitor " << INTEL_PCM_VERSION << std::endl; + std::cerr << "\n Power Monitoring Utility\n Copyright (c) 2011-2014 Intel Corporation\n"; + + int imc_profile = 0; + int pcu_profile = 0; + double delay = -1.0; + char *sysCmd = NULL; + char **sysArgv = NULL; + + freq_band[0] = default_freq_band[0]; + freq_band[1] = default_freq_band[1]; + freq_band[2] = default_freq_band[2]; + + bool csv = false; + long diff_usec = 0; // deviation of clock is useconds between measurements + int calibrated = PCM_CALIBRATION_INTERVAL - 2; // keeps track is the clock calibration needed + string program = string(argv[0]); + + PCM * m = PCM::getInstance(); + + if(argc > 1) do + { + argv++; + argc--; + if (strncmp(*argv, "--help", 6) == 0 || + strncmp(*argv, "-h", 2) == 0 || + strncmp(*argv, "/h", 2) == 0) + { + print_usage(program); + exit(EXIT_FAILURE); + } + else + if (strncmp(*argv, "-csv",4) == 0 || + strncmp(*argv, "/csv",4) == 0) + { + csv = true; + string cmd = string(*argv); + size_t found = cmd.find('=',4); + if (found != string::npos) { + string filename = cmd.substr(found+1); + if (!filename.empty()) { + m->setOutput(filename); + } + } + continue; + } + else + if (strncmp(*argv, "-m",2) == 0) + { + argv++; + argc--; + imc_profile = atoi(*argv); + continue; + } + else + if (strncmp(*argv, "-p",2) == 0) + { + argv++; + argc--; + pcu_profile = atoi(*argv); + continue; + } + else + if (strncmp(*argv, "-a",2) == 0) + { + argv++; + argc--; + freq_band[0] = atoi(*argv); + continue; + } + else + if (strncmp(*argv, "-b",2) == 0) + { + argv++; + argc--; + freq_band[1] = atoi(*argv); + continue; + } + else + if (strncmp(*argv, "-c",2) == 0) + { + argv++; + argc--; + freq_band[2] = atoi(*argv); + continue; + } + else + if (strncmp(*argv, "--", 2) == 0) + { + argv++; + sysCmd = *argv; + sysArgv = argv; + break; + } + else + { + // any other options positional that is a floating point number is treated as , + // while the other options are ignored with a warning issues to stderr + double delay_input; + std::istringstream is_str_stream(*argv); + is_str_stream >> noskipws >> delay_input; + if(is_str_stream.eof() && !is_str_stream.fail()) { + delay = delay_input; + } else { + cerr << "WARNING: unknown command-line option: \"" << *argv << "\". Ignoring it." << endl; + print_usage(program); + exit(EXIT_FAILURE); + } + continue; + } + } while(argc > 1); // end of command line partsing loop + + m->disableJKTWorkaround(); + + const int cpu_model = m->getCPUModel(); + if(!(m->hasPCICFGUncore())) + { + std::cerr <<"Unsupported processor model ("<programServerUncorePowerMetrics(imc_profile,pcu_profile,freq_band)) + { +#ifdef _MSC_VER + std::cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program" << std::endl; +#elif defined(__linux__) + std::cerr << "You need to be root and loaded 'msr' Linux kernel module to execute the program. You may load the 'msr' module with 'modprobe msr'. \n"; +#endif + exit(EXIT_FAILURE); + } + ServerUncorePowerState * BeforeState = new ServerUncorePowerState[m->getNumSockets()]; + ServerUncorePowerState * AfterState = new ServerUncorePowerState[m->getNumSockets()]; + uint64 BeforeTime = 0, AfterTime = 0; + + std::cerr << std::dec << std::endl; + std::cerr.precision(2); + std::cerr << std::fixed; + std::cout << std::dec << std::endl; + std::cout.precision(2); + std::cout << std::fixed; + std::cerr << "\nMC counter group: "<setBlocked(true); + } else { + m->setBlocked(false); + } + + if( ((delay<1.0) && (delay>0.0)) || (delay<=0.0) ) delay = PCM_DELAY_DEFAULT; + + uint32 i = 0; + + for(i=0; igetNumSockets(); ++i) + BeforeState[i] = m->getServerUncorePowerState(i); + + BeforeTime = m->getTickCount(); + if( sysCmd != NULL ) { + MySystem(sysCmd, sysArgv); + } + + while(1) + { + std::cout << "----------------------------------------------------------------------------------------------"<getTickCount() - BeforeTime); + if(delay_ms < 0) delay_ms = 0; +#else + // compensation of delay on Linux/UNIX + // to make the samling interval as monotone as possible + struct timeval start_ts, end_ts; + if(calibrated == 0) { + gettimeofday(&end_ts, NULL); + diff_usec = (end_ts.tv_sec-start_ts.tv_sec)*1000000.0+(end_ts.tv_usec-start_ts.tv_usec); + calibrated_delay_ms = delay_ms - diff_usec/1000.0; + } +#endif + + MySleepMs(calibrated_delay_ms); + +#ifndef _MSC_VER + calibrated = (calibrated + 1) % PCM_CALIBRATION_INTERVAL; + if(calibrated == 0) { + gettimeofday(&start_ts, NULL); + } +#endif + + AfterTime = m->getTickCount(); + for(i=0; igetNumSockets(); ++i) + AfterState[i] = m->getServerUncorePowerState(i); + + std::cout << "Time elapsed: "<getNumSockets();++socket) + { + for(uint32 port=0;portgetQPILinksPerSocket();++port) + { + std::cout << "S"<getMCChannelsPerSocket();++channel) + { + if(imc_profile <= 3 && imc_profile >= 0) + { + std::cout << "S"<isBlocked() ) { + std::cout << "----------------------------------------------------------------------------------------------"< +#include +#include +#include "cpuasynchcounter.h" + +using namespace std; + +int main() +{ + AsynchronCounterState counters; + + cout << "CPU counter sensor "<< INTEL_PCM_VERSION << endl; + cout << "(C) 2010-2014 Intel Corp." << endl; + cout << "ksysguardd 1.2.0" << endl; + cout << "ksysguardd> "; + + while (1) + { + string s; + cin >> s; + + // list counters + if (s == "monitors") { + for (uint32 i = 0; i < counters.getNumCores(); ++i) { + for (uint32 a = 0; a < counters.getNumSockets(); ++a) + if (a == counters.getSocketId(i)) { + cout << "Socket" << a << "/CPU" << i << "/Frequency\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/IPC\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/L2CacheHitRatio\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/L3CacheHitRatio\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/L2CacheMisses\tinteger" << endl; + cout << "Socket" << a << "/CPU" << i << "/L3CacheMisses\tinteger" << endl; + cout << "Socket" << a << "/CPU" << i << "/CoreC0StateResidency\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/CoreC3StateResidency\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/CoreC6StateResidency\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/CoreC7StateResidency\tfloat" << endl; + cout << "Socket" << a << "/CPU" << i << "/ThermalHeadroom\tinteger" << endl; + } + } + for (uint32 a = 0; a < counters.getNumSockets(); ++a) { + cout << "Socket" << a << "/BytesReadFromMC\tfloat" << endl; + cout << "Socket" << a << "/BytesWrittenToMC\tfloat" << endl; + cout << "Socket" << a << "/Frequency\tfloat" << endl; + cout << "Socket" << a << "/IPC\tfloat" << endl; + cout << "Socket" << a << "/L2CacheHitRatio\tfloat" << endl; + cout << "Socket" << a << "/L3CacheHitRatio\tfloat" << endl; + cout << "Socket" << a << "/L2CacheMisses\tinteger" << endl; + cout << "Socket" << a << "/L3CacheMisses\tinteger" << endl; + cout << "Socket" << a << "/CoreC0StateResidency\tfloat" << endl; + cout << "Socket" << a << "/CoreC3StateResidency\tfloat" << endl; + cout << "Socket" << a << "/CoreC6StateResidency\tfloat" << endl; + cout << "Socket" << a << "/CoreC7StateResidency\tfloat" << endl; + cout << "Socket" << a << "/PackageC2StateResidency\tfloat" << endl; + cout << "Socket" << a << "/PackageC3StateResidency\tfloat" << endl; + cout << "Socket" << a << "/PackageC6StateResidency\tfloat" << endl; + cout << "Socket" << a << "/PackageC7StateResidency\tfloat" << endl; + cout << "Socket" << a << "/ThermalHeadroom\tinteger" << endl; + cout << "Socket" << a << "/CPUEnergy\tfloat" << endl; + cout << "Socket" << a << "/DRAMEnergy\tfloat" << endl; + } + for (uint32 a = 0; a < counters.getNumSockets(); ++a) { + for (uint32 l = 0; l < counters.getQPILinksPerSocket(); ++l) + cout << "Socket" << a << "/BytesIncomingToQPI" << l << "\tfloat" << endl; + } + + cout << "QPI_Traffic\tfloat" << endl; + cout << "Frequency\tfloat" << endl; + cout << "IPC\tfloat" << endl; //double check output + cout << "L2CacheHitRatio\tfloat" << endl; + cout << "L3CacheHitRatio\tfloat" << endl; + cout << "L2CacheMisses\tinteger" << endl; + cout << "L3CacheMisses\tinteger" << endl; + cout << "CoreC0StateResidency\tfloat" << endl; + cout << "CoreC3StateResidency\tfloat" << endl; + cout << "CoreC6StateResidency\tfloat" << endl; + cout << "CoreC7StateResidency\tfloat" << endl; + cout << "PackageC2StateResidency\tfloat" << endl; + cout << "PackageC3StateResidency\tfloat" << endl; + cout << "PackageC6StateResidency\tfloat" << endl; + cout << "PackageC7StateResidency\tfloat" << endl; + cout << "CPUEnergy\tfloat" << endl; + cout << "DRAMEnergy\tfloat" << endl; + } + + // provide metadata + + for (uint32 i = 0; i < counters.getNumCores(); ++i) { + for (uint32 a = 0; a < counters.getNumSockets(); ++a) + if (a == counters.getSocketId(i)) { + { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/Frequency?"; + if (s == c.str()) { + cout << "FREQ. CPU" << i << "\t\t\tMHz" << endl; + } + } + { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/ThermalHeadroom?"; + if (s == c.str()) { + cout << "Temperature reading in 1 degree Celsius relative to the TjMax temperature (thermal headroom) for CPU" << i << "\t\t\t°C" << endl; + } + } + { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/CoreC0StateResidency?"; + if (s == c.str()) { + cout << "core C0-state residency for CPU" << i << "\t\t\t%" << endl; + } + } + { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/CoreC3StateResidency?"; + if (s == c.str()) { + cout << "core C3-state residency for CPU" << i << "\t\t\t%" << endl; + } + } + { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/CoreC6StateResidency?"; + if (s == c.str()) { + cout << "core C6-state residency for CPU" << i << "\t\t\t%" << endl; + } + } + { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/CoreC7StateResidency?"; + if (s == c.str()) { + cout << "core C7-state residency for CPU" << i << "\t\t\t%" << endl; + } + } + } + } + for (uint32 i = 0; i < counters.getNumCores(); ++i) { + for (uint32 a = 0; a < counters.getNumSockets(); ++a) + if (a == counters.getSocketId(i)) { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/IPC?"; + if (s == c.str()) { + cout << "IPC CPU" << i << "\t0\t\t" << endl; + //cout << "CPU" << i << "\tInstructions per Cycle\t0\t1\t " << endl; + } + } + } + for (uint32 i = 0; i < counters.getNumCores(); ++i) { + for (uint32 a = 0; a < counters.getNumSockets(); ++a) + if (a == counters.getSocketId(i)) { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/L2CacheHitRatio?"; + if (s == c.str()) { + cout << "L2 Cache Hit Ratio CPU" << i << "\t0\t\t" << endl; + // cout << "CPU" << i << "\tL2 Cache Hit Ratio\t0\t1\t " << endl; + } + } + } + for (uint32 i = 0; i < counters.getNumCores(); ++i) { + for (uint32 a = 0; a < counters.getNumSockets(); ++a) + if (a == counters.getSocketId(i)) { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/L3CacheHitRatio?"; + if (s == c.str()) { + cout << "L3 Cache Hit Ratio CPU" << i << "\t0\t\t " << endl; + } + } + } + for (uint32 i = 0; i < counters.getNumCores(); ++i) { + for (uint32 a = 0; a < counters.getNumSockets(); ++a) + if (a == counters.getSocketId(i)) { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/L2CacheMisses?"; + if (s == c.str()) { + cout << "L2 Cache Misses CPU" << i << "\t0\t\t " << endl; + //cout << "CPU" << i << "\tL2 Cache Misses\t0\t1\t " << endl; + } + } + } + for (uint32 i = 0; i < counters.getNumCores(); ++i) { + for (uint32 a = 0; a < counters.getNumSockets(); ++a) + if (a == counters.getSocketId(i)) { + stringstream c; + c << "Socket" << a << "/CPU" << i << "/L3CacheMisses?"; + if (s == c.str()) { + cout << "L3 Cache Misses CPU" << i << "\t0\t\t " << endl; + //cout << "CPU" << i << "\tL3 Cache Misses\t0\t1\t " << endl; + } + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/BytesReadFromMC?"; + if (s == c.str()) { + cout << "read from MC Socket" << i << "\t0\t\tGB" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/DRAMEnergy?"; + if (s == c.str()) { + cout << "Energy consumed by DRAM on socket " << i << "\t0\t\tJoule" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/CPUEnergy?"; + if (s == c.str()) { + cout << "Energy consumed by CPU package " << i << "\t0\t\tJoule" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/ThermalHeadroom?"; + if (s == c.str()) { + cout << "Temperature reading in 1 degree Celsius relative to the TjMax temperature (thermal headroom) for CPU package " << i << "\t0\t\t°C" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/CoreC0StateResidency?"; + if (s == c.str()) { + cout << "core C0-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/CoreC3StateResidency?"; + if (s == c.str()) { + cout << "core C3-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/CoreC6StateResidency?"; + if (s == c.str()) { + cout << "core C6-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/CoreC7StateResidency?"; + if (s == c.str()) { + cout << "core C7-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/PackageC2StateResidency?"; + if (s == c.str()) { + cout << "package C2-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/PackageC3StateResidency?"; + if (s == c.str()) { + cout << "package C3-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/PackageC6StateResidency?"; + if (s == c.str()) { + cout << "package C6-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/PackageC7StateResidency?"; + if (s == c.str()) { + cout << "package C7-state residency for CPU package " << i << "\t0\t\t%" << endl; + } + } + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/BytesWrittenToMC?"; + if (s == c.str()) { + cout << "written to MC Socket" << i << "\t0\t\tGB" << endl; + //cout << "CPU" << i << "\tBytes written to memory channel\t0\t1\t GB" << endl; + } + } + + for (uint32 l = 0; l < counters.getQPILinksPerSocket(); ++l) { + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/BytesIncomingToQPI" << l << "?"; + if (s == c.str()) { + //cout << "Socket" << i << "\tBytes incoming to QPI link\t" << l<< "\t\t GB" << endl; + cout << "incoming to Socket" << i << " QPI Link" << l << "\t0\t\tGB" << endl; + } + } + } + + { + stringstream c; + c << "QPI_Traffic?"; + if (s == c.str()) { + cout << "Traffic on all QPIs\t0\t\tGB" << endl; + } + } + + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/Frequency?"; + if (s == c.str()) { + cout << "Socket" << i << " Frequency\t0\t\tMHz" << endl; + } + } + + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/IPC?"; + if (s == c.str()) { + cout << "Socket" << i << " IPC\t0\t\t" << endl; + } + } + + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/L2CacheHitRatio?"; + if (s == c.str()) { + cout << "Socket" << i << " L2 Cache Hit Ratio\t0\t\t" << endl; + } + } + + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/L3CacheHitRatio?"; + if (s == c.str()) { + cout << "Socket" << i << " L3 Cache Hit Ratio\t0\t\t" << endl; + } + } + + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/L2CacheMisses?"; + if (s == c.str()) { + cout << "Socket" << i << " L2 Cache Misses\t0\t\t" << endl; + } + } + + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/L3CacheMisses?"; + if (s == c.str()) { + cout << "Socket" << i << " L3 Cache Misses\t0\t\t" << endl; + } + } + + { + stringstream c; + c << "Frequency?"; + if (s == c.str()) { + cout << "Frequency system wide\t0\t\tMhz" << endl; + } + } + + { + stringstream c; + c << "IPC?"; + if (s == c.str()) { + cout << "IPC system wide\t0\t\t" << endl; + } + } + + { + stringstream c; + c << "L2CacheHitRatio?"; + if (s == c.str()) { + cout << "System wide L2 Cache Hit Ratio\t0\t\t" << endl; + } + } + + { + stringstream c; + c << "L3CacheHitRatio?"; + if (s == c.str()) { + cout << "System wide L3 Cache Hit Ratio\t0\t\t" << endl; + } + } + + { + stringstream c; + c << "L2CacheMisses?"; + if (s == c.str()) { + cout << "System wide L2 Cache Misses\t0\t\t" << endl; + } + } + + { + stringstream c; + c << "L3CacheMisses?"; + if (s == c.str()) { + cout << "System wide L3 Cache Misses\t0\t\t" << endl; + } + } + + { + stringstream c; + c << "L3CacheMisses?"; + if (s == c.str()) { + cout << "System wide L3 Cache Misses\t0\t\t" << endl; + } + } + + { + stringstream c; + c << "DRAMEnergy?"; + if (s == c.str()) { + cout << "System wide energy consumed by DRAM \t0\t\tJoule" << endl; + } + } + { + stringstream c; + c << "CPUEnergy?"; + if (s == c.str()) { + cout << "System wide energy consumed by CPU packages \t0\t\tJoule" << endl; + } + } + { + stringstream c; + c << "CoreC0StateResidency?"; + if (s == c.str()) { + cout << "System wide core C0-state residency \t0\t\t%" << endl; + } + } + { + stringstream c; + c << "CoreC3StateResidency?"; + if (s == c.str()) { + cout << "System wide core C3-state residency \t0\t\t%" << endl; + } + } + { + stringstream c; + c << "CoreC6StateResidency?"; + if (s == c.str()) { + cout << "System wide core C6-state residency \t0\t\t%" << endl; + } + } + { + stringstream c; + c << "CoreC7StateResidency?"; + if (s == c.str()) { + cout << "System wide core C7-state residency \t0\t\t%" << endl; + } + } + { + stringstream c; + c << "PackageC2StateResidency?"; + if (s == c.str()) { + cout << "System wide package C2-state residency \t0\t\t%" << endl; + } + } + { + stringstream c; + c << "PackageC3StateResidency?"; + if (s == c.str()) { + cout << "System wide package C3-state residency \t0\t\t%" << endl; + } + } + { + stringstream c; + c << "PackageC6StateResidency?"; + if (s == c.str()) { + cout << "System wide package C6-state residency \t0\t\t%" << endl; + } + } + { + stringstream c; + c << "PackageC7StateResidency?"; + if (s == c.str()) { + cout << "System wide package C7-state residency \t0\t\t%" << endl; + } + } + + // sensors + +#define OUTPUT_CORE_METRIC(name,function) \ + for (uint32 i = 0; i < counters.getNumCores(); ++i) { \ + for (uint32 a = 0; a < counters.getNumSockets(); ++a) \ + if (a == counters.getSocketId(i)) { \ + stringstream c; \ + c << "Socket" << a << "/CPU" << i << name; \ + if (s == c.str()) { \ + cout << function << endl; \ + } \ + } \ + } + + OUTPUT_CORE_METRIC("/Frequency", (counters.get(i) / 1000000 ) ) + OUTPUT_CORE_METRIC("/IPC", (counters.get(i) ) ) + OUTPUT_CORE_METRIC("/L2CacheHitRatio", (counters.get(i) ) ) + OUTPUT_CORE_METRIC("/L3CacheHitRatio", (counters.get(i) ) ) + OUTPUT_CORE_METRIC("/L2CacheMisses", (counters.get(i) / 1000000) ) + OUTPUT_CORE_METRIC("/L3CacheMisses", (counters.get(i) / 1000000) ) + OUTPUT_CORE_METRIC("/CoreC0StateResidency", (counters.get(0,i)*100.) ) + OUTPUT_CORE_METRIC("/CoreC3StateResidency", (counters.get(3,i)*100.) ) + OUTPUT_CORE_METRIC("/CoreC6StateResidency", (counters.get(6,i)*100.) ) + OUTPUT_CORE_METRIC("/CoreC7StateResidency", (counters.get(7,i)*100.) ) + OUTPUT_CORE_METRIC("/ThermalHeadroom", (counters.get(i)) ) + + #define OUTPUT_SOCKET_METRIC(name, function) \ + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { \ + stringstream c; \ + c << "Socket" << i << name; \ + if (s == c.str()) { \ + cout << function << endl; \ + } \ + } + + OUTPUT_SOCKET_METRIC("/DRAMEnergy", (counters.getSocket(i)) ) + OUTPUT_SOCKET_METRIC("/CPUEnergy", (counters.getSocket(i)) ) + OUTPUT_SOCKET_METRIC("/CoreC0StateResidency", (counters.getSocket(0,i)*100.) ) + OUTPUT_SOCKET_METRIC("/CoreC3StateResidency", (counters.getSocket(3,i)*100.) ) + OUTPUT_SOCKET_METRIC("/CoreC6StateResidency", (counters.getSocket(6,i)*100.) ) + OUTPUT_SOCKET_METRIC("/CoreC7StateResidency", (counters.getSocket(7,i)*100.) ) + OUTPUT_SOCKET_METRIC("/PackageC2StateResidency", (counters.getSocket(2,i)*100.) ) + OUTPUT_SOCKET_METRIC("/PackageC3StateResidency", (counters.getSocket(3,i)*100.) ) + OUTPUT_SOCKET_METRIC("/PackageC6StateResidency", (counters.getSocket(6,i)*100.) ) + OUTPUT_SOCKET_METRIC("/PackageC7StateResidency", (counters.getSocket(7,i)*100.) ) + OUTPUT_SOCKET_METRIC("/ThermalHeadroom", (counters.getSocket(i)) ) + OUTPUT_SOCKET_METRIC("/BytesReadFromMC", (double(counters.getSocket(i)) / 1024 / 1024 / 1024) ) + OUTPUT_SOCKET_METRIC("/BytesWrittenToMC", (double(counters.getSocket(i)) / 1024 / 1024 / 1024 ) ) + OUTPUT_SOCKET_METRIC("/Frequency", (counters.getSocket(i) / 1000000 ) ) + OUTPUT_SOCKET_METRIC("/IPC", (counters.getSocket(i) ) ) + OUTPUT_SOCKET_METRIC("/L2CacheHitRatio", (counters.getSocket(i) ) ) + OUTPUT_SOCKET_METRIC("/L3CacheHitRatio", (counters.getSocket(i) ) ) + OUTPUT_SOCKET_METRIC("/L2CacheMisses", (counters.getSocket(i) ) ) + OUTPUT_SOCKET_METRIC("/L3CacheMisses", (counters.getSocket(i) ) ) + + for (uint32 l = 0; l < counters.getQPILinksPerSocket(); ++l) { + for (uint32 i = 0; i < counters.getNumSockets(); ++i) { + stringstream c; + c << "Socket" << i << "/BytesIncomingToQPI" << l; + if (s == c.str()) { + cout << double(counters.getSocket(i, l)) / 1024 / 1024 / 1024 << endl; + } + } + } + + #define OUTPUT_SYSTEM_METRIC(name, function) \ + { \ + stringstream c; \ + c << name; \ + if (s == c.str()) { \ + cout << function << endl; \ + } \ + } + + OUTPUT_SYSTEM_METRIC("DRAMEnergy", (counters.getSystem()) ) + OUTPUT_SYSTEM_METRIC("CPUEnergy", (counters.getSystem()) ) + OUTPUT_SYSTEM_METRIC("CoreC0StateResidency", (counters.getSystem(0)*100.) ) + OUTPUT_SYSTEM_METRIC("CoreC3StateResidency", (counters.getSystem(3)*100.) ) + OUTPUT_SYSTEM_METRIC("CoreC6StateResidency", (counters.getSystem(6)*100.) ) + OUTPUT_SYSTEM_METRIC("CoreC7StateResidency", (counters.getSystem(7)*100.) ) + OUTPUT_SYSTEM_METRIC("PackageC2StateResidency", (counters.getSystem(2)*100.) ) + OUTPUT_SYSTEM_METRIC("PackageC3StateResidency", (counters.getSystem(3)*100.) ) + OUTPUT_SYSTEM_METRIC("PackageC6StateResidency", (counters.getSystem(6)*100.) ) + OUTPUT_SYSTEM_METRIC("PackageC7StateResidency", (counters.getSystem(7)*100.) ) + OUTPUT_SYSTEM_METRIC("Frequency", (double(counters.getSystem()) / 1000000) ) + OUTPUT_SYSTEM_METRIC("IPC", (double(counters.getSystem())) ) + OUTPUT_SYSTEM_METRIC("L2CacheHitRatio", (double(counters.getSystem())) ) + OUTPUT_SYSTEM_METRIC("L3CacheHitRatio", (double(counters.getSystem())) ) + OUTPUT_SYSTEM_METRIC("L2CacheMisses", (double(counters.getSystem())) ) + OUTPUT_SYSTEM_METRIC("L3CacheMisses", (double(counters.getSystem())) ) + OUTPUT_SYSTEM_METRIC("QPI_Traffic", (double(counters.getSystem()) / 1024 / 1024 / 1024) ) + + // exit + if (s == "quit" || s == "exit") { + break; + } + + + cout << "ksysguardd> "; + } + + return 0; +} diff --git a/pcm-tsx.cpp b/pcm-tsx.cpp new file mode 100644 index 0000000..696079c --- /dev/null +++ b/pcm-tsx.cpp @@ -0,0 +1,471 @@ +/* + Copyright (c) 2009-2013, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// written by Roman Dementiev + + +/*! \file pcm-tsx.cpp + \brief Example of using CPU counters: implements a performance counter monitoring utility for Intel Transactional Synchronization Extensions + */ +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include +#ifdef _MSC_VER +#pragma warning(disable : 4996) // for sprintf +#include +#include "../PCM_Win/windriver.h" +#else +#include +#include +#include // for gettimeofday() +#endif +#include +#include +#include +#include +#include +#include +#include +#include "cpucounters.h" +#include "utils.h" +#ifdef _MSC_VER +#include "freegetopt/getopt.h" +#endif + +#include +#define PCM_DELAY_DEFAULT 1.0 // in seconds +#define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs +#define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration + +using namespace std; + +struct TSXEvent +{ + const char * name; + unsigned char event; + unsigned char umask; + const char * description; +}; + +TSXEvent eventDefinition[] = { + +{"RTM_RETIRED.START", 0xC9, 0x01, "Number of times an RTM execution started."}, +{"RTM_RETIRED.COMMIT", 0xC9, 0x02, "Number of times an RTM execution successfully committed"}, +{"RTM_RETIRED.ABORTED", 0xC9, 0x04, "Number of times an RTM execution aborted due to any reasons (multiple categories may count as one)"}, +{"RTM_RETIRED.ABORTED_MISC1", 0xC9, 0x08, "Number of times an RTM execution aborted due to various memory events"}, +{"RTM_RETIRED.ABORTED_MISC2", 0xC9, 0x10, "Number of times an RTM execution aborted due to uncommon conditions"}, +{"RTM_RETIRED.ABORTED_MISC3", 0xC9, 0x20, "Number of times an RTM execution aborted due to HLE-unfriendly instructions"}, +{"RTM_RETIRED.ABORTED_MISC4", 0xC9, 0x40, "Number of times an RTM execution aborted due to incompatible memory type"}, +{"RTM_RETIRED.ABORTED_MISC5", 0xC9, 0x80, "Number of times an RTM execution aborted due to none of the previous 4 categories (e.g. interrupt)"}, + +{"HLE_RETIRED.START", 0xC8, 0x01, "Number of times an HLE execution started."}, +{"HLE_RETIRED.COMMIT", 0xC8, 0x02, "Number of times an HLE execution successfully committed"}, +{"HLE_RETIRED.ABORTED", 0xC8, 0x04, "Number of times an HLE execution aborted due to any reasons (multiple categories may count as one)"}, +{"HLE_RETIRED.ABORTED_MISC1", 0xC8, 0x08, "Number of times an HLE execution aborted due to various memory events"}, +{"HLE_RETIRED.ABORTED_MISC2", 0xC8, 0x10, "Number of times an HLE execution aborted due to uncommon conditions"}, +{"HLE_RETIRED.ABORTED_MISC3", 0xC8, 0x20, "Number of times an HLE execution aborted due to HLE-unfriendly instructions"}, +{"HLE_RETIRED.ABORTED_MISC4", 0xC8, 0x40, "Number of times an HLE execution aborted due to incompatible memory type"}, +{"HLE_RETIRED.ABORTED_MISC5", 0xC8, 0x80, "Number of times an HLE execution aborted due to none of the previous 4 categories (e.g. interrupt)"}, + +{"TX_MEM.ABORT_CONFLICT", 0x54, 0x01, "Number of times a transactional abort was signaled due to a data conflict on a transactionally accessed address"}, +{"TX_MEM.ABORT_CAPACITY_WRITE", 0x54, 0x02, "Number of times a transactional abort was signaled due to limited resources for transactional stores"}, +{"TX_MEM.ABORT_HLE_STORE_TO_ELIDED_LOCK", 0x54, 0x04, "Number of times a HLE transactional region aborted due to a non XRELEASE prefixed instruction writing to an elided lock in the elision buffer"}, +{"TX_MEM.ABORT_HLE_ELISION_BUFFER_NOT_EMPTY", 0x54, 0x08, "Number of times an HLE transactional execution aborted due to NoAllocatedElisionBuffer being nonzero."}, +{"TX_MEM.ABORT_HLE_ELISION_BUFFER_MISMATCH", 0x54, 0x10, "Number of times an HLE transactional execution aborted due to XRELEASE lock not satisfying the address and value requirements in the elision buffer."}, +{"TX_MEM.ABORT_HLE_ELISION_BUFFER_UNSUPPORTED_ALIGNMENT", 0x54, 0x20, "Number of times an HLE transactional execution aborted due to an unsupported read alignment from the elision buffer."}, +{"TX_MEM.HLE_ELISION_BUFFER_FULL", 0x54, 0x40, "Number of times HLE lock could not be elided due to ElisionBufferAvailable being zero."}, + +{"TX_EXEC.MISC1", 0x5D, 0x01, "Counts the number of times a class of instructions that may cause a transactional abort was executed. Since this is the count of execution, it may not always cause a transactional abort."}, +{"TX_EXEC.MISC2", 0x5D, 0x02, "Counts the number of times a class of instructions that may cause a transactional abort was executed inside a transactional region"}, +{"TX_EXEC.MISC3", 0x5D, 0x04, "Counts the number of times an instruction execution caused the nest count supported to be exceeded"}, +{"TX_EXEC.MISC4", 0x5D, 0x08, "Counts the number of times an HLE XACQUIRE instruction was executed inside an RTM transactional region"} + +}; + +void print_usage(const string progname) +{ + cerr << endl << " Usage: " << endl << " " << progname + << " --help | [delay] [options] [-- external_program [external_program_options]]" << endl; + cerr << " => time interval to sample performance counters." << endl; + cerr << " If not specified, or 0, with external program given" << endl; + cerr << " will read counters only after external program finishes" << endl; + cerr << " Supported are: " << endl; + cerr << " -h | --help | /h => print this help and exit" << endl; + cerr << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or" << endl + << " to a file, in case filename is provided" << endl; + cerr << " [-e event1] [-e event2] [-e event3]=> optional list of custom TSX events to monitor (up to 4)." + << " The list of supported events:" << endl ; + for(uint32 i=0; i< sizeof(eventDefinition)/sizeof(TSXEvent); ++i) + { + cerr << eventDefinition[i].name << "\t"< print counters every second without core and socket output" << endl; + cerr << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format" << endl; + cerr << " " << progname << " /csv 5 2>/dev/null => one sampe every 5 seconds, and discard all diagnostic output" << endl; + cerr << endl; +} + +template +void print_basic_stats(const StateType & BeforeState, const StateType & AfterState, bool csv) +{ + uint64 cycles = getCycles(BeforeState, AfterState); + uint64 instr = getInstructionsRetired(BeforeState, AfterState); + const uint64 TXcycles = getNumberOfCustomEvents(3, BeforeState, AfterState); + const uint64 TXcycles_commited = getNumberOfCustomEvents(2, BeforeState, AfterState); + const uint64 Abr_cycles = (TXcycles > TXcycles_commited)?(TXcycles-TXcycles_commited):0ULL; + uint64 nRTM = getNumberOfCustomEvents(0, BeforeState, AfterState); + uint64 nHLE = getNumberOfCustomEvents(1, BeforeState, AfterState); + + if(csv) + { + cout << double(instr)/double(cycles) << ","; + cout << instr << ","; + cout << cycles << ","; + cout << TXcycles << ","<< std::setw(5) <<100.*double(TXcycles)/double(cycles) << "%,"; + cout << Abr_cycles << ","<< std::setw(5) <<100.*double(Abr_cycles)/double(cycles) << "%,"; + cout << nRTM << ","; + cout << nHLE << ","; + } + else + { + cout << double(instr)/double(cycles) << " "; + cout << unit_format(instr) << " "; + cout << unit_format(cycles) << " "; + cout << unit_format(TXcycles) << " ("<< std::setw(5) <<100.*double(TXcycles)/double(cycles) << "%) "; + cout << unit_format(Abr_cycles) << " ("<< std::setw(5) <<100.*double(Abr_cycles)/double(cycles) << "%) "; + cout << unit_format(nRTM) << " "; + cout << unit_format(nHLE) << " "; + } + + if(nRTM + nHLE) + { + uint64 cyclesPerTransaction = TXcycles/(nRTM + nHLE); + if(csv) + cout << cyclesPerTransaction<< "\n"; + else + cout << unit_format(cyclesPerTransaction)<< "\n"; + } + else + cout << " N/A"<< "\n"; + +} + +template +void print_custom_stats(const StateType & BeforeState, const StateType & AfterState ,bool csv) +{ + for(int i=0;i<4;++i) + if(!csv) + cout << unit_format(getNumberOfCustomEvents(i, BeforeState, AfterState)) << " "; + else + cout << getNumberOfCustomEvents(i, BeforeState, AfterState)<<","; + + cout << "\n"; +} + + + +int findEvent(const char * name) +{ + const int all = sizeof(eventDefinition)/sizeof(TSXEvent); + for(int i=0; i events; + int cur_event; + bool csv = false; + long diff_usec = 0; // deviation of clock is useconds between measurements + int calibrated = PCM_CALIBRATION_INTERVAL - 2; // keeps track is the clock calibration needed + string program = string(argv[0]); + + PCM * m = PCM::getInstance(); + + if(argc > 1) do + { + argv++; + argc--; + if (strncmp(*argv, "--help", 6) == 0 || + strncmp(*argv, "-h", 2) == 0 || + strncmp(*argv, "/h", 2) == 0) + { + print_usage(program); + exit(EXIT_FAILURE); + } + else + if (strncmp(*argv, "-csv",4) == 0 || + strncmp(*argv, "/csv",4) == 0) + { + csv = true; + string cmd = string(*argv); + size_t found = cmd.find('=',4); + if (found != string::npos) { + string filename = cmd.substr(found+1); + if (!filename.empty()) { + m->setOutput(filename); + } + } + continue; + } + else + if (strncmp(*argv, "-e",2) == 0) + { + argv++; + argc--; + if(events.size() >= 4 ) { + cerr << "At most 4 events are allowed"<< endl; + exit(EXIT_FAILURE); + } + cur_event = findEvent(*argv); + if(cur_event < 0) { + cerr << "Event " << *argv << " is not supported. See the list of supported events"<< endl; + print_usage(program); + exit(EXIT_FAILURE); + } + events.push_back(cur_event); + continue; + } + else + if (strncmp(*argv, "--", 2) == 0) + { + argv++; + sysCmd = *argv; + sysArgv = argv; + break; + } + else + { + // any other options positional that is a floating point number is treated as , + // while the other options are ignored with a warning issues to stderr + double delay_input; + std::istringstream is_str_stream(*argv); + is_str_stream >> noskipws >> delay_input; + if(is_str_stream.eof() && !is_str_stream.fail()) { + delay = delay_input; + } else { + cerr << "WARNING: unknown command-line option: \"" << *argv << "\". Ignoring it." << endl; + print_usage(program); + exit(EXIT_FAILURE); + } + continue; + } + } while(argc > 1); // end of command line partsing loop + + EventSelectRegister def_event_select_reg; + def_event_select_reg.value = 0; + def_event_select_reg.fields.usr = 1; + def_event_select_reg.fields.os = 1; + def_event_select_reg.fields.enable = 1; + + PCM::ExtendedCustomCoreEventDescription conf; + conf.fixedCfg = NULL; // default + conf.nGPCounters = 4; + EventSelectRegister regs[4]; + conf.gpCounterCfg = regs; + for(int i=0;i<4;++i) + regs[i] = def_event_select_reg; + + if(events.empty()) + { + regs[0].fields.event_select = 0xc9; + regs[0].fields.umask = 0x01; + regs[1].fields.event_select = 0xc8; + regs[1].fields.umask = 0x01; + regs[2].fields.event_select = 0x3c; + regs[2].fields.in_tx = 1; + regs[2].fields.in_txcp = 1; + regs[3].fields.event_select = 0x3c; + regs[3].fields.in_tx = 1; + } + else + { + for(unsigned int i=0;iprogram(PCM::EXT_CUSTOM_CORE_EVENTS, &conf); + switch (status) + { + case PCM::Success: + break; + case PCM::MSRAccessDenied: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (no MSR or PCI CFG space access)." << endl; + exit(EXIT_FAILURE); + case PCM::PMUBusy: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Performance Monitoring Unit is occupied by other application). Try to stop the application that uses PMU." << endl; + cerr << "Alternatively you can try to reset PMU configuration at your own risk. Try to reset? (y/n)" << endl; + char yn; + std::cin >> yn; + if ('y' == yn) + { + m->resetPMU(); + cerr << "PMU configuration has been reset. Try to rerun the program again." << endl; + } + exit(EXIT_FAILURE); + default: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Unknown error)." << endl; + exit(EXIT_FAILURE); + } + + cerr << "\nDetected "<< m->getCPUBrandString() << " \"Intel(r) microarchitecture codename "<getUArchCodename()<<"\""<getNumCores(); + std::vector BeforeState, AfterState; + std::vector DummySocketStates; + + if ( (sysCmd != NULL) && (delay<=0.0) ) { + // in case external command is provided in command line, and + // delay either not provided (-1) or is zero + m->setBlocked(true); + } else { + m->setBlocked(false); + } + + if (csv) { + if( delay<=0.0 ) delay = PCM_DELAY_DEFAULT; + } else { + // for non-CSV mode delay < 1.0 does not make a lot of practical sense: + // hard to read from the screen, or + // in case delay is not provided in command line => set default + if( ((delay<1.0) && (delay>0.0)) || (delay<=0.0) ) delay = PCM_DELAY_DEFAULT; + } + + cerr << "Update every "<getTickCount(); + m->getAllCounterStates(SysBeforeState, DummySocketStates, BeforeState); + + if( sysCmd != NULL ) { + MySystem(sysCmd, sysArgv); + } + + while(1) + { + if(!csv) cout << std::flush; + int delay_ms = int(delay * 1000); + int calibrated_delay_ms = delay_ms; +#ifdef _MSC_VER + // compensate slow Windows console output + if(AfterTime) delay_ms -= (int)(m->getTickCount() - BeforeTime); + if(delay_ms < 0) delay_ms = 0; +#else + // compensation of delay on Linux/UNIX + // to make the samling interval as monotone as possible + struct timeval start_ts, end_ts; + if(calibrated == 0) { + gettimeofday(&end_ts, NULL); + diff_usec = (end_ts.tv_sec-start_ts.tv_sec)*1000000.0+(end_ts.tv_usec-start_ts.tv_usec); + calibrated_delay_ms = delay_ms - diff_usec/1000.0; + } +#endif + + MySleepMs(calibrated_delay_ms); + +#ifndef _MSC_VER + calibrated = (calibrated + 1) % PCM_CALIBRATION_INTERVAL; + if(calibrated == 0) { + gettimeofday(&start_ts, NULL); + } +#endif + AfterTime = m->getTickCount(); + m->getAllCounterStates(SysAfterState, DummySocketStates, AfterState); + + cout << "Time elapsed: "<isBlocked() ) { + // in case PCM was blocked after spawning child application: break monitoring loop here + break; + } + } + exit(EXIT_SUCCESS); +} diff --git a/pcm.cpp b/pcm.cpp new file mode 100644 index 0000000..319e5a6 --- /dev/null +++ b/pcm.cpp @@ -0,0 +1,1198 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev, +// Thomas Willhalm, +// Patrick Ungerer + + +/*! \file pcm.cpp +\brief Example of using CPU counters: implements a simple performance counter monitoring utility +*/ +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include +#ifdef _MSC_VER +#pragma warning(disable : 4996) // for sprintf +#include +#include "../PCM_Win/windriver.h" +#else +#include +#include // for atexit() +#include // for gettimeofday() +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpucounters.h" +#include "utils.h" + +#define SIZE (10000000) +#define PCM_DELAY_DEFAULT 1.0 // in seconds +#define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs +#define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration + +using namespace std; + +template +double float_format(IntType n) +{ + return double(n) / 1024 / 1024; +} + +std::string temp_format(int32 t) +{ + char buffer[1024]; + if (t == PCM_INVALID_THERMAL_HEADROOM) + return "N/A"; + + sprintf(buffer, "%2d", t); + return buffer; +} + +std::string l3cache_occ_format(uint64 o) +{ + char buffer[1024]; + if (o == PCM_INVALID_L3_CACHE_OCCUPANCY) + return "N/A"; + + sprintf(buffer, "%6d", (uint32) o); + return buffer; +} + +void print_help(const string prog_name) +{ + cerr << endl << " Usage: " << endl << " " << prog_name + << " --help | [delay] [options] [-- external_program [external_program_options]]" << endl; + cerr << " => time interval to sample performance counters." << endl; + cerr << " If not specified, or 0, with external program given" << endl; + cerr << " will read counters only after external program finishes" << endl; + cerr << " Supported are: " << endl; + cerr << " -h | --help | /h => print this help and exit" << endl; +#ifdef _MSC_VER + cerr << " --uninstallDriver | --installDriver=> (un)install driver" << endl; +#endif + cerr << " -r | --reset | /reset => reset PMU configuration (at your own risk)" << endl; + cerr << " -nc | --nocores | /nc => hide core related output" << endl; + cerr << " -ns | --nosockets | /ns => hide socket related output" << endl; + cerr << " -nsys | --nosystem | /nsys => hide system related output" << endl; + cerr << " -m | --multiple-instances | /m => allow multiple PCM instances running in parallel" << endl; + cerr << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or" << endl + << " to a file, in case filename is provided" << endl; + cerr << " -i[=number] | /i[=number] => allow to determine number of iterations" << endl; + cerr << " Examples:" << endl; + cerr << " " << prog_name << " 1 -nc -ns => print counters every second without core and socket output" << endl; + cerr << " " << prog_name << " 1 -i=10 => print counters every second 10 times and exit" << endl; + cerr << " " << prog_name << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format" << endl; + cerr << " " << prog_name << " /csv 5 2>/dev/null => one sampe every 5 seconds, and discard all diagnostic output" << endl; + cerr << endl; +} + +void print_output(PCM * m, + const std::vector & cstates1, + const std::vector & cstates2, + const std::vector & sktstate1, + const std::vector & sktstate2, + const SystemCounterState& sstate1, + const SystemCounterState& sstate2, + const int cpu_model, + const bool show_core_output, + const bool show_socket_output, + const bool show_system_output + ) +{ + cout << "\n"; + cout << " EXEC : instructions per nominal CPU cycle" << "\n"; + cout << " IPC : instructions per CPU cycle" << "\n"; + cout << " FREQ : relation to nominal CPU frequency='unhalted clock ticks'/'invariant timer ticks' (includes Intel Turbo Boost)" << "\n"; + if (cpu_model != PCM::ATOM) cout << " AFREQ : relation to nominal CPU frequency while in active state (not in power-saving C state)='unhalted clock ticks'/'invariant timer ticks while in C0-state' (includes Intel Turbo Boost)" << "\n"; + if (cpu_model != PCM::ATOM) cout << " L3MISS: L3 cache misses " << "\n"; + if (cpu_model == PCM::ATOM) + cout << " L2MISS: L2 cache misses " << "\n"; + else + cout << " L2MISS: L2 cache misses (including other core's L2 cache *hits*) " << "\n"; + if (cpu_model != PCM::ATOM) cout << " L3HIT : L3 cache hit ratio (0.00-1.00)" << "\n"; + cout << " L2HIT : L2 cache hit ratio (0.00-1.00)" << "\n"; + if (cpu_model != PCM::ATOM) cout << " L3CLK : ratio of CPU cycles lost due to L3 cache misses (0.00-1.00), in some cases could be >1.0 due to a higher memory latency" << "\n"; + if (cpu_model != PCM::ATOM) cout << " L2CLK : ratio of CPU cycles lost due to missing L2 cache but still hitting L3 cache (0.00-1.00)" << "\n"; + if (cpu_model != PCM::ATOM) cout << " READ : bytes read from memory controller (in GBytes)" << "\n"; + if (cpu_model != PCM::ATOM) cout << " WRITE : bytes written to memory controller (in GBytes)" << "\n"; + if (m->memoryIOTrafficMetricAvailable()) cout << " IO : bytes read/written due to IO requests to memory controller (in GBytes); this may be an over estimate due to same-cache-line partial requests" << "\n"; + if (m->L3CacheOccupancyMetricAvailable()) cout << " L3OCC : L3 occupancy (in KBytes)" << "\n"; + cout << " TEMP : Temperature reading in 1 degree Celsius relative to the TjMax temperature (thermal headroom): 0 corresponds to the max temperature" << "\n"; + cout << "\n"; + cout << "\n"; + cout.precision(2); + cout << std::fixed; + if (cpu_model == PCM::ATOM) + cout << " Core (SKT) | EXEC | IPC | FREQ | L2MISS | L2HIT | TEMP" << "\n" << "\n"; + else + { + cout << " Core (SKT) | EXEC | IPC | FREQ | AFREQ | L3MISS | L2MISS | L3HIT | L2HIT | L3CLK | L2CLK |"; + + if (m->L3CacheOccupancyMetricAvailable()) + cout << " L3OCC | READ | WRITE |"; + else + cout << " READ | WRITE |"; + + if (m->memoryIOTrafficMetricAvailable()) + cout << " IO | TEMP |" << "\n" << "\n"; + else + cout << " TEMP" << "\n" << "\n"; + + } + + + if (show_core_output) + { + for (uint32 i = 0; i < m->getNumCores(); ++i) + { + if (m->isCoreOnline(i) == false) + continue; + + if (cpu_model != PCM::ATOM) + { + cout << " " << setw(3) << i << " " << setw(2) << m->getSocketId(i) << + " " << getExecUsage(cstates1[i], cstates2[i]) << + " " << getIPC(cstates1[i], cstates2[i]) << + " " << getRelativeFrequency(cstates1[i], cstates2[i]) << + " " << getActiveRelativeFrequency(cstates1[i], cstates2[i]) << + " " << unit_format(getL3CacheMisses(cstates1[i], cstates2[i])) << + " " << unit_format(getL2CacheMisses(cstates1[i], cstates2[i])) << + " " << getL3CacheHitRatio(cstates1[i], cstates2[i]) << + " " << getL2CacheHitRatio(cstates1[i], cstates2[i]) << + " " << getCyclesLostDueL3CacheMisses(cstates1[i], cstates2[i]) << + " " << getCyclesLostDueL2CacheMisses(cstates1[i], cstates2[i]) ; + if (m->L3CacheOccupancyMetricAvailable()) + cout << " " << setw(6) << l3cache_occ_format(getL3CacheOccupancy(cstates2[i])) ; + if (m->memoryIOTrafficMetricAvailable()) + cout << " N/A N/A N/A"; + else + cout << " N/A N/A"; + cout << " " << temp_format(cstates2[i].getThermalHeadroom()) << + "\n"; + } + else + cout << " " << setw(3) << i << " " << setw(2) << m->getSocketId(i) << + " " << getExecUsage(cstates1[i], cstates2[i]) << + " " << getIPC(cstates1[i], cstates2[i]) << + " " << getRelativeFrequency(cstates1[i], cstates2[i]) << + " " << unit_format(getL2CacheMisses(cstates1[i], cstates2[i])) << + " " << getL2CacheHitRatio(cstates1[i], cstates2[i]) << + " " << temp_format(cstates2[i].getThermalHeadroom()) << + "\n"; + } + } + if (show_socket_output) + { + if (!(m->getNumSockets() == 1 && cpu_model == PCM::ATOM)) + { + cout << "-----------------------------------------------------------------------------------------------------------------------------" << "\n"; + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + cout << " SKT " << setw(2) << i << + " " << getExecUsage(sktstate1[i], sktstate2[i]) << + " " << getIPC(sktstate1[i], sktstate2[i]) << + " " << getRelativeFrequency(sktstate1[i], sktstate2[i]) << + " " << getActiveRelativeFrequency(sktstate1[i], sktstate2[i]) << + " " << unit_format(getL3CacheMisses(sktstate1[i], sktstate2[i])) << + " " << unit_format(getL2CacheMisses(sktstate1[i], sktstate2[i])) << + " " << getL3CacheHitRatio(sktstate1[i], sktstate2[i]) << + " " << getL2CacheHitRatio(sktstate1[i], sktstate2[i]) << + " " << getCyclesLostDueL3CacheMisses(sktstate1[i], sktstate2[i]) << + " " << getCyclesLostDueL2CacheMisses(sktstate1[i], sktstate2[i]); + if (m->L3CacheOccupancyMetricAvailable()) + cout << " " << setw(6) << l3cache_occ_format(getL3CacheOccupancy(sktstate2[i])) ; + if (m->memoryTrafficMetricsAvailable()) + cout << " " << getBytesReadFromMC(sktstate1[i], sktstate2[i]) / double(1024ULL * 1024ULL * 1024ULL) << + " " << getBytesWrittenToMC(sktstate1[i], sktstate2[i]) / double(1024ULL * 1024ULL * 1024ULL); + else + cout << " N/A N/A"; + + if (m->memoryIOTrafficMetricAvailable()) + cout << " " << getIORequestBytesFromMC(sktstate1[i], sktstate2[i]) / double(1024ULL * 1024ULL * 1024ULL); + + cout << " " << temp_format(sktstate2[i].getThermalHeadroom()) << "\n"; + } + } + } + cout << "-----------------------------------------------------------------------------------------------------------------------------" << "\n"; + + if (show_system_output) + { + if (cpu_model != PCM::ATOM) + { + cout << " TOTAL * " << getExecUsage(sstate1, sstate2) << + " " << getIPC(sstate1, sstate2) << + " " << getRelativeFrequency(sstate1, sstate2) << + " " << getActiveRelativeFrequency(sstate1, sstate2) << + " " << unit_format(getL3CacheMisses(sstate1, sstate2)) << + " " << unit_format(getL2CacheMisses(sstate1, sstate2)) << + " " << getL3CacheHitRatio(sstate1, sstate2) << + " " << getL2CacheHitRatio(sstate1, sstate2) << + " " << getCyclesLostDueL3CacheMisses(sstate1, sstate2) << + " " << getCyclesLostDueL2CacheMisses(sstate1, sstate2) ; + if (m->L3CacheOccupancyMetricAvailable()) + cout << " " << " N/A "; + if (m->memoryTrafficMetricsAvailable()) + cout << " " << getBytesReadFromMC(sstate1, sstate2) / double(1024ULL * 1024ULL * 1024ULL) << + " " << getBytesWrittenToMC(sstate1, sstate2) / double(1024ULL * 1024ULL * 1024ULL); + else + cout << " N/A N/A"; + + if (m->memoryIOTrafficMetricAvailable()) + cout << " " << getIORequestBytesFromMC(sstate1, sstate2) / double(1024ULL * 1024ULL * 1024ULL); + + cout << " N/A\n"; + } + else + cout << " TOTAL * " << getExecUsage(sstate1, sstate2) << + " " << getIPC(sstate1, sstate2) << + " " << getRelativeFrequency(sstate1, sstate2) << + " " << unit_format(getL2CacheMisses(sstate1, sstate2)) << + " " << getL2CacheHitRatio(sstate1, sstate2) << + " N/A\n"; + } + + if (show_system_output) + { + cout << "\n" << " Instructions retired: " << unit_format(getInstructionsRetired(sstate1, sstate2)) << " ; Active cycles: " << unit_format(getCycles(sstate1, sstate2)) << " ; Time (TSC): " << unit_format(getInvariantTSC(cstates1[0], cstates2[0])) << "ticks ; C0 (active,non-halted) core residency: " << (getCoreCStateResidency(0, sstate1, sstate2)*100.) << " %\n"; + cout << "\n"; + for (int s = 1; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + std::cout << " C" << s << " core residency: " << (getCoreCStateResidency(s, sstate1, sstate2)*100.) << " %;"; + cout << "\n"; + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isPackageCStateResidencySupported(s)) + std::cout << " C" << s << " package residency: " << (getPackageCStateResidency(s, sstate1, sstate2)*100.) << " %;"; + cout << "\n"; + if (m->getNumCores() == m->getNumOnlineCores()) + { + cout << "\n" << " PHYSICAL CORE IPC : " << getCoreIPC(sstate1, sstate2) << " => corresponds to " << 100. * (getCoreIPC(sstate1, sstate2) / double(m->getMaxIPC())) << " % utilization for cores in active state"; + cout << "\n" << " Instructions per nominal CPU cycle: " << getTotalExecUsage(sstate1, sstate2) << " => corresponds to " << 100. * (getTotalExecUsage(sstate1, sstate2) / double(m->getMaxIPC())) << " % core utilization over time interval" << "\n"; + } + } + + if (show_socket_output) + { + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + { + cout << "\n" << "Intel(r) QPI data traffic estimation in bytes (data traffic coming to CPU/socket through QPI links):" << "\n" << "\n"; + + + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + + cout << " "; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << " QPI" << i << " "; + + if (m->qpiUtilizationMetricsAvailable()) + { + cout << "| "; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << " QPI" << i << " "; + } + + cout << "\n" << "----------------------------------------------------------------------------------------------" << "\n"; + + + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + cout << " SKT " << setw(2) << i << " "; + for (uint32 l = 0; l < qpiLinks; ++l) + cout << unit_format(getIncomingQPILinkBytes(i, l, sstate1, sstate2)) << " "; + + if (m->qpiUtilizationMetricsAvailable()) + { + cout << "| "; + for (uint32 l = 0; l < qpiLinks; ++l) + cout << setw(3) << std::dec << int(100. * getIncomingQPILinkUtilization(i, l, sstate1, sstate2)) << "% "; + } + + cout << "\n"; + } + } + } + + if (show_system_output) + { + cout << "----------------------------------------------------------------------------------------------" << "\n"; + + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + cout << "Total QPI incoming data traffic: " << unit_format(getAllIncomingQPILinkBytes(sstate1, sstate2)) << " QPI data traffic/Memory controller traffic: " << getQPItoMCTrafficRatio(sstate1, sstate2) << "\n"; + } + + if (show_socket_output) + { + if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems + { + cout << "\n" << "Intel(r) QPI traffic estimation in bytes (data and non-data traffic outgoing from CPU/socket through QPI links):" << "\n" << "\n"; + + + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + + cout << " "; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << " QPI" << i << " "; + + + cout << "| "; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << " QPI" << i << " "; + + + cout << "\n" << "----------------------------------------------------------------------------------------------" << "\n"; + + + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + cout << " SKT " << setw(2) << i << " "; + for (uint32 l = 0; l < qpiLinks; ++l) + cout << unit_format(getOutgoingQPILinkBytes(i, l, sstate1, sstate2)) << " "; + + cout << "| "; + for (uint32 l = 0; l < qpiLinks; ++l) + cout << setw(3) << std::dec << int(100. * getOutgoingQPILinkUtilization(i, l, sstate1, sstate2)) << "% "; + + cout << "\n"; + } + + cout << "----------------------------------------------------------------------------------------------" << "\n"; + cout << "Total QPI outgoing data and non-data traffic: " << unit_format(getAllOutgoingQPILinkBytes(sstate1, sstate2)) << "\n"; + } + } + if (show_socket_output) + { + if (m->packageEnergyMetricsAvailable()) + { + cout << "\n"; + cout << "----------------------------------------------------------------------------------------------" << "\n"; + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + cout << " SKT " << setw(2) << i << " package consumed " << getConsumedJoules(sktstate1[i], sktstate2[i]) << " Joules\n"; + } + cout << "----------------------------------------------------------------------------------------------" << "\n"; + cout << " TOTAL: " << getConsumedJoules(sstate1, sstate2) << " Joules\n"; + } + if (m->dramEnergyMetricsAvailable()) + { + cout << "\n"; + cout << "----------------------------------------------------------------------------------------------" << "\n"; + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + cout << " SKT " << setw(2) << i << " DIMMs consumed " << getDRAMConsumedJoules(sktstate1[i], sktstate2[i]) << " Joules\n"; + } + cout << "----------------------------------------------------------------------------------------------" << "\n"; + cout << " TOTAL: " << getDRAMConsumedJoules(sstate1, sstate2) << " Joules\n"; + } + } + +} + +void print_csv_header(PCM * m, + const int cpu_model, + const bool show_core_output, + const bool show_socket_output, + const bool show_system_output + ) +{ + // print first header line + cout << "System;;"; + if (show_system_output) + { + if (cpu_model != PCM::ATOM) + { + if (m->L3CacheOccupancyMetricAvailable()) + cout << ";;;;;;;;;;;;;"; + else + cout << ";;;;;;;;;;;;"; + } + else + cout << ";;;;;"; + + + cout << ";;;;;;;"; + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + cout << ";;"; + if (m->qpiUtilizationMetricsAvailable()) + cout << ";"; + cout << "System Core C-States"; + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << ";"; + cout << "System Pack C-States"; + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isPackageCStateResidencySupported(s)) + cout << ";"; + if (m->packageEnergyMetricsAvailable()) + cout << ";"; + if (m->dramEnergyMetricsAvailable()) + cout << ";"; + } + + + if (show_socket_output) + { + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + if (cpu_model == PCM::ATOM) + cout << "Socket" << i << ";;;;;;;"; + else + { + if (m->L3CacheOccupancyMetricAvailable()) + cout << "Socket" << i << ";;;;;;;;;;;;;;"; + else + cout << "Socket" << i << ";;;;;;;;;;;;;"; + } + } + + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + { + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + + for (uint32 s = 0; s < m->getNumSockets(); ++s) + { + cout << "SKT" << s << "dataIn"; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << ";"; + if (m->qpiUtilizationMetricsAvailable()) + { + cout << "SKT" << s << "dataIn (percent)"; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << ";"; + } + } + } + + if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems + { + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + + for (uint32 s = 0; s < m->getNumSockets(); ++s) + { + cout << "SKT" << s << "trafficOut"; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << ";"; + cout << "SKT" << s << "trafficOut (percent)"; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << ";"; + } + } + + + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + cout << "SKT" << i << " Core C-State"; + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << ";"; + cout << "SKT" << i << " Package C-State"; + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isPackageCStateResidencySupported(s)) + cout << ";"; + } + + if (m->packageEnergyMetricsAvailable()) + { + cout << "Proc Energy (Joules)"; + for (uint32 i = 0; i < m->getNumSockets(); ++i) + cout << ";"; + } + if (m->dramEnergyMetricsAvailable()) + { + cout << "DRAM Energy (Joules)"; + for (uint32 i = 0; i < m->getNumSockets(); ++i) + cout << ";"; + } + } + + if (show_core_output) + { + for (uint32 i = 0; i < m->getNumCores(); ++i) + { + if (cpu_model == PCM::ATOM) + cout << "Core" << i << " (Socket" << setw(2) << m->getSocketId(i) << ");;;;;"; + else + cout << "Core" << i << " (Socket" << setw(2) << m->getSocketId(i) << ");;;;;;;;;;;"; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << ";"; + } + } + + // print second header line + cout << "\nDate;Time;"; + if (show_system_output) + { + if (cpu_model != PCM::ATOM) + { + if (m->L3CacheOccupancyMetricAvailable()) + cout << "EXEC;IPC;FREQ;AFREQ;L3MISS;L2MISS;L3HIT;L2HIT;L3CLK;L2CLK;L3OCC;READ;WRITE;"; + else + cout << "EXEC;IPC;FREQ;AFREQ;L3MISS;L2MISS;L3HIT;L2HIT;L3CLK;L2CLK;READ;WRITE;"; + } + else + { + cout << "EXEC;IPC;FREQ;L2MISS;L2HIT;"; + } + + + cout << "INST;ACYC;TIME(ticks);PhysIPC;PhysIPC%;INSTnom;INSTnom%;"; + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + cout << "TotalQPIin;QPItoMC;"; + if (m->outgoingQPITrafficMetricsAvailable()) + cout << "TotalQPIout;"; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << "C" << s << "res%;"; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isPackageCStateResidencySupported(s)) + cout << "C" << s << "res%;"; + + if (m->packageEnergyMetricsAvailable()) + cout << "Proc Energy (Joules);"; + if (m->dramEnergyMetricsAvailable()) + cout << "DRAM Energy (Joules);"; + } + + + if (show_socket_output) + { + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + if (cpu_model == PCM::ATOM) + cout << "EXEC;IPC;FREQ;L2MISS;L2HIT;TEMP;"; + else + { + if (m->L3CacheOccupancyMetricAvailable()) + cout << "EXEC;IPC;FREQ;AFREQ;L3MISS;L2MISS;L3HIT;L2HIT;L3CLK;L2CLK;L3OCC;READ;WRITE;TEMP;"; + else + cout << "EXEC;IPC;FREQ;AFREQ;L3MISS;L2MISS;L3HIT;L2HIT;L3CLK;L2CLK;READ;WRITE;TEMP;"; + } + } + + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + { + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + + for (uint32 s = 0; s < m->getNumSockets(); ++s) + { + for (uint32 i = 0; i < qpiLinks; ++i) + cout << "QPI" << i << ";"; + + if (m->qpiUtilizationMetricsAvailable()) + for (uint32 i = 0; i < qpiLinks; ++i) + cout << "QPI" << i << ";"; + } + + } + + if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems + { + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + for (uint32 s = 0; s < m->getNumSockets(); ++s) + { + for (uint32 i = 0; i < qpiLinks; ++i) + cout << "QPI" << i << ";"; + for (uint32 i = 0; i < qpiLinks; ++i) + cout << "QPI" << i << ";"; + } + + } + + + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << "C" << s << "res%;"; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isPackageCStateResidencySupported(s)) + cout << "C" << s << "res%;"; + } + + if (m->packageEnergyMetricsAvailable()) + { + for (uint32 i = 0; i < m->getNumSockets(); ++i) + cout << "SKT" << i << ";"; + } + if (m->dramEnergyMetricsAvailable()) + { + for (uint32 i = 0; i < m->getNumSockets(); ++i) + cout << "SKT" << i << ";"; + } + + } + + if (show_core_output) + { + for (uint32 i = 0; i < m->getNumCores(); ++i) + { + if (cpu_model == PCM::ATOM) + cout << "EXEC;IPC;FREQ;L2MISS;L2HIT;"; + else + { + if (m->L3CacheOccupancyMetricAvailable()) + cout << "EXEC;IPC;FREQ;AFREQ;L3MISS;L2MISS;L3HIT;L2HIT;L3CLK;L2CLK;L3OCC;"; + else + cout << "EXEC;IPC;FREQ;AFREQ;L3MISS;L2MISS;L3HIT;L2HIT;L3CLK;L2CLK;"; + } + + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << "C" << s << "res%;"; + + cout << "TEMP;"; + + } + + } + +} + +void print_csv(PCM * m, + const std::vector & cstates1, + const std::vector & cstates2, + const std::vector & sktstate1, + const std::vector & sktstate2, + const SystemCounterState& sstate1, + const SystemCounterState& sstate2, + const int cpu_model, + const bool show_core_output, + const bool show_socket_output, + const bool show_system_output + ) +{ +#ifndef _MSC_VER + struct timeval timestamp; + gettimeofday(×tamp, NULL); +#endif + time_t t = time(NULL); + tm *tt = localtime(&t); + char old_fill = cout.fill('0'); + cout.precision(3); + cout << endl << setw(4) << 1900 + tt->tm_year << '-' << setw(2) << 1 + tt->tm_mon << '-' + << setw(2) << tt->tm_mday << ';' << setw(2) << tt->tm_hour << ':' + << setw(2) << tt->tm_min << ':' << setw(2) << tt->tm_sec +#ifdef _MSC_VER + << ';'; +#else + << "." << setw(3) << ceil(timestamp.tv_usec / 1000) << ';'; +#endif + cout.fill(old_fill); + + if (show_system_output) + { + if (cpu_model != PCM::ATOM) + { + cout << getExecUsage(sstate1, sstate2) << + ';' << getIPC(sstate1, sstate2) << + ';' << getRelativeFrequency(sstate1, sstate2) << + ';' << getActiveRelativeFrequency(sstate1, sstate2) << + ';' << float_format(getL3CacheMisses(sstate1, sstate2)) << + ';' << float_format(getL2CacheMisses(sstate1, sstate2)) << + ';' << getL3CacheHitRatio(sstate1, sstate2) << + ';' << getL2CacheHitRatio(sstate1, sstate2) << + ';' << getCyclesLostDueL3CacheMisses(sstate1, sstate2) << + ';' << getCyclesLostDueL2CacheMisses(sstate1, sstate2) << ";"; + if (m->L3CacheOccupancyMetricAvailable()) + cout << "N/A;"; + if (!(m->memoryTrafficMetricsAvailable())) + cout << "N/A;N/A;"; + else + cout << getBytesReadFromMC(sstate1, sstate2) / double(1024ULL * 1024ULL * 1024ULL) << + ';' << getBytesWrittenToMC(sstate1, sstate2) / double(1024ULL * 1024ULL * 1024ULL) << ';'; + } + else + cout << getExecUsage(sstate1, sstate2) << + ';' << getIPC(sstate1, sstate2) << + ';' << getRelativeFrequency(sstate1, sstate2) << + ';' << float_format(getL2CacheMisses(sstate1, sstate2)) << + ';' << getL2CacheHitRatio(sstate1, sstate2) << + ';'; + + cout << float_format(getInstructionsRetired(sstate1, sstate2)) << ";" + << float_format(getCycles(sstate1, sstate2)) << ";" + << float_format(getInvariantTSC(cstates1[0], cstates2[0])) << ";" + << getCoreIPC(sstate1, sstate2) << ";" + << 100. * (getCoreIPC(sstate1, sstate2) / double(m->getMaxIPC())) << ";" + << getTotalExecUsage(sstate1, sstate2) << ";" + << 100. * (getTotalExecUsage(sstate1, sstate2) / double(m->getMaxIPC())) << ";"; + + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + cout << float_format(getAllIncomingQPILinkBytes(sstate1, sstate2)) << ";" + << getQPItoMCTrafficRatio(sstate1, sstate2) << ";"; + if (m->outgoingQPITrafficMetricsAvailable()) + cout << float_format(getAllOutgoingQPILinkBytes(sstate1, sstate2)) << ";"; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << getCoreCStateResidency(s, sstate1, sstate2) * 100 << ";"; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isPackageCStateResidencySupported(s)) + cout << getPackageCStateResidency(s, sstate1, sstate2) * 100 << ";"; + + if (m->packageEnergyMetricsAvailable()) + cout << getConsumedJoules(sstate1, sstate2) << ";"; + if (m->dramEnergyMetricsAvailable()) + cout << getDRAMConsumedJoules(sstate1, sstate2) << ";"; + + } + + + if (show_socket_output) + { + { + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + if (cpu_model == PCM::ATOM) + cout << getExecUsage(sktstate1[i], sktstate2[i]) << + ';' << getIPC(sktstate1[i], sktstate2[i]) << + ';' << getRelativeFrequency(sktstate1[i], sktstate2[i]) << + ';' << float_format(getL2CacheMisses(sktstate1[i], sktstate2[i])) << + ';' << getL2CacheHitRatio(sktstate1[i], sktstate2[i]); + else + cout << getExecUsage(sktstate1[i], sktstate2[i]) << + ';' << getIPC(sktstate1[i], sktstate2[i]) << + ';' << getRelativeFrequency(sktstate1[i], sktstate2[i]) << + ';' << getActiveRelativeFrequency(sktstate1[i], sktstate2[i]) << + ';' << float_format(getL3CacheMisses(sktstate1[i], sktstate2[i])) << + ';' << float_format(getL2CacheMisses(sktstate1[i], sktstate2[i])) << + ';' << getL3CacheHitRatio(sktstate1[i], sktstate2[i]) << + ';' << getL2CacheHitRatio(sktstate1[i], sktstate2[i]) << + ';' << getCyclesLostDueL3CacheMisses(sktstate1[i], sktstate2[i]) << + ';' << getCyclesLostDueL2CacheMisses(sktstate1[i], sktstate2[i]); + if (m->L3CacheOccupancyMetricAvailable()) + cout << ';' << l3cache_occ_format(getL3CacheOccupancy(sktstate2[i])); + if (!(m->memoryTrafficMetricsAvailable())) + cout << ";N/A;N/A"; + else + cout << ';' << getBytesReadFromMC(sktstate1[i], sktstate2[i]) / double(1024ULL * 1024ULL * 1024ULL) << + ';' << getBytesWrittenToMC(sktstate1[i], sktstate2[i]) / double(1024ULL * 1024ULL * 1024ULL); + cout << ';' << temp_format(sktstate2[i].getThermalHeadroom()) << ';'; + } + } + + if (m->getNumSockets() > 1) // QPI info only for multi socket systems + { + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + for (uint32 l = 0; l < qpiLinks; ++l) + cout << float_format(getIncomingQPILinkBytes(i, l, sstate1, sstate2)) << ";"; + + if (m->qpiUtilizationMetricsAvailable()) + { + for (uint32 l = 0; l < qpiLinks; ++l) + cout << setw(3) << std::dec << int(100. * getIncomingQPILinkUtilization(i, l, sstate1, sstate2)) << "%;"; + } + } + } + + if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems + { + const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + for (uint32 l = 0; l < qpiLinks; ++l) + cout << float_format(getOutgoingQPILinkBytes(i, l, sstate1, sstate2)) << ";"; + + for (uint32 l = 0; l < qpiLinks; ++l) + cout << setw(3) << std::dec << int(100. * getOutgoingQPILinkUtilization(i, l, sstate1, sstate2)) << "%;"; + } + } + + + for (uint32 i = 0; i < m->getNumSockets(); ++i) + { + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << getCoreCStateResidency(s, sktstate1[i], sktstate2[i]) * 100 << ";"; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isPackageCStateResidencySupported(s)) + cout << getPackageCStateResidency(s, sktstate1[i], sktstate2[i]) * 100 << ";"; + } + + if (m->packageEnergyMetricsAvailable()) + { + for (uint32 i = 0; i < m->getNumSockets(); ++i) + cout << getConsumedJoules(sktstate1[i], sktstate2[i]) << ";"; + } + if (m->dramEnergyMetricsAvailable()) + { + for (uint32 i = 0; i < m->getNumSockets(); ++i) + cout << getDRAMConsumedJoules(sktstate1[i], sktstate2[i]) << " ;"; + } + } + + if (show_core_output) + { + for (uint32 i = 0; i < m->getNumCores(); ++i) + { + if (cpu_model != PCM::ATOM) + { + cout << getExecUsage(cstates1[i], cstates2[i]) << + ';' << getIPC(cstates1[i], cstates2[i]) << + ';' << getRelativeFrequency(cstates1[i], cstates2[i]) << + ';' << getActiveRelativeFrequency(cstates1[i], cstates2[i]) << + ';' << float_format(getL3CacheMisses(cstates1[i], cstates2[i])) << + ';' << float_format(getL2CacheMisses(cstates1[i], cstates2[i])) << + ';' << getL3CacheHitRatio(cstates1[i], cstates2[i]) << + ';' << getL2CacheHitRatio(cstates1[i], cstates2[i]) << + ';' << getCyclesLostDueL3CacheMisses(cstates1[i], cstates2[i]) << + ';' << getCyclesLostDueL2CacheMisses(cstates1[i], cstates2[i]) ; + if (m->L3CacheOccupancyMetricAvailable()) + cout << ';' << l3cache_occ_format(getL3CacheOccupancy(cstates2[i])) ; + } + else + cout << getExecUsage(cstates1[i], cstates2[i]) << + ';' << getIPC(cstates1[i], cstates2[i]) << + ';' << getRelativeFrequency(cstates1[i], cstates2[i]) << + ';' << float_format(getL2CacheMisses(cstates1[i], cstates2[i])) << + ';' << getL2CacheHitRatio(cstates1[i], cstates2[i]) << + ';' << temp_format(cstates2[i].getThermalHeadroom()) << + ';'; + + for (int s = 0; s <= PCM::MAX_C_STATE; ++s) + if (m->isCoreCStateResidencySupported(s)) + cout << getCoreCStateResidency(s, cstates1[i], cstates2[i]) * 100 << ";"; + + cout << temp_format(cstates2[i].getThermalHeadroom()) << ';'; + } + } + +} + +int main(int argc, char * argv[]) +{ + set_signal_handlers(); + +#ifdef PCM_FORCE_SILENT + null_stream nullStream1, nullStream2; + std::cout.rdbuf(&nullStream1); + std::cerr.rdbuf(&nullStream2); +#endif + + cerr << endl; + cerr << " Intel(r) Performance Counter Monitor " << INTEL_PCM_VERSION << endl; + cerr << endl; + cerr << " Copyright (c) 2009-2014 Intel Corporation" << endl; + cerr << endl; + + // if delay is not specified: use either default (1 second), + // or only read counters before or after PCM started: keep PCM blocked + double delay = -1.0; + + char *sysCmd = NULL; + char **sysArgv = NULL; + bool show_core_output = true; + bool show_socket_output = true; + bool show_system_output = true; + bool csv_output = false; + bool reset_pmu = false; + bool allow_multiple_instances = false; + bool disable_JKT_workaround = false; // as per http://software.intel.com/en-us/articles/performance-impact-when-sampling-certain-llc-events-on-snb-ep-with-vtune + + long diff_usec = 0; // deviation of clock is useconds between measurements + int calibrated = PCM_CALIBRATION_INTERVAL - 2; // keeps track is the clock calibration needed + unsigned int numberOfIterations = 0; // number of iterations + string program = string(argv[0]); + + PCM * m = PCM::getInstance(); + + if (argc > 1) do + { + argv++; + argc--; + if (strncmp(*argv, "--help", 6) == 0 || + strncmp(*argv, "-h", 2) == 0 || + strncmp(*argv, "/h", 2) == 0) + { + print_help(program); + exit(EXIT_FAILURE); + } + else + if (strncmp(*argv, "--nocores", 9) == 0 || + strncmp(*argv, "-nc", 3) == 0 || + strncmp(*argv, "/nc", 3) == 0) + { + show_core_output = false; + continue; + } + else + if (strncmp(*argv, "--nosockets", 11) == 0 || + strncmp(*argv, "-ns", 3) == 0 || + strncmp(*argv, "/ns", 3) == 0) + { + show_socket_output = false; + continue; + } + else + if (strncmp(*argv, "--nosystem", 9) == 0 || + strncmp(*argv, "-nsys", 5) == 0 || + strncmp(*argv, "/nsys", 5) == 0) + { + show_system_output = false; + continue; + } + else + if (strncmp(*argv, "--multiple-instances", 20) == 0 || + strncmp(*argv, "-m", 2) == 0 || + strncmp(*argv, "/m", 2) == 0) + { + allow_multiple_instances = true; + continue; + } + else + if (strncmp(*argv, "-csv", 4) == 0 || + strncmp(*argv, "/csv", 4) == 0) + { + csv_output = true; + string cmd = string(*argv); + size_t found = cmd.find('=', 4); + if (found != string::npos) { + string filename = cmd.substr(found + 1); + if (!filename.empty()) { + m->setOutput(filename); + } + } + continue; + } + else + if (strncmp(*argv, "-i", 2) == 0 || + strncmp(*argv, "/i", 2) == 0) + { + string cmd = string(*argv); + size_t found = cmd.find('=', 2); + if (found != string::npos) { + string tmp = cmd.substr(found + 1); + if (!tmp.empty()) { + numberOfIterations = (unsigned int)atoi(tmp.c_str()); + } + } + continue; + } + else + if (strncmp(*argv, "-reset", 6) == 0 || + strncmp(*argv, "-r", 2) == 0 || + strncmp(*argv, "/reset", 6) == 0) + { + reset_pmu = true; + continue; + } + else + if (strncmp(*argv, "--noJKTWA", 9) == 0) + { + disable_JKT_workaround = true; + continue; + } +#ifdef _MSC_VER + else + if (strncmp(*argv, "--uninstallDriver", 17) == 0) + { + Driver tmpDrvObject; + tmpDrvObject.uninstall(); + cerr << "msr.sys driver has been uninstalled. You might need to reboot the system to make this effective." << endl; + exit(EXIT_SUCCESS); + } + else + if (strncmp(*argv, "--installDriver", 15) == 0) + { + TCHAR driverPath[1040]; // length for current directory + "\\msr.sys" + GetCurrentDirectory(1024, driverPath); + wcscat_s(driverPath, 1040, L"\\msr.sys"); + Driver tmpDrvObject; + if (!tmpDrvObject.start(driverPath)) + { + cerr << "Can not access CPU counters" << endl; + cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program" << endl; + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } +#endif + else + if (strncmp(*argv, "--", 2) == 0) + { + argv++; + sysCmd = *argv; + sysArgv = argv; + break; + } + else + { + // any other options positional that is a floating point number is treated as , + // while the other options are ignored with a warning issues to stderr + double delay_input; + std::istringstream is_str_stream(*argv); + is_str_stream >> noskipws >> delay_input; + if (is_str_stream.eof() && !is_str_stream.fail() && delay == -1) { + delay = delay_input; + cerr << "Delay: " << delay << endl; + } + else { + cerr << "WARNING: unknown command-line option: \"" << *argv << "\". Ignoring it." << endl; + print_help(program); + exit(EXIT_FAILURE); + } + continue; + } + } while (argc > 1); // end of command line partsing loop + + if (disable_JKT_workaround) m->disableJKTWorkaround(); + + if (reset_pmu) + { + cerr << "\n Resetting PMU configuration" << endl; + m->resetPMU(); + } + + if (allow_multiple_instances) + { + m->allowMultipleInstances(); + } + + // program() creates common semaphore for the singleton, so ideally to be called before any other references to PCM + PCM::ErrorCode status = m->program(); + + switch (status) + { + case PCM::Success: + break; + case PCM::MSRAccessDenied: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (no MSR or PCI CFG space access)." << endl; + exit(EXIT_FAILURE); + case PCM::PMUBusy: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Performance Monitoring Unit is occupied by other application). Try to stop the application that uses PMU." << endl; + cerr << "Alternatively you can try running Intel PCM with option -r to reset PMU configuration at your own risk." << endl; + exit(EXIT_FAILURE); + default: + cerr << "Access to Intel(r) Performance Counter Monitor has denied (Unknown error)." << endl; + exit(EXIT_FAILURE); + } + + cerr << "\nDetected " << m->getCPUBrandString() << " \"Intel(r) microarchitecture codename " << m->getUArchCodename() << "\"" << endl; + + std::vector cstates1, cstates2; + std::vector sktstate1, sktstate2; + SystemCounterState sstate1, sstate2; + const int cpu_model = m->getCPUModel(); + uint64 TimeAfterSleep = 0; + PCM_UNUSED(TimeAfterSleep); + + if ((sysCmd != NULL) && (delay <= 0.0)) { + // in case external command is provided in command line, and + // delay either not provided (-1) or is zero + m->setBlocked(true); + } + else { + m->setBlocked(false); + } + + if (csv_output) { + print_csv_header(m, cpu_model, show_core_output, show_socket_output, show_system_output); + if (delay <= 0.0) delay = PCM_DELAY_DEFAULT; + } + else { + // for non-CSV mode delay < 1.0 does not make a lot of practical sense: + // hard to read from the screen, or + // in case delay is not provided in command line => set default + if (((delay<1.0) && (delay>0.0)) || (delay <= 0.0)) delay = PCM_DELAY_DEFAULT; + } + // cerr << "DEBUG: Delay: " << delay << " seconds. Blocked: " << m->isBlocked() << endl; + + m->getAllCounterStates(sstate1, sktstate1, cstates1); + + if (sysCmd != NULL) { + MySystem(sysCmd, sysArgv); + } + + unsigned int i = 1; + + while ((i <= numberOfIterations) || (numberOfIterations == 0)) + { + if (!csv_output) cout << std::flush; + int delay_ms = int(delay * 1000); + int calibrated_delay_ms = delay_ms; +#ifdef _MSC_VER + // compensate slow Windows console output + if (TimeAfterSleep) delay_ms -= (uint32)(m->getTickCount() - TimeAfterSleep); + if (delay_ms < 0) delay_ms = 0; +#else + // compensation of delay on Linux/UNIX + // to make the sampling interval as monotone as possible + struct timeval start_ts, end_ts; + if (calibrated == 0) { + gettimeofday(&end_ts, NULL); + diff_usec = (end_ts.tv_sec - start_ts.tv_sec)*1000000.0 + (end_ts.tv_usec - start_ts.tv_usec); + calibrated_delay_ms = delay_ms - diff_usec / 1000.0; + } +#endif + + MySleepMs(calibrated_delay_ms); + +#ifndef _MSC_VER + calibrated = (calibrated + 1) % PCM_CALIBRATION_INTERVAL; + if (calibrated == 0) { + gettimeofday(&start_ts, NULL); + } +#endif + TimeAfterSleep = m->getTickCount(); + + m->getAllCounterStates(sstate2, sktstate2, cstates2); + + if (csv_output) + print_csv(m, cstates1, cstates2, sktstate1, sktstate2, sstate1, sstate2, + cpu_model, show_core_output, show_socket_output, show_system_output); + else + print_output(m, cstates1, cstates2, sktstate1, sktstate2, sstate1, sstate2, + cpu_model, show_core_output, show_socket_output, show_system_output); + + // sanity checks + if (cpu_model == PCM::ATOM) + { + assert(getNumberOfCustomEvents(0, sstate1, sstate2) == getL2CacheMisses(sstate1, sstate2)); + assert(getNumberOfCustomEvents(1, sstate1, sstate2) == getL2CacheMisses(sstate1, sstate2) + getL2CacheHits(sstate1, sstate2)); + } + else + { + assert(getNumberOfCustomEvents(0, sstate1, sstate2) == getL3CacheMisses(sstate1, sstate2)); + assert(getNumberOfCustomEvents(1, sstate1, sstate2) == getL3CacheHitsNoSnoop(sstate1, sstate2)); + assert(getNumberOfCustomEvents(2, sstate1, sstate2) == getL3CacheHitsSnoop(sstate1, sstate2)); + assert(getNumberOfCustomEvents(3, sstate1, sstate2) == getL2CacheHits(sstate1, sstate2)); + } + + std::swap(sstate1, sstate2); + std::swap(sktstate1, sktstate2); + std::swap(cstates1, cstates2); + + if (m->isBlocked()) { + // in case PCM was blocked after spawning child application: break monitoring loop here + break; + } + + ++i; + } + + exit(EXIT_SUCCESS); +} diff --git a/readmem.cpp b/readmem.cpp new file mode 100644 index 0000000..f706ac8 --- /dev/null +++ b/readmem.cpp @@ -0,0 +1,89 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// + +#include "cpucounters.h" +#include +#include +#include +#include +#include + +using std::cout; +using std::endl; + +inline double my_timestamp() +{ + struct timeval tp; + gettimeofday(&tp, NULL); + return double(tp.tv_sec) + tp.tv_usec / 1000000.; +} + +struct T +{ + int key[1]; + int data[15]; + + T() { } + T(int a) { key[0] = a; } + + bool operator == (const T & k) const + { + return k.key[0] == key[0]; + } +}; + +template +void Memory_intensive_task(DS & ds) +{ + std::find(ds.begin(), ds.end(), ds.size()); +} + + +int main(int argc, char * argv[]) +{ + std::vector vector; + int nelements = 13000000; + + int i = 0; + int delay = atoi(argv[1]); + + cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB" << std::endl; + + for ( ; i < nelements; ++i) + { + vector.push_back(i); + } + + double before_ts, after_ts; + + + while (1) + { + before_ts = my_timestamp(); + cout << "Reading memory for " << delay << " seconds" << std::endl; + do + { + Memory_intensive_task(vector); + after_ts = my_timestamp(); + } while ((after_ts - before_ts) < delay); + + + cout << "Sleeping for " << delay << " seconds" << std::endl; + sleep(delay); + } + + + return 0; +} diff --git a/readmem.sh b/readmem.sh new file mode 100644 index 0000000..f03659b --- /dev/null +++ b/readmem.sh @@ -0,0 +1,15 @@ + + +numactl --cpunodebind=0 --membind=0 ./readmem 10 & +numactl --cpunodebind=1 --membind=1 ./readmem 10 & +numactl --cpunodebind=0 --membind=0 ./readmem 10 & +numactl --cpunodebind=1 --membind=1 ./readmem 10 & +numactl --cpunodebind=0 --membind=0 ./readmem 10 & +numactl --cpunodebind=1 --membind=1 ./readmem 10 & +numactl --cpunodebind=0 --membind=0 ./readmem 10 & +numactl --cpunodebind=1 --membind=1 ./readmem 10 & +numactl --cpunodebind=0 --membind=0 ./readmem 10 & +numactl --cpunodebind=1 --membind=1 ./readmem 10 & +numactl --cpunodebind=0 --membind=0 ./readmem 10 & +numactl --cpunodebind=1 --membind=1 ./readmem 10 & + diff --git a/realtime.cpp b/realtime.cpp new file mode 100644 index 0000000..1734f79 --- /dev/null +++ b/realtime.cpp @@ -0,0 +1,283 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// + +#define HACK_TO_REMOVE_DUPLICATE_ERROR +#include "cpucounters.h" +#include "cpuasynchcounter.h" +#include +#include +#include +#include +#include + +/*! \file realtime.cpp + \brief Two use-cases: realtime data structure performance analysis and memory-bandwidth aware scheduling +*/ + +using std::cout; +using std::endl; + +inline double my_timestamp() +{ + struct timeval tp; + gettimeofday(&tp, NULL); + return double(tp.tv_sec) + tp.tv_usec / 1000000.; +} + +long long int fib(long long int num) +{ + long long int result = 1, a = 1, b = 1; + + for (long long int i = 3; i <= num; ++i) + { + result = a + b; + a = b; + b = result; + } + + return result; +} + +SystemCounterState before_sstate, after_sstate; +double before_time, after_time; + +AsynchronCounterState counters; + +long long int all_fib = 0; + + +void CPU_intensive_task() +{ + cout << "CPU task" << endl; + all_fib += fib(80000000ULL + (rand() % 2)); +} + + +template +void Memory_intensive_task(DS & ds) +{ + cout << "Mem task" << endl; + std::find(ds.begin(), ds.end(), ds.size()); +} + +double currentMemoryBandwidth() +{ + return (counters.getSystem() + counters.getSystem()) / (1024 * 1024); +} + +template +void measure(DS & ds, size_t repeat, size_t nelements) +{ + SystemCounterState before_sstate, after_sstate; + double before_ts, after_ts; + + // warm up + std::find(ds.begin(), ds.end(), nelements); + + double before1_ts; +#if 0 + for (int kkk = 1000; kkk > 0; --kkk) + { + ::sleep(1); + before1_ts = my_timestamp(); + + // start measuring + before_sstate = getSystemCounterState(); + before_ts = my_timestamp(); + + cout << "Response time of getSystemCounterState(): " << 1000. * (before_ts - before1_ts) << " ms" << std::endl; + } +#endif + + for (int j = 0; j < repeat; ++j) std::find(ds.begin(), ds.end(), nelements); + + // stop measuring + after_sstate = getSystemCounterState(); + after_ts = my_timestamp(); + + + cout << "\nSearch runtime: " << ((after_ts - before_ts) * 1000. / repeat) << " ms " << std::endl; + cout << "Search runtime per element: " << ((after_ts - before_ts) * 1000000000. / repeat) / nelements << " ns " << std::endl; + + cout << "Number of L2 cache misses per 1000 elements: " + << (1000. * getL2CacheMisses(before_sstate, after_sstate) / repeat) / nelements << + " \nL2 Cache hit ratio : " << getL2CacheHitRatio(before_sstate, after_sstate) * 100. << " %" << std::endl; + + + cout << "Number of L3 cache misses per 1000 elements: " + << (1000. * getL3CacheMisses(before_sstate, after_sstate) / repeat) / nelements << + " \nL3 Cache hit ratio : " << getL3CacheHitRatio(before_sstate, after_sstate) * 100. << " %" << std::endl; + + cout << "Bytes written to memory controller per element: " << + (double(getBytesWrittenToMC(before_sstate, after_sstate)) / repeat) / nelements << std::endl; + + cout << "Bytes read from memory controller per element : " << + (double(getBytesReadFromMC(before_sstate, after_sstate)) / repeat) / nelements << std::endl; + + + cout << "Used memory bandwidth: " << + ((getBytesReadFromMC(before_sstate, after_sstate) + getBytesWrittenToMC(before_sstate, after_sstate)) / (after_ts - before_ts)) / (1024 * 1024) << " MByte/sec" << std::endl; + + cout << "Instructions retired: " << getInstructionsRetired(before_sstate, after_sstate) / 1000000 << "mln" << std::endl; + + cout << "CPU cycles: " << getCycles(before_sstate, after_sstate) / 1000000 << "mln" << std::endl; + + cout << "Instructions per cycle: " << getCoreIPC(before_sstate, after_sstate) << std::endl; +} + +#if 0 +typedef int T; + +#else + +struct T +{ + int key[1]; + int data[15]; + + T() { } + T(int a) { key[0] = a; } + + bool operator == (const T & k) const + { + return k.key[0] == key[0]; + } +}; + +#endif + +int main(int argc, char * argv[]) +{ + PCM * m = PCM::getInstance(); + + if (!m->good()) + { + cout << "Can not access CPU counters" << endl; + cout << "Try to execute 'modprobe msr' as root user and then" << endl; + cout << "you also must have read and write permissions for /dev/cpu/?/msr devices (the 'chown' command can help)."; + return -1; + } + + if(m->program() != PCM::Success){ + cout << "Program was not successful..." << endl; + delete m; + return -1; + } + + int nelements = atoi(argv[1]); + + +#if 1 /* use-case: compare data structures in real-time */ + std::list list; + std::vector vector; + int i = 0; + + for ( ; i < nelements; ++i) + { + list.push_back(i); + vector.push_back(i); + } + + + unsigned long long int totalops = 200000ULL * 1000ULL * 64ULL / sizeof(T); + int repeat = totalops / nelements, j; + + cout << std::endl << std::endl << "Elements to traverse: " << totalops << std::endl; + cout << "Items in data structure: " << nelements << std::endl; + cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB" << std::endl; + cout << "Test repetions: " << repeat << std::endl; + + cout << "\n*List data structure*" << endl; + measure(list, repeat, nelements); + + cout << "\n\n*Vector/array data structure*" << endl; + measure(vector, repeat, nelements); + +#else + /* use-case: memory bandwidth-aware scheduling */ + + std::vector vector; + nelements = 13000000; + + int i = 0; + + cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB" << std::endl; + + for ( ; i < nelements; ++i) + { + vector.push_back(i); + } + + double before_ts, after_ts; + + before_ts = my_timestamp(); + { + int m_tasks = 1000; + int c_tasks = 1000; + while (m_tasks + c_tasks != 0) + { + if (m_tasks > 0) + { + Memory_intensive_task(vector); + --m_tasks; + continue; + } + + if (c_tasks > 0) + { + CPU_intensive_task(); + --c_tasks; + } + } + } + after_ts = my_timestamp(); + + cout << "In order scheduling, Running time: " << (after_ts - before_ts) << " seconds" << endl; + + + before_ts = my_timestamp(); + { + int m_tasks = 1000; + int c_tasks = 1000; + while (m_tasks + c_tasks != 0) + { + double band = currentMemoryBandwidth(); + //cout << "Mem band: "<< band << " MB/sec" << endl; + if (m_tasks > 0 && (band < (25 * 1024 /* MB/sec*/) + || c_tasks == 0)) + { + Memory_intensive_task(vector); + --m_tasks; + continue; + } + + if (c_tasks > 0) + { + CPU_intensive_task(); + --c_tasks; + continue; + } + } + } + + after_ts = my_timestamp(); + + cout << "CPU monitoring conscoius scheduling, Running time: " << (after_ts - before_ts) << " seconds" << endl; + +#endif + m->cleanup(); + + return 0; +} diff --git a/types.h b/types.h new file mode 100644 index 0000000..25ef460 --- /dev/null +++ b/types.h @@ -0,0 +1,750 @@ +/* +Copyright (c) 2009-2013, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// + +#ifndef CPUCounters_TYPES_H +#define CPUCounters_TYPES_H + + +/*! \file types.h + \brief Internal type and constant definitions +*/ + +// compile for Windows 7 or Windows Server 2008 R2 (processor group support needed for systems with high core count) +#define COMPILE_FOR_WINDOWS_7 + +#undef PCM_DEBUG + +#include +#include +#include +#include + +typedef unsigned long long uint64; +typedef signed long long int64; +typedef unsigned int uint32; +typedef signed int int32; + + +/* + MSR addreses from + "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: + System Programming Guide, Part 2", Appendix A "PERFORMANCE-MONITORING EVENTS" +*/ + +#define INST_RETIRED_ANY_ADDR (0x309) +#define CPU_CLK_UNHALTED_THREAD_ADDR (0x30A) +#define CPU_CLK_UNHALTED_REF_ADDR (0x30B) +#define IA32_CR_PERF_GLOBAL_CTRL (0x38F) +#define IA32_CR_FIXED_CTR_CTRL (0x38D) +#define IA32_PERFEVTSEL0_ADDR (0x186) +#define IA32_PERFEVTSEL1_ADDR (IA32_PERFEVTSEL0_ADDR + 1) +#define IA32_PERFEVTSEL2_ADDR (IA32_PERFEVTSEL0_ADDR + 2) +#define IA32_PERFEVTSEL3_ADDR (IA32_PERFEVTSEL0_ADDR + 3) + +#define PERF_MAX_COUNTERS (7) + +#define IA32_DEBUGCTL (0x1D9) + +#define IA32_PMC0 (0xC1) +#define IA32_PMC1 (0xC1 + 1) +#define IA32_PMC2 (0xC1 + 2) +#define IA32_PMC3 (0xC1 + 3) + +#define MSR_OFFCORE_RSP0 (0x1A6) +#define MSR_OFFCORE_RSP1 (0x1A7) + +/* From Table B-5. of the above mentioned document */ +#define PLATFORM_INFO_ADDR (0xCE) + +#define IA32_TIME_STAMP_COUNTER (0x10) + +// Event IDs + +// Nehalem/Westmere on-core events +#define MEM_LOAD_RETIRED_L3_MISS_EVTNR (0xCB) +#define MEM_LOAD_RETIRED_L3_MISS_UMASK (0x10) + +#define MEM_LOAD_RETIRED_L3_UNSHAREDHIT_EVTNR (0xCB) +#define MEM_LOAD_RETIRED_L3_UNSHAREDHIT_UMASK (0x04) + +#define MEM_LOAD_RETIRED_L2_HITM_EVTNR (0xCB) +#define MEM_LOAD_RETIRED_L2_HITM_UMASK (0x08) + +#define MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) +#define MEM_LOAD_RETIRED_L2_HIT_UMASK (0x02) + +// Sandy Bridge on-core events + +#define MEM_LOAD_UOPS_MISC_RETIRED_LLC_MISS_EVTNR (0xD4) +#define MEM_LOAD_UOPS_MISC_RETIRED_LLC_MISS_UMASK (0x02) + +#define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_EVTNR (0xD2) +#define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_UMASK (0x08) + +#define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_HITM_EVTNR (0xD2) +#define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_HITM_UMASK (0x04) + +#define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_EVTNR (0xD2) +#define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_UMASK (0x07) + +#define MEM_LOAD_UOPS_RETIRED_L2_HIT_EVTNR (0xD1) +#define MEM_LOAD_UOPS_RETIRED_L2_HIT_UMASK (0x02) + +// architectural on-core events + +#define ARCH_LLC_REFERENCE_EVTNR (0x2E) +#define ARCH_LLC_REFERENCE_UMASK (0x4F) + +#define ARCH_LLC_MISS_EVTNR (0x2E) +#define ARCH_LLC_MISS_UMASK (0x41) + +// Atom on-core events + +#define ATOM_MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) +#define ATOM_MEM_LOAD_RETIRED_L2_HIT_UMASK (0x01) + +#define ATOM_MEM_LOAD_RETIRED_L2_MISS_EVTNR (0xCB) +#define ATOM_MEM_LOAD_RETIRED_L2_MISS_UMASK (0x02) + +#define ATOM_MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) +#define ATOM_MEM_LOAD_RETIRED_L2_HIT_UMASK (0x01) + +#define ATOM_MEM_LOAD_RETIRED_L2_MISS_EVTNR (0xCB) +#define ATOM_MEM_LOAD_RETIRED_L2_MISS_UMASK (0x02) + +#define ATOM_MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) +#define ATOM_MEM_LOAD_RETIRED_L2_HIT_UMASK (0x01) + +#define ATOM_MEM_LOAD_RETIRED_L2_MISS_EVTNR (0xCB) +#define ATOM_MEM_LOAD_RETIRED_L2_MISS_UMASK (0x02) + +/* + For Nehalem(-EP) processors from Intel(r) 64 and IA-32 Architectures Software Developer's Manual +*/ + +// Uncore msrs + +#define MSR_UNCORE_PERF_GLOBAL_CTRL_ADDR (0x391) + +#define MSR_UNCORE_PERFEVTSEL0_ADDR (0x3C0) +#define MSR_UNCORE_PERFEVTSEL1_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 1) +#define MSR_UNCORE_PERFEVTSEL2_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 2) +#define MSR_UNCORE_PERFEVTSEL3_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 3) +#define MSR_UNCORE_PERFEVTSEL4_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 4) +#define MSR_UNCORE_PERFEVTSEL5_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 5) +#define MSR_UNCORE_PERFEVTSEL6_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 6) +#define MSR_UNCORE_PERFEVTSEL7_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 7) + + +#define MSR_UNCORE_PMC0 (0x3B0) +#define MSR_UNCORE_PMC1 (MSR_UNCORE_PMC0 + 1) +#define MSR_UNCORE_PMC2 (MSR_UNCORE_PMC0 + 2) +#define MSR_UNCORE_PMC3 (MSR_UNCORE_PMC0 + 3) +#define MSR_UNCORE_PMC4 (MSR_UNCORE_PMC0 + 4) +#define MSR_UNCORE_PMC5 (MSR_UNCORE_PMC0 + 5) +#define MSR_UNCORE_PMC6 (MSR_UNCORE_PMC0 + 6) +#define MSR_UNCORE_PMC7 (MSR_UNCORE_PMC0 + 7) + +// Uncore event IDs + +#define UNC_QMC_WRITES_FULL_ANY_EVTNR (0x2F) +#define UNC_QMC_WRITES_FULL_ANY_UMASK (0x07) + +#define UNC_QMC_NORMAL_READS_ANY_EVTNR (0x2C) +#define UNC_QMC_NORMAL_READS_ANY_UMASK (0x07) + +#define UNC_QHL_REQUESTS_EVTNR (0x20) + +#define UNC_QHL_REQUESTS_IOH_READS_UMASK (0x01) +#define UNC_QHL_REQUESTS_IOH_WRITES_UMASK (0x02) +#define UNC_QHL_REQUESTS_REMOTE_READS_UMASK (0x04) +#define UNC_QHL_REQUESTS_REMOTE_WRITES_UMASK (0x08) +#define UNC_QHL_REQUESTS_LOCAL_READS_UMASK (0x10) +#define UNC_QHL_REQUESTS_LOCAL_WRITES_UMASK (0x20) + +/* + From "Intel(r) Xeon(r) Processor 7500 Series Uncore Programming Guide" +*/ + +// Beckton uncore event IDs + +#define U_MSR_PMON_GLOBAL_CTL (0x0C00) + +#define MB0_MSR_PERF_GLOBAL_CTL (0x0CA0) +#define MB0_MSR_PMU_CNT_0 (0x0CB1) +#define MB0_MSR_PMU_CNT_CTL_0 (0x0CB0) +#define MB0_MSR_PMU_CNT_1 (0x0CB3) +#define MB0_MSR_PMU_CNT_CTL_1 (0x0CB2) +#define MB0_MSR_PMU_ZDP_CTL_FVC (0x0CAB) + + +#define MB1_MSR_PERF_GLOBAL_CTL (0x0CE0) +#define MB1_MSR_PMU_CNT_0 (0x0CF1) +#define MB1_MSR_PMU_CNT_CTL_0 (0x0CF0) +#define MB1_MSR_PMU_CNT_1 (0x0CF3) +#define MB1_MSR_PMU_CNT_CTL_1 (0x0CF2) +#define MB1_MSR_PMU_ZDP_CTL_FVC (0x0CEB) + +#define BB0_MSR_PERF_GLOBAL_CTL (0x0C20) +#define BB0_MSR_PERF_CNT_1 (0x0C33) +#define BB0_MSR_PERF_CNT_CTL_1 (0x0C32) + +#define BB1_MSR_PERF_GLOBAL_CTL (0x0C60) +#define BB1_MSR_PERF_CNT_1 (0x0C73) +#define BB1_MSR_PERF_CNT_CTL_1 (0x0C72) + +#define R_MSR_PMON_CTL0 (0x0E10) +#define R_MSR_PMON_CTR0 (0x0E11) +#define R_MSR_PMON_CTL1 (0x0E12) +#define R_MSR_PMON_CTR1 (0x0E13) +#define R_MSR_PMON_CTL2 (0x0E14) +#define R_MSR_PMON_CTR2 (0x0E15) +#define R_MSR_PMON_CTL3 (0x0E16) +#define R_MSR_PMON_CTR3 (0x0E17) +#define R_MSR_PMON_CTL4 (0x0E18) +#define R_MSR_PMON_CTR4 (0x0E19) +#define R_MSR_PMON_CTL5 (0x0E1A) +#define R_MSR_PMON_CTR5 (0x0E1B) +#define R_MSR_PMON_CTL6 (0x0E1C) +#define R_MSR_PMON_CTR6 (0x0E1D) +#define R_MSR_PMON_CTL7 (0x0E1E) +#define R_MSR_PMON_CTR7 (0x0E1F) +#define R_MSR_PMON_CTL8 (0x0E30) +#define R_MSR_PMON_CTR8 (0x0E31) +#define R_MSR_PMON_CTL9 (0x0E32) +#define R_MSR_PMON_CTR9 (0x0E33) +#define R_MSR_PMON_CTL10 (0x0E34) +#define R_MSR_PMON_CTR10 (0x0E35) +#define R_MSR_PMON_CTL11 (0x0E36) +#define R_MSR_PMON_CTR11 (0x0E37) +#define R_MSR_PMON_CTL12 (0x0E38) +#define R_MSR_PMON_CTR12 (0x0E39) +#define R_MSR_PMON_CTL13 (0x0E3A) +#define R_MSR_PMON_CTR13 (0x0E3B) +#define R_MSR_PMON_CTL14 (0x0E3C) +#define R_MSR_PMON_CTR14 (0x0E3D) +#define R_MSR_PMON_CTL15 (0x0E3E) +#define R_MSR_PMON_CTR15 (0x0E3F) + +#define R_MSR_PORT0_IPERF_CFG0 (0x0E04) +#define R_MSR_PORT1_IPERF_CFG0 (0x0E05) +#define R_MSR_PORT2_IPERF_CFG0 (0x0E06) +#define R_MSR_PORT3_IPERF_CFG0 (0x0E07) +#define R_MSR_PORT4_IPERF_CFG0 (0x0E08) +#define R_MSR_PORT5_IPERF_CFG0 (0x0E09) +#define R_MSR_PORT6_IPERF_CFG0 (0x0E0A) +#define R_MSR_PORT7_IPERF_CFG0 (0x0E0B) + +#define R_MSR_PORT0_IPERF_CFG1 (0x0E24) +#define R_MSR_PORT1_IPERF_CFG1 (0x0E25) +#define R_MSR_PORT2_IPERF_CFG1 (0x0E26) +#define R_MSR_PORT3_IPERF_CFG1 (0x0E27) +#define R_MSR_PORT4_IPERF_CFG1 (0x0E28) +#define R_MSR_PORT5_IPERF_CFG1 (0x0E29) +#define R_MSR_PORT6_IPERF_CFG1 (0x0E2A) +#define R_MSR_PORT7_IPERF_CFG1 (0x0E2B) + +#define R_MSR_PMON_GLOBAL_CTL_7_0 (0x0E00) +#define R_MSR_PMON_GLOBAL_CTL_15_8 (0x0E20) + +#define W_MSR_PMON_GLOBAL_CTL (0xC80) +#define W_MSR_PMON_FIXED_CTR_CTL (0x395) +#define W_MSR_PMON_FIXED_CTR (0x394) + +/* + * Platform QoS MSRs + */ + +#define IA32_PQR_ASSOC (0xc8f) +#define IA32_QM_EVTSEL (0xc8d) +#define IA32_QM_CTR (0xc8e) + +#define PCM_INVALID_L3_CACHE_OCCUPANCY ((std::numeric_limits::max)()) + +/* \brief Event Select Register format + + According to + "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: + System Programming Guide, Part 2", Figure 30-6. Layout of IA32_PERFEVTSELx + MSRs Supporting Architectural Performance Monitoring Version 3 +*/ +struct EventSelectRegister +{ + union + { + struct + { + uint64 event_select : 8; + uint64 umask : 8; + uint64 usr : 1; + uint64 os : 1; + uint64 edge : 1; + uint64 pin_control : 1; + uint64 apic_int : 1; + uint64 any_thread : 1; + uint64 enable : 1; + uint64 invert : 1; + uint64 cmask : 8; + uint64 in_tx : 1; + uint64 in_txcp : 1; + uint64 reservedX : 30; + } fields; + uint64 value; + }; +}; + + +/* \brief Fixed Event Control Register format + + According to + "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: + System Programming Guide, Part 2", Figure 30-7. Layout of + IA32_FIXED_CTR_CTRL MSR Supporting Architectural Performance Monitoring Version 3 +*/ +struct FixedEventControlRegister +{ + union + { + struct + { + // CTR0 + uint64 os0 : 1; + uint64 usr0 : 1; + uint64 any_thread0 : 1; + uint64 enable_pmi0 : 1; + // CTR1 + uint64 os1 : 1; + uint64 usr1 : 1; + uint64 any_thread1 : 1; + uint64 enable_pmi1 : 1; + // CTR2 + uint64 os2 : 1; + uint64 usr2 : 1; + uint64 any_thread2 : 1; + uint64 enable_pmi2 : 1; + + uint64 reserved1 : 52; + } fields; + uint64 value; + }; +}; + +inline std::ostream & operator << (std::ostream & o, const FixedEventControlRegister & reg) +{ + o << "os0\t\t" << reg.fields.os0 << std::endl; + o << "usr0\t\t" << reg.fields.usr0 << std::endl; + o << "any_thread0\t" << reg.fields.any_thread0 << std::endl; + o << "enable_pmi0\t" << reg.fields.enable_pmi0 << std::endl; + + o << "os1\t\t" << reg.fields.os1 << std::endl; + o << "usr1\t\t" << reg.fields.usr1 << std::endl; + o << "any_thread1\t" << reg.fields.any_thread1 << std::endl; + o << "enable_pmi10\t" << reg.fields.enable_pmi1 << std::endl; + + o << "os2\t\t" << reg.fields.os2 << std::endl; + o << "usr2\t\t" << reg.fields.usr2 << std::endl; + o << "any_thread2\t" << reg.fields.any_thread2 << std::endl; + o << "enable_pmi2\t" << reg.fields.enable_pmi2 << std::endl; + + o << "reserved1\t" << reg.fields.reserved1 << std::endl; + return o; +} + +// UNCORE COUNTER CONTROL + +/* \brief Uncore Event Select Register Register format + + According to + "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: + System Programming Guide, Part 2", Figure 30-20. Layout of MSR_UNCORE_PERFEVTSELx MSRs +*/ +struct UncoreEventSelectRegister +{ + union + { + struct + { + uint64 event_select : 8; + uint64 umask : 8; + uint64 reserved1 : 1; + uint64 occ_ctr_rst : 1; + uint64 edge : 1; + uint64 reserved2 : 1; + uint64 enable_pmi : 1; + uint64 reserved3 : 1; + uint64 enable : 1; + uint64 invert : 1; + uint64 cmask : 8; + uint64 reservedx : 32; + } fields; + uint64 value; + }; +}; + +/* \brief Beckton Uncore PMU ZDP FVC Control Register format + + From "Intel(r) Xeon(r) Processor 7500 Series Uncore Programming Guide" + Table 2-80. M_MSR_PMU_ZDP_CTL_FVC Register - Field Definitions +*/ +struct BecktonUncorePMUZDPCTLFVCRegister +{ + union + { + struct + { + uint64 fvid : 5; + uint64 bcmd : 3; + uint64 resp : 3; + uint64 evnt0 : 3; + uint64 evnt1 : 3; + uint64 evnt2 : 3; + uint64 evnt3 : 3; + uint64 pbox_init_err : 1; + } fields; // nehalem-ex version + struct + { + uint64 fvid : 6; + uint64 bcmd : 3; + uint64 resp : 3; + uint64 evnt0 : 3; + uint64 evnt1 : 3; + uint64 evnt2 : 3; + uint64 evnt3 : 3; + uint64 pbox_init_err : 1; + } fields_wsm; // westmere-ex version + uint64 value; + }; +}; + +/* \brief Beckton Uncore PMU Counter Control Register format + + From "Intel(r) Xeon(r) Processor 7500 Series Uncore Programming Guide" + Table 2-67. M_MSR_PMU_CNT_CTL{5-0} Register - Field Definitions +*/ +struct BecktonUncorePMUCNTCTLRegister +{ + union + { + struct + { + uint64 en : 1; + uint64 pmi_en : 1; + uint64 count_mode : 2; + uint64 storage_mode : 2; + uint64 wrap_mode : 1; + uint64 flag_mode : 1; + uint64 rsv1 : 1; + uint64 inc_sel : 5; + uint64 rsv2 : 5; + uint64 set_flag_sel : 3; + } fields; + uint64 value; + }; +}; + +/* \brief Sandy Bridge energy counters +*/ + +#define MSR_PKG_ENERGY_STATUS (0x611) +#define MSR_RAPL_POWER_UNIT (0x606) +#define MSR_PKG_POWER_INFO (0x614) + +#define PCM_INTEL_PCI_VENDOR_ID (0x8086) +#define PCM_PCI_VENDOR_ID_OFFSET (0) + +// server PCICFG uncore counters + +#define JKTIVT_MC0_CH0_REGISTER_DEV_ADDR (16) +#define JKTIVT_MC0_CH1_REGISTER_DEV_ADDR (16) +#define JKTIVT_MC0_CH2_REGISTER_DEV_ADDR (16) +#define JKTIVT_MC0_CH3_REGISTER_DEV_ADDR (16) +#define JKTIVT_MC0_CH0_REGISTER_FUNC_ADDR (4) +#define JKTIVT_MC0_CH1_REGISTER_FUNC_ADDR (5) +#define JKTIVT_MC0_CH2_REGISTER_FUNC_ADDR (0) +#define JKTIVT_MC0_CH3_REGISTER_FUNC_ADDR (1) + +#define JKTIVT_MC1_CH0_REGISTER_DEV_ADDR (30) +#define JKTIVT_MC1_CH1_REGISTER_DEV_ADDR (30) +#define JKTIVT_MC1_CH2_REGISTER_DEV_ADDR (30) +#define JKTIVT_MC1_CH3_REGISTER_DEV_ADDR (30) +#define JKTIVT_MC1_CH0_REGISTER_FUNC_ADDR (4) +#define JKTIVT_MC1_CH1_REGISTER_FUNC_ADDR (5) +#define JKTIVT_MC1_CH2_REGISTER_FUNC_ADDR (0) +#define JKTIVT_MC1_CH3_REGISTER_FUNC_ADDR (1) + +#define HSX_MC0_CH0_REGISTER_DEV_ADDR (20) +#define HSX_MC0_CH1_REGISTER_DEV_ADDR (20) +#define HSX_MC0_CH2_REGISTER_DEV_ADDR (21) +#define HSX_MC0_CH3_REGISTER_DEV_ADDR (21) +#define HSX_MC0_CH0_REGISTER_FUNC_ADDR (0) +#define HSX_MC0_CH1_REGISTER_FUNC_ADDR (1) +#define HSX_MC0_CH2_REGISTER_FUNC_ADDR (0) +#define HSX_MC0_CH3_REGISTER_FUNC_ADDR (1) + +#define HSX_MC1_CH0_REGISTER_DEV_ADDR (23) +#define HSX_MC1_CH1_REGISTER_DEV_ADDR (23) +#define HSX_MC1_CH2_REGISTER_DEV_ADDR (24) +#define HSX_MC1_CH3_REGISTER_DEV_ADDR (24) +#define HSX_MC1_CH0_REGISTER_FUNC_ADDR (0) +#define HSX_MC1_CH1_REGISTER_FUNC_ADDR (1) +#define HSX_MC1_CH2_REGISTER_FUNC_ADDR (0) +#define HSX_MC1_CH3_REGISTER_FUNC_ADDR (1) + +#define MC_CH_PCI_PMON_BOX_CTL_ADDR (0x0F4) + +#define MC_CH_PCI_PMON_FIXED_CTL_ADDR (0x0F0) +#define MC_CH_PCI_PMON_CTL3_ADDR (0x0E4) +#define MC_CH_PCI_PMON_CTL2_ADDR (0x0E0) +#define MC_CH_PCI_PMON_CTL1_ADDR (0x0DC) +#define MC_CH_PCI_PMON_CTL0_ADDR (0x0D8) + +#define MC_CH_PCI_PMON_FIXED_CTR_ADDR (0x0D0) +#define MC_CH_PCI_PMON_CTR3_ADDR (0x0B8) +#define MC_CH_PCI_PMON_CTR2_ADDR (0x0B0) +#define MC_CH_PCI_PMON_CTR1_ADDR (0x0A8) +#define MC_CH_PCI_PMON_CTR0_ADDR (0x0A0) + +#define JKTIVT_QPI_PORT0_REGISTER_DEV_ADDR (8) +#define JKTIVT_QPI_PORT0_REGISTER_FUNC_ADDR (2) +#define JKTIVT_QPI_PORT1_REGISTER_DEV_ADDR (9) +#define JKTIVT_QPI_PORT1_REGISTER_FUNC_ADDR (2) +#define JKTIVT_QPI_PORT2_REGISTER_DEV_ADDR (24) +#define JKTIVT_QPI_PORT2_REGISTER_FUNC_ADDR (2) + +#define HSX_QPI_PORT0_REGISTER_DEV_ADDR (8) +#define HSX_QPI_PORT0_REGISTER_FUNC_ADDR (2) +#define HSX_QPI_PORT1_REGISTER_DEV_ADDR (9) +#define HSX_QPI_PORT1_REGISTER_FUNC_ADDR (2) +#define HSX_QPI_PORT2_REGISTER_DEV_ADDR (10) +#define HSX_QPI_PORT2_REGISTER_FUNC_ADDR (2) + +#define QPI_PORT0_MISC_REGISTER_FUNC_ADDR (0) +#define QPI_PORT1_MISC_REGISTER_FUNC_ADDR (0) +#define QPI_PORT2_MISC_REGISTER_FUNC_ADDR (0) + +#define Q_P_PCI_PMON_BOX_CTL_ADDR (0x0F4) + +#define Q_P_PCI_PMON_CTL3_ADDR (0x0E4) +#define Q_P_PCI_PMON_CTL2_ADDR (0x0E0) +#define Q_P_PCI_PMON_CTL1_ADDR (0x0DC) +#define Q_P_PCI_PMON_CTL0_ADDR (0x0D8) + +#define Q_P_PCI_PMON_CTR3_ADDR (0x0B8) +#define Q_P_PCI_PMON_CTR2_ADDR (0x0B0) +#define Q_P_PCI_PMON_CTR1_ADDR (0x0A8) +#define Q_P_PCI_PMON_CTR0_ADDR (0x0A0) + +#define QPI_RATE_STATUS_ADDR (0x0D4) + +#define JKTIVT_PCU_MSR_PMON_CTR3_ADDR (0x0C39) +#define JKTIVT_PCU_MSR_PMON_CTR2_ADDR (0x0C38) +#define JKTIVT_PCU_MSR_PMON_CTR1_ADDR (0x0C37) +#define JKTIVT_PCU_MSR_PMON_CTR0_ADDR (0x0C36) + +#define JKTIVT_PCU_MSR_PMON_BOX_FILTER_ADDR (0x0C34) + +#define JKTIVT_PCU_MSR_PMON_CTL3_ADDR (0x0C33) +#define JKTIVT_PCU_MSR_PMON_CTL2_ADDR (0x0C32) +#define JKTIVT_PCU_MSR_PMON_CTL1_ADDR (0x0C31) +#define JKTIVT_PCU_MSR_PMON_CTL0_ADDR (0x0C30) + +#define JKTIVT_PCU_MSR_PMON_BOX_CTL_ADDR (0x0C24) + +#define HSX_PCU_MSR_PMON_CTR3_ADDR (0x071A) +#define HSX_PCU_MSR_PMON_CTR2_ADDR (0x0719) +#define HSX_PCU_MSR_PMON_CTR1_ADDR (0x0718) +#define HSX_PCU_MSR_PMON_CTR0_ADDR (0x0717) + +#define HSX_PCU_MSR_PMON_BOX_FILTER_ADDR (0x0715) + +#define HSX_PCU_MSR_PMON_CTL3_ADDR (0x0714) +#define HSX_PCU_MSR_PMON_CTL2_ADDR (0x0713) +#define HSX_PCU_MSR_PMON_CTL1_ADDR (0x0712) +#define HSX_PCU_MSR_PMON_CTL0_ADDR (0x0711) + +#define HSX_PCU_MSR_PMON_BOX_CTL_ADDR (0x0710) + +#define MC_CH_PCI_PMON_BOX_CTL_RST_CONTROL (1<<0) +#define MC_CH_PCI_PMON_BOX_CTL_RST_COUNTERS (1<<1) +#define MC_CH_PCI_PMON_BOX_CTL_FRZ (1<<8) +#define MC_CH_PCI_PMON_BOX_CTL_FRZ_EN (1<<16) + +#define UNCORE_PMON_BOX_CTL_VALID_BITS_MASK ((1<<17)-1) + +#define MC_CH_PCI_PMON_FIXED_CTL_RST (1<<19) +#define MC_CH_PCI_PMON_FIXED_CTL_EN (1<<22) + +#define MC_CH_PCI_PMON_CTL_EVENT(x) (x<<0) +#define MC_CH_PCI_PMON_CTL_UMASK(x) (x<<8) +#define MC_CH_PCI_PMON_CTL_RST (1<<17) +#define MC_CH_PCI_PMON_CTL_EDGE_DET (1<<18) +#define MC_CH_PCI_PMON_CTL_EN (1<<22) +#define MC_CH_PCI_PMON_CTL_INVERT (1<<23) +#define MC_CH_PCI_PMON_CTL_THRESH(x) (x<<24UL) + +#define Q_P_PCI_PMON_BOX_CTL_RST_CONTROL (1<<0) +#define Q_P_PCI_PMON_BOX_CTL_RST_COUNTERS (1<<1) +#define Q_P_PCI_PMON_BOX_CTL_RST_FRZ (1<<8) +#define Q_P_PCI_PMON_BOX_CTL_RST_FRZ_EN (1<<16) + +#define Q_P_PCI_PMON_CTL_EVENT(x) (x<<0) +#define Q_P_PCI_PMON_CTL_UMASK(x) (x<<8) +#define Q_P_PCI_PMON_CTL_RST (1<<17) +#define Q_P_PCI_PMON_CTL_EDGE_DET (1<<18) +#define Q_P_PCI_PMON_CTL_EVENT_EXT (1<<21) +#define Q_P_PCI_PMON_CTL_EN (1<<22) +#define Q_P_PCI_PMON_CTL_INVERT (1<<23) +#define Q_P_PCI_PMON_CTL_THRESH(x) (x<<24UL) + +#define PCU_MSR_PMON_BOX_FILTER_BAND_0(x) (x<<0) +#define PCU_MSR_PMON_BOX_FILTER_BAND_1(x) (x<<8) +#define PCU_MSR_PMON_BOX_FILTER_BAND_2(x) (x<<16) +#define PCU_MSR_PMON_BOX_FILTER_BAND_3(x) (x<<24) + +#define PCU_MSR_PMON_BOX_CTL_RST_CONTROL (1<<0) +#define PCU_MSR_PMON_BOX_CTL_RST_COUNTERS (1<<1) +#define PCU_MSR_PMON_BOX_CTL_FRZ (1<<8) +#define PCU_MSR_PMON_BOX_CTL_FRZ_EN (1<<16) + +#define PCU_MSR_PMON_CTL_EVENT(x) (x<<0) +#define PCU_MSR_PMON_CTL_OCC_SEL(x) (x<<14) +#define PCU_MSR_PMON_CTL_RST (1<<17) +#define PCU_MSR_PMON_CTL_EDGE_DET (1<<18) +#define PCU_MSR_PMON_CTL_EXTRA_SEL (1<<21) +#define PCU_MSR_PMON_CTL_EN (1<<22) +#define PCU_MSR_PMON_CTL_INVERT (1<<23) +#define PCU_MSR_PMON_CTL_THRESH(x) (x<<24UL) +#define PCU_MSR_PMON_CTL_OCC_INVERT (1UL<<30UL) +#define PCU_MSR_PMON_CTL_OCC_EDGE_DET (1UL<<31UL) + + +#define JKT_C0_MSR_PMON_CTR3 0x0D19 // CBo 0 PMON Counter 3 +#define JKT_C0_MSR_PMON_CTR2 0x0D18 // CBo 0 PMON Counter 2 +#define JKT_C0_MSR_PMON_CTR1 0x0D17 // CBo 0 PMON Counter 1 +#define JKT_C0_MSR_PMON_CTR0 0x0D16 // CBo 0 PMON Counter 0 +#define JKT_C0_MSR_PMON_BOX_FILTER 0x0D14 // CBo 0 PMON Filter +#define JKT_C0_MSR_PMON_CTL3 0x0D13 // CBo 0 PMON Control for Counter 3 +#define JKT_C0_MSR_PMON_CTL2 0x0D12 // CBo 0 PMON Control for Counter 2 +#define JKT_C0_MSR_PMON_CTL1 0x0D11 // CBo 0 PMON Control for Counter 1 +#define JKT_C0_MSR_PMON_CTL0 0x0D10 // CBo 0 PMON Control for Counter 0 +#define JKT_C0_MSR_PMON_BOX_CTL 0x0D04 // CBo 0 PMON Box-Wide Control + +#define JKTIVT_CBO_MSR_STEP 0x0020 // CBo MSR Step + +#define IVT_C0_MSR_PMON_BOX_FILTER1 0x0D1A // CBo 0 PMON Filter 1 + +#define HSX_C0_MSR_PMON_CTR3 0x0E0B // CBo 0 PMON Counter 3 +#define HSX_C0_MSR_PMON_CTR2 0x0E0A // CBo 0 PMON Counter 2 +#define HSX_C0_MSR_PMON_CTR1 0x0E09 // CBo 0 PMON Counter 1 +#define HSX_C0_MSR_PMON_CTR0 0x0E08 // CBo 0 PMON Counter 0 + +#define HSX_C0_MSR_PMON_BOX_FILTER1 0x0E06 // CBo 0 PMON Filter1 +#define HSX_C0_MSR_PMON_BOX_FILTER 0x0E05 // CBo 0 PMON Filter0 + +#define HSX_C0_MSR_PMON_CTL3 0x0E04 // CBo 0 PMON Control for Counter 3 +#define HSX_C0_MSR_PMON_CTL2 0x0E03 // CBo 0 PMON Control for Counter 2 +#define HSX_C0_MSR_PMON_CTL1 0x0E02 // CBo 0 PMON Control for Counter 1 +#define HSX_C0_MSR_PMON_CTL0 0x0E01 // CBo 0 PMON Control for Counter 0 + +#define HSX_C0_MSR_PMON_BOX_STATUS 0x0E07 // CBo 0 PMON Box-Wide Status +#define HSX_C0_MSR_PMON_BOX_CTL 0x0E00 // CBo 0 PMON Box-Wide Control + +#define HSX_CBO_MSR_STEP 0x0010 // CBo MSR Step + +#define CBO_MSR_PMON_BOX_CTL_RST_CONTROL (1<<0) +#define CBO_MSR_PMON_BOX_CTL_RST_COUNTERS (1<<1) +#define CBO_MSR_PMON_BOX_CTL_FRZ (1<<8) +#define CBO_MSR_PMON_BOX_CTL_FRZ_EN (1<<16) + +#define CBO_MSR_PMON_CTL_EVENT(x) (x<<0) +#define CBO_MSR_PMON_CTL_UMASK(x) (x<<8) +#define CBO_MSR_PMON_CTL_RST (1<<17) +#define CBO_MSR_PMON_CTL_EDGE_DET (1<<18) +#define CBO_MSR_PMON_CTL_TID_EN (1<<19) +#define CBO_MSR_PMON_CTL_EN (1<<22) +#define CBO_MSR_PMON_CTL_INVERT (1<<23) +#define CBO_MSR_PMON_CTL_THRESH(x) (x<<24UL) + +#define JKT_CBO_MSR_PMON_BOX_FILTER_OPC(x) (x<<23UL) +#define IVTHSX_CBO_MSR_PMON_BOX_FILTER1_OPC(x) (x<<20UL) + +#define MSR_PACKAGE_THERM_STATUS (0x01B1) +#define MSR_IA32_THERM_STATUS (0x019C) +#define PCM_INVALID_THERMAL_HEADROOM ((std::numeric_limits::min)()) + +#define MSR_DRAM_ENERGY_STATUS (0x0619) + +#define MSR_PKG_C2_RESIDENCY (0x60D) +#define MSR_PKG_C3_RESIDENCY (0x3F8) +#define MSR_PKG_C6_RESIDENCY (0x3F9) +#define MSR_PKG_C7_RESIDENCY (0x3FA) +#define MSR_CORE_C3_RESIDENCY (0x3FC) +#define MSR_CORE_C6_RESIDENCY (0x3FD) +#define MSR_CORE_C7_RESIDENCY (0x3FE) + +#ifdef _MSC_VER +#include +// data structure for converting two uint32s <-> uin64 +union cvt_ds +{ + UINT64 ui64; + struct + { + DWORD low; + DWORD high; + } ui32; +}; + +#endif + +struct MCFGRecord +{ + unsigned long long baseAddress; + unsigned short PCISegmentGroupNumber; + unsigned char startBusNumber; + unsigned char endBusNumber; + char reserved[4]; + void print() + { + std::cout <<"BaseAddress="<< (std::hex) << "0x"< +#include +#include +#include +#include +#ifdef _MSC_VER +#include +#include +#else +#include // for waitpid() +#endif +#include "utils.h" + +//! \brief handler of exit() call +void exit_cleanup(void) +{ + std::cout << std::flush; + + restore_signal_handlers(); + + // this replaces same call in cleanup() from util.h + PCM::getInstance()->cleanup(); // this replaces same call in cleanup() from util.h + +//TODO: delete other shared objects.... if any. + +// now terminate the program immediately + _exit(EXIT_SUCCESS); +} + +#ifdef _MSC_VER + +#ifdef COMPILE_FOR_WINDOWS_7 +ThreadGroupTempAffinity::ThreadGroupTempAffinity(uint32 core_id) +{ + GROUP_AFFINITY NewGroupAffinity; + memset(&NewGroupAffinity, 0, sizeof(GROUP_AFFINITY)); + memset(&PreviousGroupAffinity, 0, sizeof(GROUP_AFFINITY)); + uint32 currentGroupSize = 0; + + while (core_id >= (currentGroupSize = GetMaximumProcessorCount(NewGroupAffinity.Group))) + { + core_id -= currentGroupSize; + ++NewGroupAffinity.Group; + } + NewGroupAffinity.Mask = 1ULL << core_id; + SetThreadGroupAffinity(GetCurrentThread(),&NewGroupAffinity,&PreviousGroupAffinity); +} +ThreadGroupTempAffinity::~ThreadGroupTempAffinity() +{ + SetThreadGroupAffinity(GetCurrentThread(),&PreviousGroupAffinity,NULL); +} +#endif + +LONG unhandled_exception_handler(LPEXCEPTION_POINTERS p) +{ + std::cerr << "DEBUG: Unhandled Exception event" << std::endl; + exit(EXIT_FAILURE); +} + +/** +* \brief version of interrupt handled for Windows +*/ +BOOL sigINT_handler(DWORD fdwCtrlType) +{ + + // output for DEBUG only + std::cerr << "DEBUG: caught signal to interrupt: "; + switch( fdwCtrlType ) + { + // Handle the CTRL-C signal. + case CTRL_C_EVENT: + std::cerr << "Ctrl-C event" << std::endl; + break; + + // CTRL-CLOSE: confirm that the user wants to exit. + case CTRL_CLOSE_EVENT: + std::cerr << "Ctrl-Close event" << std::endl; + break; + + // Pass other signals to the next handler. + case CTRL_BREAK_EVENT: + std::cerr << "Ctrl-Break event" << std::endl; + break; + + case CTRL_LOGOFF_EVENT: + std::cerr << "Ctrl-Logoff event" << std::endl; + break; + + case CTRL_SHUTDOWN_EVENT: + std::cerr << "Ctrl-Shutdown event" << std::endl; + break; + + default: + std::cerr << "Unknown event" << std::endl; + break; + } + + // TODO: dump summary, if needed + + // in case PCM is blocked just return and summary will be dumped in + // calling function, if needed + if( PCM::getInstance()->isBlocked() ) { + return FALSE; + } else { + exit_cleanup(); + return FALSE; // to prevent Warning + } +} + +/** +* \brief started in a separate thread and blocks waiting for child applicaiton to exit. +* After child app exits: -> print Child's termination status and terminates PCM +*/ +void waitForChild(void *proc_id) +{ + intptr_t procHandle = (intptr_t)proc_id; + int termstat; + _cwait(&termstat, procHandle, _WAIT_CHILD); + std::cerr << "Program exited with status " << termstat << std::endl; + exit(EXIT_SUCCESS); +} +#else +/** + * \brief handles signals that lead to termination of the program + * such as SIGINT, SIGQUIT, SIGABRT, SIGSEGV, SIGTERM, SIGCHLD + * this function specifically works when the client aplication launched + * by pcm -- terminates + */ +void sigINT_handler(int signum) +{ + // output for DEBUG only + std::cerr << "DEBUG: caught signal to interrupt (" << strsignal(signum) << ")." << std::endl; + // TODO: dump summary, if needed + + // in case PCM is blocked just return and summary will be dumped in + // calling function, if needed + if( PCM::getInstance()->isBlocked() ) + return; + else + exit_cleanup(); +} + +/** + * \brief handles signals that lead to restart the application + * such as SIGHUP. + * for example to re-read environment variables controlling PCM execution + */ +void sigHUP_handler(int signum) +{ + // output for DEBUG only + std::cerr << "DEBUG: caught signal to hangup. Reloading configuration and continue..." << std::endl; + // TODO: restart; so far do nothing + + return; // continue program execution +} + +/** + * \brief handles signals that lead to update of configuration + * such as SIGUSR1 and SIGUSR2. + * for the future extensions + */ +void sigUSR_handler(int signum) +{ + std::cerr << "DEBUG: caught USR signal. Continue." << std::endl; + // TODO: reload configurationa, reset accumulative counters; + + return; +} + +/** + * \brief handles signals that lead to update of configuration + * such as SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU + */ +void sigSTOP_handler(int signum) +{ + PCM *m = PCM::getInstance(); + int runState = m->getRunState(); + std::string state = (runState==1 ? "suspend" : "continue"); + std::cerr << "DEBUG: caught signal to " << state << " execution." << std::endl; // debug of signals only + if(runState==1) { + // stop counters and sleep... almost forever; + m->setRunState(0); + sleep(INT_MAX); + } else { + // resume + m->setRunState(1); + alarm(1); + } + return; +} + +/** +* \brief handles signals that lead to update of configuration +* such as SIGCONT +*/ +void sigCONT_handler(int signum) +{ + std::cout << "DEBUG: caught signal to continue execution." << std::endl; // debug of signals only + // TODO: clear counters, resume counting. + return; +} +#endif // ifdef _MSC_VER + +//! \brief install various handlers for system signals +void set_signal_handlers(void) +{ + if( atexit(exit_cleanup) != 0 ) + { + std::cerr << "ERROR: Failed to install exit handler." << std::endl; + return; + } + +#ifdef _MSC_VER + BOOL handlerStatus; + // Increase the priority a bit to improve context switching delays on Windows + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +// to fix Cygwin/BASH setting Ctrl+C handler need first to restore the default one + handlerStatus = SetConsoleCtrlHandler(NULL, FALSE); // restores normal processing of CTRL+C input + if(handlerStatus == 0) { + _com_error error(GetLastError()); + std::wcerr << "Failed to set Ctrl+C hanlder. Error code: " << GetLastError() << " " << error.ErrorMessage() << std::endl; + _exit(EXIT_FAILURE); + } + SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigINT_handler, TRUE); + SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&unhandled_exception_handler); + std::cerr << "DEBUG: Setting Ctrl+C done." << std:: endl; + +#else + struct sigaction saINT, saHUP, saUSR, saSTOP, saCONT; + + // install handlers that interrupt execution + saINT.sa_handler = sigINT_handler; + sigemptyset(&saINT.sa_mask); + saINT.sa_flags = SA_RESTART; + sigaction(SIGINT , &saINT, NULL); + sigaction(SIGQUIT, &saINT, NULL); + sigaction(SIGABRT, &saINT, NULL); + sigaction(SIGTERM, &saINT, NULL); + sigaction(SIGSEGV, &saINT, NULL); + + saINT.sa_flags = SA_RESTART || SA_NOCLDSTOP; + sigaction(SIGCHLD, &saINT, NULL); // get there is our child exits. do nothing if it stoped/continued + + // install SIGHUP handler to restart + saHUP.sa_handler = sigHUP_handler; + sigemptyset(&saHUP.sa_mask); + saHUP.sa_flags = SA_RESTART; + sigaction(SIGHUP, &saHUP, NULL); + + // install SIGHUP handler to restart + saUSR.sa_handler = sigUSR_handler; + sigemptyset(&saUSR.sa_mask); + saUSR.sa_flags = SA_RESTART; + sigaction(SIGUSR1, &saUSR, NULL); + sigaction(SIGUSR2, &saUSR, NULL); + + // install SIGSTOP handler: pause/resume + saSTOP.sa_handler = sigSTOP_handler; + sigemptyset(&saSTOP.sa_mask); + saSTOP.sa_flags = SA_RESTART; + sigaction(SIGSTOP, &saSTOP, NULL); + sigaction(SIGTSTP, &saSTOP, NULL); + sigaction(SIGTTIN, &saSTOP, NULL); + sigaction(SIGTTOU, &saSTOP, NULL); + + // install SIGCONT & SIGALRM handler + saCONT.sa_handler = sigCONT_handler; + sigemptyset(&saCONT.sa_mask); + saCONT.sa_flags = SA_RESTART; + sigaction(SIGCONT, &saCONT, NULL); + sigaction(SIGALRM, &saCONT, NULL); + +#endif + return; +} + +//! \brief Restores default signal handlers under Linux/UNIX +void restore_signal_handlers(void) +{ +#ifndef _MSC_VER + struct sigaction action; + action.sa_handler = SIG_DFL; + + sigaction(SIGINT , &action, NULL); + sigaction(SIGQUIT, &action, NULL); + sigaction(SIGABRT, &action, NULL); + sigaction(SIGTERM, &action, NULL); + sigaction(SIGSEGV, &action, NULL); + + sigaction(SIGCHLD, &action, NULL); + + // restore SIGHUP handler to restart + sigaction(SIGHUP, &action, NULL); + + // restore SIGHUP handler to restart + sigaction(SIGUSR1, &action, NULL); + sigaction(SIGUSR2, &action, NULL); + + // restore SIGSTOP handler: pause/resume +// sigaction(SIGSTOP, &action, NULL); // cannot catch this +// handle SUSP character: normally C-z) + sigaction(SIGTSTP, &action, NULL); + sigaction(SIGTTIN, &action, NULL); + sigaction(SIGTTOU, &action, NULL); + + // restore SIGCONT & SIGALRM handler + sigaction(SIGCONT, &action, NULL); + sigaction(SIGALRM, &action, NULL); +#endif + + return; +} + +//!\brief launches external program in a separate process +void MySystem(char * sysCmd, char ** sysArgv) +{ + if( sysCmd == NULL ) { + assert("No program provided. NULL pointer"); + exit(EXIT_FAILURE); + } + std::cerr << std::endl << "Executing \""; + std::cerr << sysCmd; + std::cerr << "\" command:" << std::endl; +#ifdef _MSC_VER + intptr_t ret; + char cbuf[128]; + + if( PCM::getInstance()->isBlocked() ) { // synchronous start: wait for child process completion + // in case PCM should be blocked waiting for the child applicaiton to end + // 1. returns and ret = -1 in case of error creating process is encountered + // 2. + ret = _spawnvp(_P_WAIT, sysCmd, sysArgv); + if(ret == -1) { // process creation failed. + strerror_s(cbuf, 128, errno); + std::cerr << "Failed to start program \"" << sysCmd << "\". " << cbuf << std::endl; + exit(EXIT_FAILURE); + } else { // process created, worked, and completed with exist code in ret. ret=0 -> Success + std::cerr << "Program exited with status " << ret << std::endl; + } + } else { // async start: PCM works in parallel with the child process, and exits when + ret = _spawnvp(_P_NOWAIT, sysCmd, sysArgv); + if(ret == -1) { + strerror_s(cbuf, 128, errno); + std::cerr << "Failed to start program \"" << sysCmd << "\". " << cbuf << std::endl; + exit(EXIT_FAILURE); + } else { // ret here is the new process handle. + // start new thread which will wait for child completion, and continue PCM's execution + if(_beginthread(waitForChild, 0, (void *)ret) == -1L) { + strerror_s(cbuf, 128, errno); + std::cerr << "WARNING: Failed to set waitForChild. PCM will countinue infinitely: finish it manually! " << cbuf << std::endl; + } + } + } +#else + pid_t child_pid = fork(); + + if(child_pid == 0) { + execvp(sysCmd, sysArgv); + std::cerr << "Failed to start program \"" << sysCmd << "\"" << std::endl; + exit(EXIT_FAILURE); + } + else + { + if( PCM::getInstance()->isBlocked() ) { + int res; + waitpid(child_pid, &res, 0); + std::cerr << "Program " << sysCmd << " launched with PID: " << child_pid << std::endl; + + if (WIFEXITED(res)) { + std::cerr << "Program exited with status " << WEXITSTATUS(res) << std::endl; + } + else if (WIFSIGNALED(res)) { + std::cerr << "Process " << child_pid << " was terminated with status " << WTERMSIG(res); + } + } + } +#endif +} + diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..5685682 --- /dev/null +++ b/utils.h @@ -0,0 +1,156 @@ +/* +Copyright (c) 2009-2013, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the dis +tribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNES +S FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDI +NG, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRI +CT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev + + +/*! \file utils.h + \brief Some common utility routines + */ + +#ifndef PCM_UTILS_HEADER +#define PCM_UTILS_HEADER + +#include +#include +#include +#include "cpucounters.h" + +#ifndef _MSC_VER +#include +#include +#include +#endif + +void exit_cleanup(void); +void set_signal_handlers(void); +void restore_signal_handlers(void); +#ifndef _MSC_VER +void sigINT_handler(int signum); +void sigHUP_handler(int signum); +void sigUSR_handler(int signum); +void sigSTOP_handler(int signum); +void sigCONT_handler(int signum); +#endif + +#ifdef _MSC_VER +inline void win_usleep(int delay_us) +{ + uint64 t1 = 0, t2 = 0, freq = 0; + uint64 wait_tick; + QueryPerformanceFrequency((LARGE_INTEGER *) &freq); + wait_tick = freq * delay_us / 1000000ULL; + QueryPerformanceCounter((LARGE_INTEGER *) &t1); + do { + QueryPerformanceCounter((LARGE_INTEGER *) &t2); + YieldProcessor(); + } while ((t2-t1) < wait_tick); +} +#endif + +inline void MySleep(int delay) +{ +#ifdef _MSC_VER + if(delay) Sleep(delay*1000); +#else + ::sleep(delay); +#endif +} + +inline void MySleepMs(int delay_ms) +{ +#ifdef _MSC_VER + if(delay_ms) Sleep(delay_ms); +#else + struct timespec sleep_intrval; + double complete_seconds; + sleep_intrval.tv_nsec = static_cast(1000000000.0*(::modf(delay_ms/1000.0,&complete_seconds))); + sleep_intrval.tv_sec = static_cast(complete_seconds); + ::nanosleep(&sleep_intrval, NULL); +#endif +} + +inline void MySleepUs(int delay_us) +{ +#ifdef _MSC_VER + if(delay_us) win_usleep(delay_us); +#else + ::usleep(delay_us); + +#endif +} + +void MySystem(char * sysCmd, char ** argc); + +struct null_stream : public std::streambuf +{ + void overflow(char) { } +}; + +template +inline std::string unit_format(IntType n) +{ + char buffer[1024]; + if (n <= 9999ULL) + { + sprintf(buffer, "%4d ", int32(n)); + return buffer; + } + if (n <= 9999999ULL) + { + sprintf(buffer, "%4d K", int32(n / 1000ULL)); + return buffer; + } + if (n <= 9999999999ULL) + { + sprintf(buffer, "%4d M", int32(n / 1000000ULL)); + return buffer; + } + if (n <= 9999999999999ULL) + { + sprintf(buffer, "%4d G", int32(n / 1000000000ULL)); + return buffer; + } + + sprintf(buffer, "%4d T", int32(n / (1000000000ULL * 1000ULL))); + return buffer; +} + + +#define PCM_UNUSED(x) (void)(x) + +#define PCM_COMPILE_ASSERT(condition) \ + typedef char pcm_compile_assert_failed [ (condition) ? 1 : -1 ]; \ + pcm_compile_assert_failed pcm_compile_assert_failed_; \ + PCM_UNUSED(pcm_compile_assert_failed_); + +#ifdef COMPILE_FOR_WINDOWS_7 +#ifdef _MSC_VER +class ThreadGroupTempAffinity +{ + GROUP_AFFINITY PreviousGroupAffinity; + + ThreadGroupTempAffinity(); // forbidden + ThreadGroupTempAffinity(const ThreadGroupTempAffinity &); // forbidden +public: + ThreadGroupTempAffinity(uint32 core_id); + ~ThreadGroupTempAffinity(); +}; +#endif +#endif + +#endif + diff --git a/width_extender.h b/width_extender.h new file mode 100644 index 0000000..2775ccc --- /dev/null +++ b/width_extender.h @@ -0,0 +1,163 @@ +/* +Copyright (c) 2009-2012, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// written by Roman Dementiev +// Austen Ott + +#ifndef WIDTH_EXTENDER_HEADER_ +#define WIDTH_EXTENDER_HEADER_ + +/*! \file width_extender.h + \brief Provides 64-bit "virtual" counters from underlying 32-bit HW counters +*/ + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#include +#include "cpucounters.h" +#include "client_bw.h" + +#ifdef _MSC_VER +DWORD WINAPI WatchDogProc(LPVOID state); +#else +void * WatchDogProc(void * state); +#endif + +class CounterWidthExtender +{ +public: + struct AbstractRawCounter + { + virtual uint64 operator() () = 0; + virtual ~AbstractRawCounter() {} + }; + + struct MsrHandleCounter : public AbstractRawCounter + { + SafeMsrHandle * msr; + uint64 msr_addr; + MsrHandleCounter(SafeMsrHandle * msr_, uint64 msr_addr_): msr(msr_), msr_addr(msr_addr_) {} + uint64 operator() () + { + uint64 value = 0; + msr->read(msr_addr,&value); + return value; + } + }; + + struct ClientImcReadsCounter : public AbstractRawCounter + { + ClientBW * clientBW; + ClientImcReadsCounter(ClientBW * clientBW_): clientBW(clientBW_) {} + uint64 operator() () { return clientBW->getImcReads(); } + }; + + struct ClientImcWritesCounter : public AbstractRawCounter + { + ClientBW * clientBW; + ClientImcWritesCounter(ClientBW * clientBW_): clientBW(clientBW_) {} + uint64 operator() () { return clientBW->getImcWrites(); } + }; + + struct ClientIoRequestsCounter : public AbstractRawCounter + { + ClientBW * clientBW; + ClientIoRequestsCounter(ClientBW * clientBW_): clientBW(clientBW_) {} + uint64 operator() () { return clientBW->getIoRequests(); } + }; + +private: + +#ifdef _MSC_VER + HANDLE UpdateThread; + HANDLE CounterMutex; +#else + pthread_t UpdateThread; + pthread_mutex_t CounterMutex; +#endif + + AbstractRawCounter * raw_counter; + uint64 extended_value; + uint64 last_raw_value; + + CounterWidthExtender(); // forbidden + CounterWidthExtender(CounterWidthExtender&); // forbidden + + uint64 internal_read() + { + if (this==NULL) return 0; // to make security check happy + uint64 result = 0, new_raw_value = 0; +#ifdef _MSC_VER + WaitForSingleObject(CounterMutex,INFINITE); +#else + pthread_mutex_lock(&CounterMutex); +#endif + new_raw_value = (*raw_counter)(); + if(new_raw_value < last_raw_value) + { + extended_value += ((1ULL<<32ULL)-last_raw_value) + new_raw_value; + } + else + { + extended_value += (new_raw_value-last_raw_value); + } + + last_raw_value = new_raw_value; + + result = extended_value; +#ifdef _MSC_VER + ReleaseMutex(CounterMutex); +#else + pthread_mutex_unlock(&CounterMutex); +#endif + return result; + } + +public: + CounterWidthExtender(AbstractRawCounter * raw_counter_): raw_counter(raw_counter_) + { + last_raw_value = (*raw_counter)(); + extended_value = last_raw_value; + +#ifdef _MSC_VER + CounterMutex = CreateMutex(NULL,FALSE,NULL); + UpdateThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WatchDogProc,this,0,NULL); +#else + pthread_mutex_init(&CounterMutex, NULL); + pthread_create(&UpdateThread, NULL, WatchDogProc, this); +#endif + } + ~CounterWidthExtender() + { +#ifdef _MSC_VER + TerminateThread(UpdateThread,0); + CloseHandle(UpdateThread); + CloseHandle(CounterMutex); +#else + pthread_cancel(UpdateThread); + pthread_mutex_destroy(&CounterMutex); +#endif + if(raw_counter) delete raw_counter; + } + + uint64 read() // read extended value + { + return internal_read(); + } +}; + + +#endif diff --git a/winpmem/winpmem.cpp b/winpmem/winpmem.cpp new file mode 100644 index 0000000..cfd00a9 --- /dev/null +++ b/winpmem/winpmem.cpp @@ -0,0 +1,213 @@ +/* + Copyright 2012 Michael Cohen + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/******************************************************************** + This is a single binary memory imager for Windows. + + Supported systems: + - Windows XPSP2 to Windows 8 inclusive, both 32 bit and 64 bit. + +*********************************************************************/ + +/* +Copyright (c) 2009-2013, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "winpmem.h" + +// Roman Dementiev (Intel): added function to read 32-bit word from physical memory +unsigned int WinPmem::read32(__int64 start) +{ + LARGE_INTEGER large_start; + + DWORD bytes_read = 0; + large_start.QuadPart = start; + + if(0xFFFFFFFF == SetFilePointer(fd_, large_start.LowPart, + &large_start.HighPart, FILE_BEGIN)) + { + LogError(TEXT("Failed to seek in the pmem device.\n")); + goto error; + } + + unsigned int result = 0; + + if(!ReadFile(fd_, &result, sizeof(unsigned int), &bytes_read, NULL)) + { + LogError(TEXT("Failed to Read memory.")); + goto error; + } + + return result; + + error: + return 0; +}; + +int WinPmem::set_acquisition_mode(__int32 mode) { + DWORD size; + // Set the acquisition mode. + if(!DeviceIoControl(fd_, PMEM_CTRL_IOCTRL, &mode, 4, NULL, 0, + &size, NULL)) { + LogError(TEXT("Failed to set acquisition mode.\n")); + return -1; + }; + + return 1; +}; + +WinPmem::WinPmem(): + fd_(INVALID_HANDLE_VALUE), + buffer_size_(1024*1024), + buffer_(NULL), + suppress_output(FALSE), + service_name(PMEM_SERVICE_NAME) { + buffer_ = new char[buffer_size_]; + _tcscpy_s(last_error, TEXT("")); + max_physical_memory_ = 0; + } + +WinPmem::~WinPmem() { + if (fd_ != INVALID_HANDLE_VALUE) { + CloseHandle(fd_); + }; + + if (buffer_) { + delete [] buffer_; + } +} + +void WinPmem::LogError(TCHAR *message) { + _tcsncpy_s(last_error, message, sizeof(last_error)); + if (suppress_output) return; + + wprintf(L"%s", message); +}; + +void WinPmem::Log(const TCHAR *message, ...) { + if (suppress_output) return; + + va_list ap; + va_start(ap, message); + vwprintf(message, ap); + va_end(ap); +}; + +// Roman Dementiev (Intel): added delete_driver option (default is true) +int WinPmem::install_driver(bool delete_driver) { + SC_HANDLE scm, service; + int status = -1; + + // Try to load the driver from the resource section. + if (load_driver_() < 0) + goto error; + + uninstall_driver(); + + scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (!scm) { + LogError(TEXT("Can not open SCM. Are you administrator?")); + goto error; + } + + service = CreateService(scm, + service_name, + service_name, + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + driver_filename, + NULL, + NULL, + NULL, + NULL, + NULL); + + if (GetLastError() == ERROR_SERVICE_EXISTS) { + service = OpenService(scm, service_name, SERVICE_ALL_ACCESS); + } + + if (!service) { + goto error; + }; + if (!StartService(service, 0, NULL)) { + if (GetLastError() != ERROR_SERVICE_ALREADY_RUNNING) { + LogError(TEXT("Error: StartService(), Cannot start the driver.\n")); + goto service_error; + } + } + + Log(L"Loaded Driver %s.\n", driver_filename); + + fd_ = CreateFile(TEXT("\\\\.\\") TEXT(PMEM_DEVICE_NAME), + // Write is needed for IOCTL. + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(fd_ == INVALID_HANDLE_VALUE) { + LogError(TEXT("Can not open raw device.")); + status = -1; + }; + + status = 1; + + service_error: + CloseServiceHandle(service); + CloseServiceHandle(scm); + if(delete_driver) DeleteFile(driver_filename); + + error: + return status; +} + +int WinPmem::uninstall_driver() { + SC_HANDLE scm, service; + SERVICE_STATUS ServiceStatus; + + scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + + if (!scm) return 0; + + service = OpenService(scm, service_name, SERVICE_ALL_ACCESS); + + if (service) { + ControlService(service, SERVICE_CONTROL_STOP, &ServiceStatus); + }; + + DeleteService(service); + CloseServiceHandle(service); + Log(TEXT("Driver Unloaded.\n")); + + return 1; + + CloseServiceHandle(scm); + return 0; +} + diff --git a/winpmem/winpmem.h b/winpmem/winpmem.h new file mode 100644 index 0000000..4f06aa1 --- /dev/null +++ b/winpmem/winpmem.h @@ -0,0 +1,58 @@ +#include "windows.h" +#include "stdio.h" +#include "tchar.h" + +// Executable version. +static TCHAR version[] = TEXT("1.3. Built ") TEXT(__DATE__); +#define PMEM_DEVICE_NAME "pmem" +#define PMEM_SERVICE_NAME TEXT("winpmem") + +#define PAGE_SIZE 0x1000 + +class WinPmem { + public: + WinPmem(); + virtual ~WinPmem(); + + virtual int install_driver(bool delete_driver = true); + virtual int uninstall_driver(); + virtual int set_acquisition_mode(__int32 mode); + + unsigned int read32(__int64 start); + + // This is set if output should be suppressed (e.g. if we pipe the + // image to the STDOUT). + int suppress_output; + TCHAR last_error[1024]; + + protected: + virtual int load_driver_() = 0; + + virtual void LogError(TCHAR *message); + virtual void Log(const TCHAR *message, ...); + + // The file handle to the pmem device. + HANDLE fd_; + + // The file handle to the image file. + HANDLE out_fd_; + TCHAR *service_name; + char *buffer_; + size_t buffer_size_; + TCHAR driver_filename[MAX_PATH]; + + // This is the maximum size of memory calculated. + __int64 max_physical_memory_; +}; + +// This is the filename of the driver we drop. +static TCHAR driver_filename[MAX_PATH]; + +// ioctl to get memory ranges from our driver. +#define PMEM_CTRL_IOCTRL CTL_CODE(0x22, 0x101, 0, 3) +#define PMEM_WRITE_ENABLE CTL_CODE(0x22, 0x102, 0, 3) +#define PMEM_INFO_IOCTRL CTL_CODE(0x22, 0x103, 0, 3) + +// Available modes +#define PMEM_MODE_IOSPACE 0 +#define PMEM_MODE_PHYSICAL 1 diff --git a/winring0/OlsApi.h b/winring0/OlsApi.h new file mode 100644 index 0000000..56e07de --- /dev/null +++ b/winring0/OlsApi.h @@ -0,0 +1,580 @@ +//----------------------------------------------------------------------------- +// Author : hiyohiyo +// Mail : hiyohiyo@crystalmark.info +// Web : http://openlibsys.org/ +// License : The modified BSD license +// +// Copyright 2007-2009 OpenLibSys.org. All rights reserved. +//----------------------------------------------------------------------------- +// for WinRing0 1.3.x + +#pragma once + +/****************************************************************************** +** +** DLL Information +** +******************************************************************************/ + +//----------------------------------------------------------------------------- +// GetDllStatus +//----------------------------------------------------------------------------- +DWORD // DLL Status, defined OLS_DLL_**** +WINAPI GetDllStatus(); + +//----------------------------------------------------------------------------- +// GetDllVersion +//----------------------------------------------------------------------------- +DWORD // DLL Version, defined OLS_VERSION +WINAPI GetDllVersion( + PBYTE major, // major version + PBYTE minor, // minor version + PBYTE revision, // revision + PBYTE release // release/build +); + +//----------------------------------------------------------------------------- +// GetDriverVersion +//----------------------------------------------------------------------------- +DWORD // Device Driver Version, defined OLS_DRIVER_VERSION +WINAPI GetDriverVersion( + PBYTE major, // major version + PBYTE minor, // minor version + PBYTE revision, // revision + PBYTE release // release/build +); + +//----------------------------------------------------------------------------- +// GetDriverType +//----------------------------------------------------------------------------- +DWORD // Device Driver Type, defined OLS_DRIVER_TYPE_**** +WINAPI GetDriverType(); + +//----------------------------------------------------------------------------- +// InitializeOls +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI InitializeOls(); + +//----------------------------------------------------------------------------- +// DeinitializeOls +//----------------------------------------------------------------------------- +VOID WINAPI DeinitializeOls(); + +/****************************************************************************** +** +** CPU +** +******************************************************************************/ + +//----------------------------------------------------------------------------- +// IsCpuid +//----------------------------------------------------------------------------- +BOOL // TRUE: support CPUID instruction, FALSE: not support CPUID instruction +WINAPI IsCpuid(); + +//----------------------------------------------------------------------------- +// IsMsr +//----------------------------------------------------------------------------- +BOOL // TRUE: support MSR(Model-Specific Register), FALSE: not support MSR +WINAPI IsMsr(); + +//----------------------------------------------------------------------------- +// IsTsc +//----------------------------------------------------------------------------- +BOOL // TRUE: support TSC(Time Stamp Counter), FALSE: not support TSC +WINAPI IsTsc(); + +//----------------------------------------------------------------------------- +// Rdmsr +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Rdmsr( + DWORD index, // MSR index + PDWORD eax, // bit 0-31 + PDWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// RdmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdmsrTx( + DWORD index, // MSR index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// RdmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdmsrPx( + DWORD index, // MSR index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Wrmsr +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Wrmsr( + DWORD index, // MSR index + DWORD eax, // bit 0-31 + DWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// WrmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WrmsrTx( + DWORD index, // MSR index + DWORD eax, // bit 0-31 + DWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// WrmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WrmsrPx( + DWORD index, // MSR index + DWORD eax, // bit 0-31 + DWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Rdpmc +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Rdpmc( + DWORD index, // PMC index + PDWORD eax, // bit 0-31 + PDWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// RdmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdpmcTx( + DWORD index, // PMC index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// RdmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdpmcPx( + DWORD index, // PMC index + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Cpuid +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Cpuid( + DWORD index, // CPUID index + PDWORD eax, + PDWORD ebx, + PDWORD ecx, + PDWORD edx +); + +//----------------------------------------------------------------------------- +// CpuidTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI CpuidTx( + DWORD index, // CPUID index + PDWORD eax, + PDWORD ebx, + PDWORD ecx, + PDWORD edx, + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// CpuidPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI CpuidPx( + DWORD index, // CPUID index + PDWORD eax, + PDWORD ebx, + PDWORD ecx, + PDWORD edx, + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Rdtsc +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Rdtsc( + PDWORD eax, // bit 0-31 + PDWORD edx // bit 32-63 +); + +//----------------------------------------------------------------------------- +// RdmsrTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdtscTx( + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// RdmsrPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI RdtscPx( + PDWORD eax, // bit 0-31 + PDWORD edx, // bit 32-63 + DWORD_PTR processAffinityMask +); + +//----------------------------------------------------------------------------- +// Hlt +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI Hlt(); + +//----------------------------------------------------------------------------- +// HltTx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI HltTx( + DWORD_PTR threadAffinityMask +); + +//----------------------------------------------------------------------------- +// HltPx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI HltPx( + DWORD_PTR processAffinityMask +); + +/****************************************************************************** +** +** I/O +** +******************************************************************************/ + +//----------------------------------------------------------------------------- +// ReadIoPortByte +//----------------------------------------------------------------------------- +BYTE // Read Value +WINAPI ReadIoPortByte( + WORD port // I/O port address +); + +//----------------------------------------------------------------------------- +// ReadIoPortWord +//----------------------------------------------------------------------------- +WORD // Read Value +WINAPI ReadIoPortWord( + WORD port // I/O port address +); + +//----------------------------------------------------------------------------- +// ReadIoPortDword +//----------------------------------------------------------------------------- +DWORD // Read Value +WINAPI ReadIoPortDword( + WORD port // I/O port address +); + +//----------------------------------------------------------------------------- +// ReadIoPortByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadIoPortByteEx( + WORD port, // I/O port address + PBYTE value // Read Value +); +//----------------------------------------------------------------------------- +// ReadIoPortWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadIoPortWordEx( + WORD port, // I/O port address + PWORD value // Read Value +); +//----------------------------------------------------------------------------- +// ReadIoPortDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadIoPortDwordEx( + WORD port, // I/O port address + PDWORD value // Read Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortByte +//----------------------------------------------------------------------------- +VOID +WINAPI WriteIoPortByte( + WORD port, // I/O port address + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortDword +//----------------------------------------------------------------------------- +VOID +WINAPI WriteIoPortDword( + WORD port, // I/O port address + DWORD value // Write Value +); + + +//----------------------------------------------------------------------------- +// WriteIoPortWord +//----------------------------------------------------------------------------- +VOID +WINAPI WriteIoPortWord( + WORD port, // I/O port address + WORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WriteIoPortByteEx( + WORD port, // I/O port address + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WriteIoPortWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WriteIoPortWordEx( + WORD port, // I/O port address + WORD value // Write Value +); + + +//----------------------------------------------------------------------------- +// WriteIoPortDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WriteIoPortDwordEx( + WORD port, // I/O port address + DWORD value // Write Value +); + +/****************************************************************************** +** +** PCI +** +******************************************************************************/ +// pciAddress +// 0- 2: Function Number +// 3- 7: Device Number +// 8-15: PCI Bus Number +// 16-31: Reserved +// 0xFFFFFFFF : Error + +//----------------------------------------------------------------------------- +// SetPciMaxBusNo +//----------------------------------------------------------------------------- +VOID +WINAPI SetPciMaxBusIndex( + BYTE max // Max PCI Bus to Scan +); + +//----------------------------------------------------------------------------- +// ReadPciConfigByte +//----------------------------------------------------------------------------- +BYTE // Read Value +WINAPI ReadPciConfigByte( + DWORD pciAddress, // PCI Device Address + BYTE regAddress // Configuration Address 0-255 +); + +//----------------------------------------------------------------------------- +// ReadPciConfigWord +//----------------------------------------------------------------------------- +WORD // Read Value +WINAPI ReadPciConfigWord( + DWORD pciAddress, // PCI Device Address + BYTE regAddress // Configuration Address 0-255 +); + +//----------------------------------------------------------------------------- +// ReadPciConfigDword +//----------------------------------------------------------------------------- +DWORD // Read Value +WINAPI ReadPciConfigDword( + DWORD pciAddress, // PCI Device Address + BYTE regAddress // Configuration Address 0-255 +); + +//----------------------------------------------------------------------------- +// ReadPciConfigByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadPciConfigByteEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + PBYTE value // Read Value +); + +//----------------------------------------------------------------------------- +// ReadPciConfigWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadPciConfigWordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + PWORD value // Read Value +); + +//----------------------------------------------------------------------------- +// ReadPciConfigDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI ReadPciConfigDwordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + PDWORD value // Read Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigByte +//----------------------------------------------------------------------------- +VOID +WINAPI WritePciConfigByte( + DWORD pciAddress, // PCI Device Address + BYTE regAddress, // Configuration Address 0-255 + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigWord +//----------------------------------------------------------------------------- +VOID +WINAPI WritePciConfigWord( + DWORD pciAddress, // PCI Device Address + BYTE regAddress, // Configuration Address 0-255 + WORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigDword +//----------------------------------------------------------------------------- +VOID +WINAPI WritePciConfigDword( + DWORD pciAddress, // PCI Device Address + BYTE regAddress, // Configuration Address 0-255 + DWORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigByteEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WritePciConfigByteEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + BYTE value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigWordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WritePciConfigWordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + WORD value // Write Value +); + +//----------------------------------------------------------------------------- +// WritePciConfigDwordEx +//----------------------------------------------------------------------------- +BOOL // TRUE: success, FALSE: failure +WINAPI WritePciConfigDwordEx( + DWORD pciAddress, // PCI Device Address + DWORD regAddress, // Configuration Address 0-whatever + DWORD value // Write Value +); + +//----------------------------------------------------------------------------- +// FindPciDeviceById +//----------------------------------------------------------------------------- +DWORD // pciAddress, 0xFFFFFFFF: failure +WINAPI FindPciDeviceById( + WORD vendorId, // Vendor ID + WORD deviceId, // Device ID + BYTE index // Index +); + +//----------------------------------------------------------------------------- +// FindPciDeviceByClass +//----------------------------------------------------------------------------- +DWORD // pciAddress, 0xFFFFFFFF: failure +WINAPI FindPciDeviceByClass( + BYTE baseClass, // Base Class + BYTE subClass, // Sub Class + BYTE programIf, // Program Interface + BYTE index // Index +); + +/****************************************************************************** +** +** Memory (Special API) +** +******************************************************************************/ + +#ifdef _PHYSICAL_MEMORY_SUPPORT +//----------------------------------------------------------------------------- +// ReadDmiMemory +//----------------------------------------------------------------------------- +DWORD // Read size(byte), 0: failure +WINAPI ReadDmiMemory( + PBYTE buffer, // Buffer + DWORD count, // Count + DWORD unitSize // Unit Size (BYTE, WORD, DWORD) +); + +//----------------------------------------------------------------------------- +// ReadPhysicalMemory +//----------------------------------------------------------------------------- +DWORD // Read size(byte), 0: failure +WINAPI ReadPhysicalMemory( + DWORD_PTR address, // Physical Memory Address + PBYTE buffer, // Buffer + DWORD count, // Count + DWORD unitSize // Unit Size (BYTE, WORD, DWORD) +); + +//----------------------------------------------------------------------------- +// WritePhysicalMemory +//----------------------------------------------------------------------------- +DWORD // Write size(byte), 0: failure +WINAPI WritePhysicalMemory( + DWORD_PTR address, // Physical Memory Address + PBYTE buffer, // Buffer + DWORD count, // Count + DWORD unitSize // Unit Size (BYTE, WORD, DWORD) +); +#endif \ No newline at end of file diff --git a/winring0/OlsApiInit.h b/winring0/OlsApiInit.h new file mode 100644 index 0000000..c8bc5e2 --- /dev/null +++ b/winring0/OlsApiInit.h @@ -0,0 +1,322 @@ +//----------------------------------------------------------------------------- +// Author : hiyohiyo +// Mail : hiyohiyo@crystalmark.info +// Web : http://openlibsys.org/ +// License : The modified BSD license +// +// Copyright 2007-2009 OpenLibSys.org. All rights reserved. +//----------------------------------------------------------------------------- +// for WinRing0 1.3.x + +#pragma once + +#include "OlsDef.h" +#include "OlsApiInitDef.h" + +//----------------------------------------------------------------------------- +// +// Prototypes +// +//----------------------------------------------------------------------------- + +BOOL InitOpenLibSys(HMODULE *hModule); +BOOL DeinitOpenLibSys(HMODULE *hModule); + +//----------------------------------------------------------------------------- +// +// Funtions +// +//----------------------------------------------------------------------------- + +// DLL +_GetDllStatus GetDllStatus = NULL; +_GetDllVersion GetDllVersion = NULL; +_GetDriverVersion GetDriverVersion = NULL; +_GetDriverType GetDriverType = NULL; + +_InitializeOls InitializeOls = NULL; +_DeinitializeOls DeinitializeOls = NULL; + +// CPU +_IsCpuid IsCpuid = NULL; +_IsMsr IsMsr = NULL; +_IsTsc IsTsc = NULL; + +_Hlt Hlt = NULL; +_Rdmsr Rdmsr = NULL; +_Wrmsr Wrmsr = NULL; +_Rdpmc Rdpmc = NULL; +_Cpuid Cpuid = NULL; +_Rdtsc Rdtsc = NULL; + +_HltTx HltTx = NULL; +_RdmsrTx RdmsrTx = NULL; +_WrmsrTx WrmsrTx = NULL; +_RdpmcTx RdpmcTx = NULL; +_CpuidTx CpuidTx = NULL; +_RdtscTx RdtscTx = NULL; + +_HltPx HltPx = NULL; +_RdmsrPx RdmsrPx = NULL; +_WrmsrPx WrmsrPx = NULL; +_RdpmcPx RdpmcPx = NULL; +_CpuidPx CpuidPx = NULL; +_RdtscPx RdtscPx = NULL; + +// I/O +_ReadIoPortByte ReadIoPortByte = NULL; +_ReadIoPortWord ReadIoPortWord = NULL; +_ReadIoPortDword ReadIoPortDword = NULL; + +_ReadIoPortByteEx ReadIoPortByteEx = NULL; +_ReadIoPortWordEx ReadIoPortWordEx = NULL; +_ReadIoPortDwordEx ReadIoPortDwordEx = NULL; + +_WriteIoPortByte WriteIoPortByte = NULL; +_WriteIoPortWord WriteIoPortWord = NULL; +_WriteIoPortDword WriteIoPortDword = NULL; + +_WriteIoPortByteEx WriteIoPortByteEx = NULL; +_WriteIoPortWordEx WriteIoPortWordEx = NULL; +_WriteIoPortDwordEx WriteIoPortDwordEx = NULL; + +// PCI +_SetPciMaxBusIndex SetPciMaxBusIndex = NULL; + +_ReadPciConfigByte ReadPciConfigByte = NULL; +_ReadPciConfigWord ReadPciConfigWord = NULL; +_ReadPciConfigDword ReadPciConfigDword = NULL; + +_ReadPciConfigByteEx ReadPciConfigByteEx = NULL; +_ReadPciConfigWordEx ReadPciConfigWordEx = NULL; +_ReadPciConfigDwordEx ReadPciConfigDwordEx = NULL; + +_WritePciConfigByte WritePciConfigByte = NULL; +_WritePciConfigWord WritePciConfigWord = NULL; +_WritePciConfigDword WritePciConfigDword = NULL; + +_WritePciConfigByteEx WritePciConfigByteEx = NULL; +_WritePciConfigWordEx WritePciConfigWordEx = NULL; +_WritePciConfigDwordEx WritePciConfigDwordEx = NULL; + +_FindPciDeviceById FindPciDeviceById = NULL; +_FindPciDeviceByClass FindPciDeviceByClass = NULL; + +// Memory +#ifdef _PHYSICAL_MEMORY_SUPPORT +_ReadDmiMemory ReadDmiMemory = NULL; +_ReadPhysicalMemory ReadPhysicalMemory = NULL; +_WritePhysicalMemory WritePhysicalMemory = NULL; +#endif + +#ifdef _OPEN_LIB_SYS +#ifdef _UNICODE +#define GetOlsString GetOlsStringW +#else +#define GetOlsString GetOlsStringA +#endif + +_InstallOpenLibSys InstallOpenLibSys = NULL; +_UninstallOpenLibSys UninstallOpenLibSys = NULL; +_GetDriverStatus GetDriverStatus = NULL; + +_GetOlsStringA GetOlsStringA = NULL; +_GetOlsStringW GetOlsStringW = NULL; +_GetOlsValue GetOlsValue = NULL; +_SetOlsValue SetOlsValue = NULL; +#endif + +//----------------------------------------------------------------------------- +// +// Initialize +// +//----------------------------------------------------------------------------- + +BOOL InitOpenLibSys(HMODULE *hModule) +{ +#ifdef _M_X64 + *hModule = LoadLibrary(_T("WinRing0x64.dll")); +#else + *hModule = LoadLibrary(_T("WinRing0.dll")); +#endif + + if(*hModule == NULL) + { + return FALSE; + } + + //----------------------------------------------------------------------------- + // GetProcAddress + //----------------------------------------------------------------------------- + // DLL + GetDllStatus = (_GetDllStatus) GetProcAddress (*hModule, "GetDllStatus"); + GetDllVersion = (_GetDllVersion) GetProcAddress (*hModule, "GetDllVersion"); + GetDriverVersion = (_GetDriverVersion) GetProcAddress (*hModule, "GetDriverVersion"); + GetDriverType = (_GetDriverType) GetProcAddress (*hModule, "GetDriverType"); + + InitializeOls = (_InitializeOls) GetProcAddress (*hModule, "InitializeOls"); + DeinitializeOls = (_DeinitializeOls) GetProcAddress (*hModule, "DeinitializeOls"); + + // CPU + IsCpuid = (_IsCpuid) GetProcAddress (*hModule, "IsCpuid"); + IsMsr = (_IsMsr) GetProcAddress (*hModule, "IsMsr"); + IsTsc = (_IsTsc) GetProcAddress (*hModule, "IsTsc"); + Hlt = (_Hlt) GetProcAddress (*hModule, "Hlt"); + Rdmsr = (_Rdmsr) GetProcAddress (*hModule, "Rdmsr"); + Wrmsr = (_Wrmsr) GetProcAddress (*hModule, "Wrmsr"); + Rdpmc = (_Rdpmc) GetProcAddress (*hModule, "Rdpmc"); + Cpuid = (_Cpuid) GetProcAddress (*hModule, "Cpuid"); + Rdtsc = (_Rdtsc) GetProcAddress (*hModule, "Rdtsc"); + HltTx = (_HltTx) GetProcAddress (*hModule, "HltTx"); + RdmsrTx = (_RdmsrTx) GetProcAddress (*hModule, "RdmsrTx"); + WrmsrTx = (_WrmsrTx) GetProcAddress (*hModule, "WrmsrTx"); + RdpmcTx = (_RdpmcTx) GetProcAddress (*hModule, "RdpmcTx"); + CpuidTx = (_CpuidTx) GetProcAddress (*hModule, "CpuidTx"); + RdtscTx = (_RdtscTx) GetProcAddress (*hModule, "RdtscTx"); + HltPx = (_HltPx) GetProcAddress (*hModule, "HltPx"); + RdmsrPx = (_RdmsrPx) GetProcAddress (*hModule, "RdmsrPx"); + WrmsrPx = (_WrmsrPx) GetProcAddress (*hModule, "WrmsrPx"); + RdpmcPx = (_RdpmcPx) GetProcAddress (*hModule, "RdpmcPx"); + CpuidPx = (_CpuidPx) GetProcAddress (*hModule, "CpuidPx"); + RdtscPx = (_RdtscPx) GetProcAddress (*hModule, "RdtscPx"); + + // I/O + ReadIoPortByte = (_ReadIoPortByte) GetProcAddress (*hModule, "ReadIoPortByte"); + ReadIoPortWord = (_ReadIoPortWord) GetProcAddress (*hModule, "ReadIoPortWord"); + ReadIoPortDword = (_ReadIoPortDword) GetProcAddress (*hModule, "ReadIoPortDword"); + + ReadIoPortByteEx = (_ReadIoPortByteEx) GetProcAddress (*hModule, "ReadIoPortByteEx"); + ReadIoPortWordEx = (_ReadIoPortWordEx) GetProcAddress (*hModule, "ReadIoPortWordEx"); + ReadIoPortDwordEx = (_ReadIoPortDwordEx) GetProcAddress (*hModule, "ReadIoPortDwordEx"); + + WriteIoPortByte = (_WriteIoPortByte) GetProcAddress (*hModule, "WriteIoPortByte"); + WriteIoPortWord = (_WriteIoPortWord) GetProcAddress (*hModule, "WriteIoPortWord"); + WriteIoPortDword = (_WriteIoPortDword) GetProcAddress (*hModule, "WriteIoPortDword"); + + WriteIoPortByteEx = (_WriteIoPortByteEx) GetProcAddress (*hModule, "WriteIoPortByteEx"); + WriteIoPortWordEx = (_WriteIoPortWordEx) GetProcAddress (*hModule, "WriteIoPortWordEx"); + WriteIoPortDwordEx = (_WriteIoPortDwordEx) GetProcAddress (*hModule, "WriteIoPortDwordEx"); + + // PCI + SetPciMaxBusIndex = (_SetPciMaxBusIndex) GetProcAddress (*hModule, "SetPciMaxBusIndex"); + + ReadPciConfigByte = (_ReadPciConfigByte) GetProcAddress (*hModule, "ReadPciConfigByte"); + ReadPciConfigWord = (_ReadPciConfigWord) GetProcAddress (*hModule, "ReadPciConfigWord"); + ReadPciConfigDword = (_ReadPciConfigDword) GetProcAddress (*hModule, "ReadPciConfigDword"); + + ReadPciConfigByteEx = (_ReadPciConfigByteEx) GetProcAddress (*hModule, "ReadPciConfigByteEx"); + ReadPciConfigWordEx = (_ReadPciConfigWordEx) GetProcAddress (*hModule, "ReadPciConfigWordEx"); + ReadPciConfigDwordEx = (_ReadPciConfigDwordEx) GetProcAddress (*hModule, "ReadPciConfigDwordEx"); + + WritePciConfigByte = (_WritePciConfigByte) GetProcAddress (*hModule, "WritePciConfigByte"); + WritePciConfigWord = (_WritePciConfigWord) GetProcAddress (*hModule, "WritePciConfigWord"); + WritePciConfigDword = (_WritePciConfigDword) GetProcAddress (*hModule, "WritePciConfigDword"); + + WritePciConfigByteEx = (_WritePciConfigByteEx) GetProcAddress (*hModule, "WritePciConfigByteEx"); + WritePciConfigWordEx = (_WritePciConfigWordEx) GetProcAddress (*hModule, "WritePciConfigWordEx"); + WritePciConfigDwordEx = (_WritePciConfigDwordEx)GetProcAddress (*hModule, "WritePciConfigDwordEx"); + + FindPciDeviceById = (_FindPciDeviceById) GetProcAddress (*hModule, "FindPciDeviceById"); + FindPciDeviceByClass = (_FindPciDeviceByClass) GetProcAddress (*hModule, "FindPciDeviceByClass"); + + // Memory +#ifdef _PHYSICAL_MEMORY_SUPPORT + ReadDmiMemory = (_ReadDmiMemory) GetProcAddress (*hModule, "ReadDmiMemory"); + ReadPhysicalMemory = (_ReadPhysicalMemory) GetProcAddress (*hModule, "ReadPhysicalMemory"); + WritePhysicalMemory = (_WritePhysicalMemory) GetProcAddress (*hModule, "WritePhysicalMemory"); +#endif + + //----------------------------------------------------------------------------- + // Check Functions + //----------------------------------------------------------------------------- + if(!( + GetDllStatus + && GetDllVersion + && GetDriverVersion + && GetDriverType + && InitializeOls + && DeinitializeOls + && IsCpuid + && IsMsr + && IsTsc + && Hlt + && HltTx + && HltPx + && Rdmsr + && RdmsrTx + && RdmsrPx + && Wrmsr + && WrmsrTx + && WrmsrPx + && Rdpmc + && RdpmcTx + && RdpmcPx + && Cpuid + && CpuidTx + && CpuidPx + && Rdtsc + && RdtscTx + && RdtscPx + && ReadIoPortByte + && ReadIoPortWord + && ReadIoPortDword + && ReadIoPortByteEx + && ReadIoPortWordEx + && ReadIoPortDwordEx + && WriteIoPortByte + && WriteIoPortWord + && WriteIoPortDword + && WriteIoPortByteEx + && WriteIoPortWordEx + && WriteIoPortDwordEx + && SetPciMaxBusIndex + && ReadPciConfigByte + && ReadPciConfigWord + && ReadPciConfigDword + && ReadPciConfigByteEx + && ReadPciConfigWordEx + && ReadPciConfigDwordEx + && WritePciConfigByte + && WritePciConfigWord + && WritePciConfigDword + && WritePciConfigByteEx + && WritePciConfigWordEx + && WritePciConfigDwordEx + && FindPciDeviceById + && FindPciDeviceByClass +#ifdef _PHYSICAL_MEMORY_SUPPORT + && ReadDmiMemory + && ReadPhysicalMemory + && WritePhysicalMemory +#endif + )) + { + return FALSE; + } + + return InitializeOls(); +} + +//----------------------------------------------------------------------------- +// +// Deinitialize +// +//----------------------------------------------------------------------------- + +BOOL DeinitOpenLibSys(HMODULE *hModule) +{ + BOOL result = FALSE; + + if(*hModule == NULL) + { + return TRUE; + } + else + { + DeinitializeOls(); + result = FreeLibrary(*hModule); + *hModule = NULL; + + return result; + } +} diff --git a/winring0/OlsApiInitDef.h b/winring0/OlsApiInitDef.h new file mode 100644 index 0000000..5d02d54 --- /dev/null +++ b/winring0/OlsApiInitDef.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Author : hiyohiyo +// Mail : hiyohiyo@crystalmark.info +// Web : http://openlibsys.org/ +// License : The modified BSD license +// +// Copyright 2007-2009 OpenLibSys.org. All rights reserved. +//----------------------------------------------------------------------------- +// for WinRing0 1.3.x + +#pragma once + +//----------------------------------------------------------------------------- +// +// Type Defines +// +//----------------------------------------------------------------------------- + +// DLL +typedef DWORD (WINAPI *_GetDllStatus) (); +typedef DWORD (WINAPI *_GetDllVersion) (PBYTE major, PBYTE minor, PBYTE revision, PBYTE release); +typedef DWORD (WINAPI *_GetDriverVersion) (PBYTE major, PBYTE minor, PBYTE revision, PBYTE release); +typedef DWORD (WINAPI *_GetDriverType) (); + +typedef BOOL (WINAPI *_InitializeOls) (); +typedef VOID (WINAPI *_DeinitializeOls) (); + +// CPU +typedef BOOL (WINAPI *_IsCpuid) (); +typedef BOOL (WINAPI *_IsMsr) (); +typedef BOOL (WINAPI *_IsTsc) (); + +typedef BOOL (WINAPI *_Hlt) (); +typedef DWORD (WINAPI *_Rdmsr) (DWORD index, PDWORD eax, PDWORD edx); +typedef DWORD (WINAPI *_Wrmsr) (DWORD index, DWORD eax, DWORD edx); +typedef DWORD (WINAPI *_Rdpmc) (DWORD index, PDWORD eax, PDWORD edx); +typedef DWORD (WINAPI *_Cpuid) (DWORD index, PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx); +typedef DWORD (WINAPI *_Rdtsc) (PDWORD eax, PDWORD edx); + +typedef BOOL (WINAPI *_HltTx) (DWORD_PTR threadAffinityMask); +typedef DWORD (WINAPI *_RdmsrTx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR threadAffinityMask); +typedef DWORD (WINAPI *_WrmsrTx) (DWORD index, DWORD eax, DWORD edx, DWORD_PTR threadAffinityMask); +typedef DWORD (WINAPI *_RdpmcTx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR threadAffinityMask); +typedef DWORD (WINAPI *_CpuidTx) (DWORD index, PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx, DWORD_PTR threadAffinityMask); +typedef DWORD (WINAPI *_RdtscTx) (PDWORD eax, PDWORD edx, DWORD_PTR threadAffinityMask); + +typedef BOOL (WINAPI *_HltPx) (DWORD_PTR processAffinityMask); +typedef DWORD (WINAPI *_RdmsrPx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR processAffinityMask); +typedef DWORD (WINAPI *_WrmsrPx) (DWORD index, DWORD eax, DWORD edx, DWORD_PTR processAffinityMask); +typedef DWORD (WINAPI *_RdpmcPx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR processAffinityMask); +typedef DWORD (WINAPI *_CpuidPx) (DWORD index, PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx, DWORD_PTR processAffinityMask); +typedef DWORD (WINAPI *_RdtscPx) (PDWORD eax, PDWORD edx, DWORD_PTR processAffinityMask); + +// I/O +typedef BYTE (WINAPI *_ReadIoPortByte) (WORD address); +typedef WORD (WINAPI *_ReadIoPortWord) (WORD address); +typedef DWORD (WINAPI *_ReadIoPortDword) (WORD address); + +typedef BOOL (WINAPI *_ReadIoPortByteEx) (WORD address, PBYTE value); +typedef BOOL (WINAPI *_ReadIoPortWordEx) (WORD address, PWORD value); +typedef BOOL (WINAPI *_ReadIoPortDwordEx) (WORD address, PDWORD value); + +typedef VOID (WINAPI *_WriteIoPortByte) (WORD address, BYTE value); +typedef VOID (WINAPI *_WriteIoPortWord) (WORD address, WORD value); +typedef VOID (WINAPI *_WriteIoPortDword) (WORD address, DWORD value); + +typedef BOOL (WINAPI *_WriteIoPortByteEx) (WORD address, BYTE value); +typedef BOOL (WINAPI *_WriteIoPortWordEx) (WORD address, WORD value); +typedef BOOL (WINAPI *_WriteIoPortDwordEx) (WORD address, DWORD value); + +// PCI +typedef VOID (WINAPI *_SetPciMaxBusIndex) (BYTE max); + +typedef BYTE (WINAPI *_ReadPciConfigByte) (DWORD pciAddress, BYTE regAddress); +typedef WORD (WINAPI *_ReadPciConfigWord) (DWORD pciAddress, BYTE regAddress); +typedef DWORD (WINAPI *_ReadPciConfigDword) (DWORD pciAddress, BYTE regAddress); + +typedef BOOL (WINAPI *_ReadPciConfigByteEx) (DWORD pciAddress, DWORD regAddress, PBYTE value); +typedef BOOL (WINAPI *_ReadPciConfigWordEx) (DWORD pciAddress, DWORD regAddress, PWORD value); +typedef BOOL (WINAPI *_ReadPciConfigDwordEx) (DWORD pciAddress, DWORD regAddress, PDWORD value); + +typedef VOID (WINAPI *_WritePciConfigByte) (DWORD pciAddress, BYTE regAddress, BYTE value); +typedef VOID (WINAPI *_WritePciConfigWord) (DWORD pciAddress, BYTE regAddress, WORD value); +typedef VOID (WINAPI *_WritePciConfigDword) (DWORD pciAddress, BYTE regAddress, DWORD value); + +typedef BOOL (WINAPI *_WritePciConfigByteEx) (DWORD pciAddress, DWORD regAddress, BYTE value); +typedef BOOL (WINAPI *_WritePciConfigWordEx) (DWORD pciAddress, DWORD regAddress, WORD value); +typedef BOOL (WINAPI *_WritePciConfigDwordEx) (DWORD pciAddress, DWORD regAddress, DWORD value); + +typedef DWORD (WINAPI *_FindPciDeviceById) (WORD vendorId, WORD deviceId, BYTE index); +typedef DWORD (WINAPI *_FindPciDeviceByClass) (BYTE baseClass, BYTE subClass, BYTE programIf, BYTE index); + +// Memory +#ifdef _PHYSICAL_MEMORY_SUPPORT +typedef DWORD (WINAPI *_ReadDmiMemory) (PBYTE buffer, DWORD count, DWORD unitSize); +typedef DWORD (WINAPI *_ReadPhysicalMemory) (DWORD_PTR address, PBYTE buffer, DWORD count, DWORD unitSize); +typedef DWORD (WINAPI *_WritePhysicalMemory) (DWORD_PTR address, PBYTE buffer, DWORD count, DWORD unitSize); +#endif diff --git a/winring0/OlsApiInitExt.h b/winring0/OlsApiInitExt.h new file mode 100644 index 0000000..af7ba35 --- /dev/null +++ b/winring0/OlsApiInitExt.h @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// Author : hiyohiyo +// Mail : hiyohiyo@crystalmark.info +// Web : http://openlibsys.org/ +// License : The modified BSD license +// +// Copyright 2007-2009 OpenLibSys.org. All rights reserved. +//----------------------------------------------------------------------------- +// for WinRing0 1.3.x + +#pragma once + +#include "OlsApiInitDef.h" + +//----------------------------------------------------------------------------- +// +// Externs +// +//----------------------------------------------------------------------------- + +// DLL +extern _GetDllStatus GetDllStatus; +extern _GetDllVersion GetDllVersion; +extern _GetDriverVersion GetDriverVersion; +extern _GetDriverType GetDriverType; + +extern _InitializeOls InitializeOls; +extern _DeinitializeOls DeinitializeOls; + +// CPU +extern _IsCpuid IsCpuid; +extern _IsMsr IsMsr; +extern _IsTsc IsTsc; + +extern _Hlt Hlt; +extern _Rdmsr Rdmsr; +extern _Wrmsr Wrmsr; +extern _Rdpmc Rdpmc; +extern _Cpuid Cpuid; +extern _Rdtsc Rdtsc; + +extern _HltTx HltTx; +extern _RdmsrTx RdmsrTx; +extern _WrmsrTx WrmsrTx; +extern _RdpmcTx RdpmcTx; +extern _CpuidTx CpuidTx; +extern _RdtscTx RdtscTx; + +extern _HltPx HltPx; +extern _RdmsrPx RdmsrPx; +extern _WrmsrPx WrmsrPx; +extern _RdpmcPx RdpmcPx; +extern _CpuidPx CpuidPx; +extern _RdtscPx RdtscPx; + +// I/O +extern _ReadIoPortByte ReadIoPortByte; +extern _ReadIoPortWord ReadIoPortWord; +extern _ReadIoPortDword ReadIoPortDword; + +extern _ReadIoPortByteEx ReadIoPortByteEx; +extern _ReadIoPortWordEx ReadIoPortWordEx; +extern _ReadIoPortDwordEx ReadIoPortDwordEx; + +extern _WriteIoPortByte WriteIoPortByte; +extern _WriteIoPortWord WriteIoPortWord; +extern _WriteIoPortDword WriteIoPortDword; + +extern _WriteIoPortByteEx WriteIoPortByteEx; +extern _WriteIoPortWordEx WriteIoPortWordEx; +extern _WriteIoPortDwordEx WriteIoPortDwordEx; + +// PCI +extern _SetPciMaxBusIndex SetPciMaxBusIndex; + +extern _ReadPciConfigByte ReadPciConfigByte; +extern _ReadPciConfigWord ReadPciConfigWord; +extern _ReadPciConfigDword ReadPciConfigDword; + +extern _ReadPciConfigByteEx ReadPciConfigByteEx; +extern _ReadPciConfigWordEx ReadPciConfigWordEx; +extern _ReadPciConfigDwordEx ReadPciConfigDwordEx; + +extern _WritePciConfigByte WritePciConfigByte; +extern _WritePciConfigWord WritePciConfigWord; +extern _WritePciConfigDword WritePciConfigDword; + +extern _WritePciConfigByteEx WritePciConfigByteEx; +extern _WritePciConfigWordEx WritePciConfigWordEx; +extern _WritePciConfigDwordEx WritePciConfigDwordEx; + +extern _FindPciDeviceById FindPciDeviceById; +extern _FindPciDeviceByClass FindPciDeviceByClass; + +// Memory +#ifdef _PHYSICAL_MEMORY_SUPPORT +extern _ReadDmiMemory ReadDmiMemory; +extern _ReadPhysicalMemory ReadPhysicalMemory; +extern _WritePhysicalMemory WritePhysicalMemory; +#endif diff --git a/winring0/OlsDef.h b/winring0/OlsDef.h new file mode 100644 index 0000000..a2d9812 --- /dev/null +++ b/winring0/OlsDef.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Author : hiyohiyo +// Mail : hiyohiyo@crystalmark.info +// Web : http://openlibsys.org/ +// License : The modified BSD license +// +// Copyright 2007 OpenLibSys.org. All rights reserved. +//----------------------------------------------------------------------------- + +#pragma once + +//----------------------------------------------------------------------------- +// +// DLL Status Code +// +//----------------------------------------------------------------------------- + +#define OLS_DLL_NO_ERROR 0 +#define OLS_DLL_UNSUPPORTED_PLATFORM 1 +#define OLS_DLL_DRIVER_NOT_LOADED 2 +#define OLS_DLL_DRIVER_NOT_FOUND 3 +#define OLS_DLL_DRIVER_UNLOADED 4 +#define OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK 5 +#define OLS_DLL_UNKNOWN_ERROR 9 + +//----------------------------------------------------------------------------- +// +// Driver Type +// +//----------------------------------------------------------------------------- + +#define OLS_DRIVER_TYPE_UNKNOWN 0 +#define OLS_DRIVER_TYPE_WIN_9X 1 +#define OLS_DRIVER_TYPE_WIN_NT 2 +#define OLS_DRIVER_TYPE_WIN_NT4 3 // Obsolete +#define OLS_DRIVER_TYPE_WIN_NT_X64 4 +#define OLS_DRIVER_TYPE_WIN_NT_IA64 5 // Reseved + +//----------------------------------------------------------------------------- +// +// PCI Error Code +// +//----------------------------------------------------------------------------- + +#define OLS_ERROR_PCI_BUS_NOT_EXIST (0xE0000001L) +#define OLS_ERROR_PCI_NO_DEVICE (0xE0000002L) +#define OLS_ERROR_PCI_WRITE_CONFIG (0xE0000003L) +#define OLS_ERROR_PCI_READ_CONFIG (0xE0000004L) + +//----------------------------------------------------------------------------- +// +// Support Macros +// +//----------------------------------------------------------------------------- + +// Bus Number, Device Number and Function Number to PCI Device Address +#define PciBusDevFunc(Bus, Dev, Func) ((Bus&0xFF)<<8) | ((Dev&0x1F)<<3) | (Func&7) +// PCI Device Address to Bus Number +#define PciGetBus(address) ((address>>8) & 0xFF) +// PCI Device Address to Device Number +#define PciGetDev(address) ((address>>3) & 0x1F) +// PCI Device Address to Function Number +#define PciGetFunc(address) (address&7)