├── .gitignore ├── AUTHORS ├── CODEOWNERS ├── COPYING ├── ChangeLog ├── Makefile.am ├── README.md ├── autogen.sh ├── build_pmwatch.sh ├── config └── .gitignore ├── configure.ac ├── docs ├── PMWatch_User_Guide.docx ├── PMWatch_User_Guide.pdf ├── README ├── README_INSTALL ├── README_INSTALL.txt └── esxi │ ├── README │ └── README_INSTALL ├── m4 └── .gitignore ├── package_pmwatch.sh ├── prepare_pmwatch_release.sh ├── scripts ├── esxi │ ├── install-pmw │ └── pmw_vars.sh ├── install-pmw ├── pmw_vars.cmd └── pmw_vars.sh └── src ├── .gitignore ├── esxi └── pmwatch-stop ├── inc ├── pmw_api.h ├── pmw_collect.h ├── pmw_comm.h ├── pmw_struct.h ├── pmw_utils.h └── pmw_version.h ├── pmw_api.c ├── pmw_collect.c ├── pmw_comm.c ├── pmw_utils.c ├── pmwatch-stop └── pmwatch.c /.gitignore: -------------------------------------------------------------------------------- 1 | INSTALL 2 | .deps/ 3 | .libs/ 4 | Makefile 5 | Makefile.in 6 | aclocal.m4 7 | autom4te.cache/ 8 | bin64/ 9 | config.log 10 | config.status 11 | configure 12 | libtool 13 | pmwatch 14 | *.la 15 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/intel-pmwatch/a19838bef7b6a269553b4206ecd531a781880fea/AUTHORS -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * hari.tg@intel.com -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/intel-pmwatch/a19838bef7b6a269553b4206ecd531a781880fea/ChangeLog -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | AM_CFLAGS = $(PROJECT_CFLAGS) \ 3 | $(KMOD_CFLAGS) \ 4 | $(UDEV_CFLAGS) \ 5 | $(UUID_CFLAGS) 6 | EXTRA_DIST = autogen.sh 7 | AUTOMAKE_OPTIONS = subdir-objects foreign 8 | 9 | if BUILD_PMWATCH 10 | lib_LTLIBRARIES = libpmwcollect.la libpmwapi.la 11 | libpmwcollect_la_SOURCES = src/pmw_collect.c src/pmw_comm.c src/pmw_utils.c 12 | libpmwcollect_la_LDFLAGS = $(BUILD_WITH_LIBIPMCTL_LDFLAGS) 13 | if BUILD_ESXI 14 | libpmwcollect_la_LIBADD = -lipmctl -lvmkuser -lpthread 15 | libpmwcollect_la_CFLAGS = ${NEW_CFLAGS} $(AM_CFLAGS) -DBUILD_ESXI $(BUILD_WITH_LIBIPMCTL_CFLAGS) 16 | else 17 | libpmwcollect_la_LIBADD = -lipmctl -lpthread 18 | libpmwcollect_la_CFLAGS = ${NEW_CFLAGS} $(AM_CFLAGS) $(BUILD_WITH_LIBIPMCTL_CFLAGS) 19 | endif 20 | libpmwapi_la_SOURCES = src/pmw_api.c 21 | libpmwapi_la_LIBADD = -L./.libs libpmwcollect.la 22 | libpmwapi_la_CFLAGS = ${NEW_CFLAGS} $(AM_CFLAGS) $(BUILD_WITH_LIBIPMCTL_CFLAGS) 23 | 24 | include_HEADERS = src/inc/pmw_api.h src/inc/pmw_struct.h 25 | 26 | dist_bin_SCRIPTS = src/pmwatch-stop 27 | 28 | bin_PROGRAMS = pmwatch 29 | pmwatch_SOURCES = src/pmwatch.c 30 | pmwatch_LDFLAGS = -static 31 | pmwatch_CFLAGS = ${NEW_CFLAGS} $(AM_CFLAGS) $(BUILD_WITH_LIBIPMCTL_CFLAGS) 32 | if BUILD_ESXI 33 | pmwatch_LDADD = $(lib_LTLIBRARIES) 34 | else 35 | pmwatch_LDADD = $(lib_LTLIBRARIES) $(UUID_LIBS) $(KMOD_LIBS) $(UDEV_LIBS) 36 | endif 37 | endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DISCONTINUATION OF PROJECT 2 | 3 | This project will no longer be maintained by Intel. 4 | 5 | Intel has ceased development and contributions including, but not limited to, maintenance, bug fixes, new releases, or updates, to this project. 6 | 7 | Intel no longer accepts patches to this project. 8 | 9 | If you have an ongoing need to use this project, are interested in independently developing it, or would like to maintain patches for the open source software community, please create your own fork of this project. 10 | 11 | Contact: webadmin@linux.intel.com 12 | # Intel® PMWatch 13 | Intel® PMWatch (PersistentMemoryWatch) is a tool that monitors and reports the performance and health information metrics of the Intel® Optane™ DC Persistent Memory. 14 | 15 | # Supported OS 16 | Linux 17 | 18 | # Supported Platforms 19 | Any Intel® platform with Intel® Optane™ DC Persistent Memory. 20 | 21 | # Documentation 22 | The information about features and options available in the tool can be found at [PMWatch_User_Guide.pdf](https://github.com/intel/intel-pmwatch/blob/master/docs/PMWatch_User_Guide.pdf). 23 | 24 | # Build 25 | **Check *[Dependencies](#dependencies)* section to resolve the dependencies.** 26 | 27 | > $ ./autogen.sh
28 | > $ ./configure [--with-libipmctl=\ --with-libipmctl-inc=\ --prefix=\$HOME/pmwatch --bindir=\$HOME/pmwatch/bin64 --libdir=\$HOME/pmwatch/lib64]
29 | > $ make
30 | > $ make -j install 31 | 32 | The parameters for configure are optional.
33 | **If you are building PMWatch for [collectd (dcpmm plugin)](https://github.com/collectd/collectd) dependency, make sure to use `--libdir=/lib64`.** 34 | 35 | ## Using scripts to build and create install package 36 | You can *also* use the scripts available for build and package creation. 37 | 38 | > $ ./prepare_pmwatch_release.sh M= m= u= with-libipmctl= with-libipmctl-inc= os=linux -r 39 | 40 | Check *[pmw_version.h](https://github.com/intel/intel-pmwatch/blob/master/src/inc/pmw_version.h)* to obtain major, minor version and update number. 41 | 42 | ### Build 43 | Standalone build script. 44 | 45 | > $ ./build_pmwatch.sh -all with-libipmctl= with-libipmctl-inc= os=linux 46 | 47 | ### Package 48 | Standalone packaging script. 49 | 50 | > $ ./package_pmwatch.sh M= m= u= os=linux -r 51 | 52 | Use the `-h` option to get more details about the [scripts'](https://github.com/intel/intel-pmwatch/) options. 53 | 54 | ## Dependencies 55 | PMWatch is dependent on [libipmctl](https://github.com/intel/ipmctl). 56 | 57 | Perform the following steps to install the tool dependencies. 58 | 59 | #### Fedora 60 | > $ yum install daxctl-devel.x86_64 ndctl-devel.x86_64 libipmctl-devel.x86_64 61 | 62 | #### CentOS, RHEL 63 | > $ wget https://copr-be.cloud.fedoraproject.org/results/jhli/ipmctl/epel-7-x86_64/00874029-ipmctl/libipmctl-devel-02.00.00.3446-1.el7.x86_64.rpm
64 | > $ yum install ndctl-libs.x86_64
65 | > $ rpm -ivh libipmctl-devel-02.00.00.3446-1.el7.x86_64.rpm 66 | 67 | #### Ubuntu, Debian 68 | > $ apt install libdaxctl-dev libndctl-dev libipmctl-dev 69 | 70 | ### Build tools 71 | Install the following build tools: 72 | > autoconf, automake, flex, bison, libtool, pkg-config, libkmod-dev, 73 | > libudev-dev, uuid-dev 74 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf --force --install --symlink -I config -I m4 3 | -------------------------------------------------------------------------------- /build_pmwatch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | print_usage_and_exit() 4 | { 5 | echo "Usage: ./build_pmwatch.sh <-all|-cm|-m> with-libipmctl= with-libipmctl-inc= os=[esxi]" 6 | 7 | exit $1 8 | } 9 | 10 | if [ $# = 0 ] ; then 11 | print_usage_and_exit 0 12 | fi 13 | 14 | RUN_CONFIG= 15 | RUN_MAKE= 16 | RUN_CLEANMAKE= 17 | BUILD_ESXI= 18 | WITH_IPMCTL= 19 | WITH_IPMCTL_INC= 20 | while [ $# -gt 0 ] ; do 21 | case "$1" in 22 | --help | -h) 23 | print_usage_and_exit 0 24 | ;; 25 | -all) 26 | RUN_CONFIG=1 27 | RUN_CLEANMAKE=1 28 | RUN_MAKE=1 29 | ;; 30 | -cm) 31 | RUN_CLEANMAKE=1 32 | RUN_MAKE=1 33 | ;; 34 | -m) 35 | RUN_MAKE=1 36 | ;; 37 | with-libipmctl=*) 38 | WITH_IPMCTL=`echo $1 | sed s?^with-libipmctl=??g` 39 | ;; 40 | with-libipmctl-inc=*) 41 | WITH_IPMCTL_INC=`echo $1 | sed s?^with-libipmctl-inc=??g` 42 | ;; 43 | os=*) 44 | OS=`echo $1 | sed s?^os=??g` 45 | if [[ ${OS} = "esxi" ]] ; then 46 | BUILD_ESXI="BUILD_ESXI" 47 | fi 48 | ;; 49 | *) 50 | echo "" 51 | echo "Invalid option: \"$1\"" 52 | print_usage_and_exit 1 53 | ;; 54 | esac 55 | shift 56 | done 57 | 58 | if [[ -n "${RUN_CONFIG}" ]] ; then 59 | echo "***************************************" 60 | echo "Executing ./autogen.sh..." 61 | echo "***************************************" 62 | ./autogen.sh 63 | if [[ $? -eq 0 ]] ; then 64 | echo "***************************************" 65 | echo "Execution of autogen.sh successful." 66 | echo "***************************************" 67 | echo 68 | else 69 | echo "***************************************" 70 | echo "Execution of autogen.sh failed." 71 | echo "***************************************" 72 | echo 73 | exit 1 74 | fi 75 | 76 | echo "***************************************" 77 | echo "Executing ./configure..." 78 | echo "***************************************" 79 | ./configure BUILD_ESXI=${BUILD_ESXI} --with-libipmctl=${WITH_IPMCTL} --with-libipmctl-inc=${WITH_IPMCTL_INC} 80 | if [[ $? -eq 0 ]] ; then 81 | echo "***************************************" 82 | echo "Execution of configure successful." 83 | echo "***************************************" 84 | echo 85 | else 86 | echo "***************************************" 87 | echo "Execution of configure failed." 88 | echo "***************************************" 89 | echo 90 | exit 1 91 | fi 92 | fi 93 | 94 | if [[ -n "${RUN_CLEANMAKE}" ]] ; then 95 | echo "***************************************" 96 | echo "Executing make clean..." 97 | echo "***************************************" 98 | make clean 99 | if [[ $? -eq 0 ]] ; then 100 | echo "***************************************" 101 | echo "Execution of make clean successful." 102 | echo "***************************************" 103 | echo 104 | else 105 | echo "***************************************" 106 | echo "Execution of make clean failed." 107 | echo "***************************************" 108 | echo 109 | exit 1 110 | fi 111 | fi 112 | 113 | if [[ -n "${RUN_MAKE}" ]] ; then 114 | echo "***************************************" 115 | echo "Executing make..." 116 | echo "***************************************" 117 | make -j 10 | tee makelog.log 118 | res=$? 119 | cat makelog.log | grep "Nothing to be done for 'all'" >> /dev/null 2>&1 120 | res_grep=$? 121 | rm -rf makelog.log 122 | if [[ ${res} -eq 0 && ${res_grep} -ne 0 ]] ; then 123 | echo "***************************************" 124 | echo "Execution of make successful." 125 | echo "***************************************" 126 | echo 127 | else 128 | echo "***************************************" 129 | echo "Execution of make failed." 130 | echo "***************************************" 131 | echo 132 | exit 1 133 | fi 134 | fi 135 | -------------------------------------------------------------------------------- /config/.gitignore: -------------------------------------------------------------------------------- 1 | compile 2 | config.guess 3 | config.sub 4 | depcomp 5 | install-sh 6 | ltmain.sh 7 | missing -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(Intel PMWatch, 3.0) 2 | PROJECT_CFLAGS="-std=gnu99 -I./src/inc" 3 | 4 | AC_SUBST(PROJECT_CFLAGS) 5 | AC_CONFIG_AUX_DIR(config) 6 | AC_CONFIG_SRCDIR(src/pmwatch.c) 7 | : ${CXXFLAGS="-O2 -D_FORTIFY_SOURCE=2 -s"} 8 | AC_PROG_CXX 9 | : ${CFLAGS="-O2 -D_FORTIFY_SOURCE=2 -s"} 10 | AC_PROG_CC 11 | AM_INIT_AUTOMAKE 12 | LT_INIT 13 | AC_CONFIG_MACRO_DIR([m4]) 14 | AC_CONFIG_FILES([Makefile]) 15 | AM_CONDITIONAL(BUILD_ESXI, [test x${BUILD_ESXI} = xBUILD_ESXI]) 16 | if test x${BUILD_ESXI} != xBUILD_ESXI ; then 17 | PKG_CHECK_MODULES([KMOD], [libkmod]) 18 | PKG_CHECK_MODULES([UDEV], [libudev]) 19 | PKG_CHECK_MODULES([UUID], [uuid]) 20 | fi 21 | 22 | m4_divert_once([HELP_WITH], [ 23 | PMWatch Dependencies:]) 24 | 25 | # --with-libipmctl BEGIN 26 | AC_ARG_WITH([libipmctl], 27 | [AS_HELP_STRING([--with-libipmctl@<:@=PREFIX@:>@], [Path to libipmctl.])], 28 | [ 29 | if test "x$withval" = "xyes" || test "x$withval" = "xno" ; then 30 | with_libipmctl="$withval" 31 | else 32 | with_libipmctl_ldflags="-L$withval" 33 | with_libipmctl="yes" 34 | fi 35 | ], 36 | [with_libipmctl="yes"] 37 | ) 38 | 39 | if test "x$with_libipmctl" = "xyes" ; then 40 | SAVE_LDFLAGS="$LDFLAGS" 41 | LDFLAGS="$LDFLAGS $with_libipmctl_ldflags" 42 | 43 | AC_CHECK_LIB([ipmctl], [nvm_create_context], 44 | [with_libipmctls="yes"], 45 | [with_libipmctl="no (symbol 'nvm_create_context' not found)"] 46 | ) 47 | 48 | AC_CHECK_LIB([ipmctl], [nvm_free_context], 49 | [with_libipmctls="yes"], 50 | [with_libipmctl="no (symbol 'nvm_free_context' not found)"] 51 | ) 52 | 53 | AC_CHECK_LIB([ipmctl], [nvm_get_build_number], 54 | [with_libipmctls="yes"], 55 | [with_libipmctl="no (symbol 'nvm_get_build_number' not found)"] 56 | ) 57 | 58 | AC_CHECK_LIB([ipmctl], [nvm_get_nvm_capacities], 59 | [with_libipmctls="yes"], 60 | [with_libipmctl="no (symbol 'nvm_get_nvm_capacities' not found)"] 61 | ) 62 | 63 | AC_CHECK_LIB([ipmctl], [nvm_get_number_of_devices], 64 | [with_libipmctls="yes"], 65 | [with_libipmctl="no (symbol 'nvm_get_number_of_devices' not found)"] 66 | ) 67 | 68 | AC_CHECK_LIB([ipmctl], [nvm_get_devices], 69 | [with_libipmctls="yes"], 70 | [with_libipmctl="no (symbol 'nvm_get_devices' not found)"] 71 | ) 72 | 73 | AC_CHECK_LIB([ipmctl], [nvm_send_device_passthrough_cmd], 74 | [with_libipmctls="yes"], 75 | [with_libipmctl="no (symbol 'nvm_send_device_passthrough_cmd' not found)"] 76 | ) 77 | 78 | AC_CHECK_LIB([ipmctl], [nvm_get_major_version], 79 | [with_libipmctls="yes"], 80 | [with_libipmctl="no (symbol 'nvm_get_major_version' not found)"] 81 | ) 82 | 83 | AC_CHECK_LIB([ipmctl], [nvm_get_minor_version], 84 | [with_libipmctls="yes"], 85 | [with_libipmctl="no (symbol 'nvm_get_minor_version' not found)"] 86 | ) 87 | 88 | AC_CHECK_LIB([ipmctl], [nvm_get_hotfix_number], 89 | [with_libipmctls="yes"], 90 | [with_libipmctl="no (symbol 'nvm_get_hotfix_number' not found)"] 91 | ) 92 | 93 | AC_CHECK_LIB([ipmctl], [nvm_get_build_number], 94 | [with_libipmctls="yes"], 95 | [with_libipmctl="no (symbol 'nvm_get_build_number' not found)"] 96 | ) 97 | 98 | LDFLAGS="$SAVE_LDFLAGS" 99 | fi 100 | 101 | if test "x$with_libipmctl" = "xyes" ; then 102 | BUILD_WITH_LIBIPMCTL_LDFLAGS="$with_libipmctl_ldflags" 103 | fi 104 | 105 | AC_SUBST([BUILD_WITH_LIBIPMCTL_LDFLAGS]) 106 | # END 107 | 108 | # --with-libipmctl-inc BEGIN 109 | AC_ARG_WITH([libipmctl-inc], 110 | [AS_HELP_STRING([--with-libipmctl-inc@<:@=PREFIX@:>@], [Path to libipmctl header files.])], 111 | [ 112 | if test "x$withval" = "xyes" || test "x$withval" = "xno" ; then 113 | with_libipmctlinc="$withval" 114 | else 115 | with_libipmctl_cflags="-I$withval" 116 | with_libipmctlinc="yes" 117 | fi 118 | ], 119 | [with_libipmctlinc="yes"] 120 | ) 121 | 122 | if test "x$with_libipmctlinc" = "xyes" ; then 123 | SAVE_CFLAGS="$CFLAGS" 124 | CFLAGS="$CFLAGS $with_libipmctl_cflags" 125 | 126 | AC_CHECK_HEADERS([NvmSharedDefs.h], 127 | [with_libipmctlinc="yes"], 128 | [with_libipmctlinc="no (NvmSharedDefs.h not found)"] 129 | ) 130 | 131 | AC_CHECK_HEADERS([nvm_types.h], 132 | [with_libipmctlinc="yes"], 133 | [with_libipmctlinc="no (nvm_types.h not found)"] 134 | ) 135 | 136 | AC_CHECK_HEADERS([export_api.h], 137 | [with_libipmctlinc="yes"], 138 | [with_libipmctlinc="no (export_api.h not found)"] 139 | ) 140 | 141 | AC_CHECK_HEADERS([nvm_management.h], 142 | [with_libipmctlinc="yes"], 143 | [with_libipmctlinc="no (nvm_management.h not found)"] 144 | ) 145 | 146 | CFLAGS="$SAVE_CFLAGS" 147 | fi 148 | 149 | if test "x$with_libipmctlinc" = "xyes" ; then 150 | BUILD_WITH_LIBIPMCTL_CFLAGS="$with_libipmctl_cflags" 151 | fi 152 | 153 | AC_SUBST([BUILD_WITH_LIBIPMCTL_CFLAGS]) 154 | # END 155 | 156 | AM_CONDITIONAL(BUILD_PMWATCH, test "x$with_libipmctl" = "xyes") 157 | if test "x$with_libipmctlinc" != "xyes" ; then 158 | AM_CONDITIONAL(BUILD_PMWATCH, test "x$with_libipmctlinc" = "xyes") 159 | fi 160 | 161 | AC_OUTPUT 162 | 163 | AC_MSG_RESULT() 164 | AC_MSG_RESULT([Library availability:]) 165 | AC_MSG_RESULT([ libipmctl . . . . . . . $with_libipmctl]) 166 | AC_MSG_RESULT([Header files availability:]) 167 | AC_MSG_RESULT([ libipmctl-inc . . . . . . $with_libipmctlinc]) 168 | AC_MSG_RESULT() 169 | -------------------------------------------------------------------------------- /docs/PMWatch_User_Guide.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/intel-pmwatch/a19838bef7b6a269553b4206ecd531a781880fea/docs/PMWatch_User_Guide.docx -------------------------------------------------------------------------------- /docs/PMWatch_User_Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/intel-pmwatch/a19838bef7b6a269553b4206ecd531a781880fea/docs/PMWatch_User_Guide.pdf -------------------------------------------------------------------------------- /docs/README: -------------------------------------------------------------------------------- 1 | Introduction: Intel PMWatch is a tool that monitors and reports the behavior of the Intel® Optane™ DC persistent memory. 2 | It consists of a data collector. 3 | 4 | Pre-requisites: 5 | Linux OS - Fedora or RHEL with nvdimm driver support 6 | Intel® Optane™ DC persistent memory firmware version: 01.01.00.5142 and newer 7 | 8 | PMWatch instructions: 9 | Run "source pmw_vars.sh" to set up the environment before using the tool. 10 | 11 | Tools Usage: 12 | pmwatch "" "" -f outputfile.csv 13 | 14 | Example: "pmwatch 1 100" reads the counters every 1 second 100 times. 15 | 16 | The output of PMWatch is in a CSV format listing the metrics for each DIMM on the system and outputs the counts for each interval. 17 | 18 | Explanation of metrics: 19 | 20 | Memory performance metrics: 21 | bytes_read (derived) : number of bytes transacted by the read operations 22 | bytes_written (derived): number of bytes transacted by the write operations 23 | Note: The total number of bytes transacted in any sample is computed as bytes_read (derived) + 2 * bytes_written (derived). 24 | 25 | Formula: 26 | bytes_read : (read_64B_ops_received - write_64B_ops_received) * 64 27 | bytes_written: write_64B_ops_received * 64 28 | 29 | 30 | read_hit_ratio (derived) : measures the efficiency of the buffer in the read path. Range of 0.0 - 0.75. 31 | write_hit_ratio (derived): measures the efficiency of the buffer in the write path. Range of 0.0 - 0.1. 32 | 33 | 0.75 : indicates 100% sequential write traffic 34 | > 0.75 : indicates writing to 64B addresses that are still in the write buffer (never had to go to media) 35 | 1 or ~1: likely writing to a specific address or small range of addresses (fitting in write buffer) for long periods of time. 36 | 37 | Formula: 38 | read_hit_ratio : (cpu_read_ops - media_read_ops) / cpu_read_ops 39 | write_hit_ratio: (cpu_write_ops - media_write_ops) / cpu_write_ops 40 | 41 | 42 | media_read_ops (derived) : 43 | media_write_ops (derived): 44 | Number of read and write operations performed to the physical media. Each operation transacts a 256 bytes operation. 45 | 46 | Formula: 47 | media_read_ops : (read_64B_ops_received - write_64B_ops_received) / 4 48 | media_write_ops: write_64B_ops_received / 4 49 | 50 | 51 | read_64B_ops_received : 52 | write_64B_ops_received: 53 | Number of read and write operations performed from/to the physical media. Each operation transacts a 64byte operation. These operations includes commands transacted for maintenance as well as the commands transacted by the CPU. 54 | 55 | 56 | cpu_read_ops : 57 | cpu_write_ops: 58 | Number of read and write operations received from the CPU (memory controller), for the Memory Mode and AppDirect Mode partitions. 59 | 60 | Health Information: 61 | health_status: 62 | Overall health summary. 63 | 64 | Value: Health Status 65 | 0: Normal 66 | 1: Non-critical 67 | 2: Critical 68 | 3: Fatal 69 | 70 | lifespan_used: 71 | The module’s used life as a percentage value of factory expected like span. 72 | 73 | lifespan_remaining: 74 | The module’s remaining life as a percentage value of factory expected like span. 75 | 76 | power_on_time: 77 | The lifetime the DIMM has been powered on in seconds. 78 | 79 | uptime: 80 | The current uptime of the DIMM for the current power cycle in seconds. 81 | 82 | last_shutdown_time: 83 | The time the system was last shutdown. The time is represented in epoch (seconds). 84 | 85 | media_temp : 86 | The media’s current temperature in degrees Celsius. 87 | 88 | controller_temp : 89 | The controller’s current temperature in degrees Celsius. 90 | 91 | max_media_temp : 92 | The media’s the highest temperature reported in degrees Celsius. 93 | 94 | max_controller_temp : 95 | The controller’s highest temperature reported in degrees Celsius. 96 | -------------------------------------------------------------------------------- /docs/README_INSTALL: -------------------------------------------------------------------------------- 1 | Intel PMWatch 2 | 3 | How to install the tool: 4 | $ ./install-pmw 5 | 6 | How to set up environment: 7 | $ source /opt/intel/pmwatch/pmw_vars.sh 8 | -------------------------------------------------------------------------------- /docs/README_INSTALL.txt: -------------------------------------------------------------------------------- 1 | Intel PMWatch 2 | 3 | How to install the tool: 4 | Run the below command with Administrator privilege 5 | $ install-pmw.cmd pmwatch_.zip 6 | 7 | How to set up environment: 8 | $ C:\Intel\pmwatch\pmw_vars.cmd 9 | -------------------------------------------------------------------------------- /docs/esxi/README: -------------------------------------------------------------------------------- 1 | Introduction: Intel PMWatch is a tool that monitors and reports the behavior of the Intel® Optane™ DC persistent memory. 2 | It consists of a data collector. 3 | 4 | Pre-requisites: 5 | Supported Hypervisor: VMware ESXi 6.7. 6 | ESXi should be configured in maintenance mode. 7 | 8 | Run "cd /opt/intel/pmwatch && source pmw_vars.sh && cd -" to set up the environment before using the tool. 9 | 10 | Tools Usage: 11 | pmwatch "" "" -f outputfile.csv 12 | 13 | Example: "pmwatch 1 100" reads the counters every 1 second 100 times. 14 | 15 | The output of PMWatch is in a CSV format listing the metrics for each DIMM on the system and outputs the counts for each interval. 16 | 17 | Explanation of metrics: 18 | bytes_read (derived) : number of bytes transacted by the read operations 19 | bytes_written (derived): number of bytes transacted by the write operations 20 | Note: The total number of bytes transacted in any sample is computed as bytes_read (derived) + 2 * bytes_written (derived). 21 | 22 | Formula: 23 | bytes_read : (read_64B_ops_received - write_64B_ops_received) * 64 24 | bytes_written: write_64B_ops_received * 64 25 | 26 | 27 | read_hit_ratio (derived) : measures the efficiency of the buffer in the read path. Range of 0.0 - 0.75. 28 | write_hit_ratio (derived): measures the efficiency of the buffer in the write path. Range of 0.0 - 0.1. 29 | 30 | 0.75 : indicates 100% sequential write traffic 31 | > 0.75 : indicates writing to 64B addresses that are still in the write buffer (never had to go to media) 32 | 1 or ~1: likely writing to a specific address or small range of addresses (fitting in write buffer) for long periods of time. 33 | 34 | Formula: 35 | read_hit_ratio : (cpu_read_ops - media_read_ops) / cpu_read_ops 36 | write_hit_ratio: (cpu_write_ops - media_write_ops) / cpu_write_ops 37 | 38 | 39 | media_read_ops (derived) : 40 | media_write_ops (derived): 41 | Number of read and write operations performed to the physical media. Each operation transacts a 256 bytes operation. 42 | 43 | Formula: 44 | media_read_ops : (read_64B_ops_received - write_64B_ops_received) / 4 45 | media_write_ops: write_64B_ops_received / 4 46 | 47 | 48 | read_64B_ops_received : 49 | write_64B_ops_received: 50 | Number of read and write operations performed from/to the physical media. Each operation transacts a 64byte operation. These operations includes commands transacted for maintenance as well as the commands transacted by the CPU. 51 | 52 | 53 | cpu_read_ops : 54 | cpu_write_ops: 55 | Number of read and write operations received from the CPU (memory controller), for the Memory Mode and AppDirect Mode partitions. 56 | -------------------------------------------------------------------------------- /docs/esxi/README_INSTALL: -------------------------------------------------------------------------------- 1 | Intel PMWatch 2 | 3 | How to install the tool: 4 | $ ./install-pmw pmwatch_.tar.bz2 5 | 6 | How to set up environment: 7 | $ cd /opt/intel/pmwatch 8 | $ source pmw_vars.sh 9 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | *.m4 -------------------------------------------------------------------------------- /package_pmwatch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a 8 | # copy of this software and associated documentation files (the "Software"), 9 | # to deal in the Software without restriction, including without limitation 10 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | # and/or sell copies of the Software, and to permit persons to whom the 12 | # Software is furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | # DEALINGS IN THE SOFTWARE. 24 | 25 | PWD="pwd" 26 | CWD=`${PWD}` 27 | 28 | print_usage_and_exit() 29 | { 30 | echo "Usage: ./package_pmwatch.sh M= m= u= os= [-r]" 31 | echo "Option:" 32 | echo " -r removes the package directory after tarball creation" 33 | echo 34 | echo "Always run this script at the base directory." 35 | 36 | exit $1 37 | } 38 | 39 | if [ $# = 0 ] ; then 40 | print_usage_and_exit 0 41 | fi 42 | 43 | OS= 44 | remove_package_dir= 45 | while [ $# -gt 0 ] ; do 46 | case "$1" in 47 | --help | -h) 48 | print_usage_and_exit 0 49 | ;; 50 | M=*) 51 | MAJOR_VERSION=`echo $1 | sed s?^M=??g` 52 | if [[ ! ${MAJOR_VERSION} =~ ^-?[0-9]+$ ]] ; then 53 | echo "Invalid argument $1! Use integer" 54 | exit 55 | fi 56 | ;; 57 | m=*) 58 | MINOR_VERSION=`echo $1 | sed s?^m=??g` 59 | if [[ ! ${MINOR_VERSION} =~ ^-?[0-9]+$ ]] ; then 60 | echo "Invalid argument $1! Use integer" 61 | exit 62 | fi 63 | ;; 64 | u=*) 65 | UPDATE_NUM=`echo $1 | sed s?^u=??g` 66 | if [[ ! ${UPDATE_NUM} =~ ^-?[0-9]+$ ]] ; then 67 | echo "Invalid argument $1! Use integer" 68 | exit 69 | fi 70 | ;; 71 | os=*) 72 | OS=`echo $1 | sed s?^os=??g` 73 | if [[ ${OS} != "esxi" && ${OS} != "linux" ]] ; then 74 | echo "" 75 | echo "Invalid OS!" 76 | print_usage_and_exit 77 | fi 78 | ;; 79 | -r) 80 | remove_package_dir=1 81 | ;; 82 | *) 83 | echo "" 84 | echo "Invalid option: \"$1\"" 85 | print_usage_and_exit 1 86 | ;; 87 | esac 88 | shift 89 | done 90 | 91 | 92 | TOOL_NAME="pmwatch" 93 | PACKAGE_NAME="${TOOL_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${UPDATE_NUM}" 94 | PKG="package" 95 | OUTER_PACKAGE_NAME="${PACKAGE_NAME}_${OS}_${PKG}" 96 | PACKAGE_DIR="${CWD}/${PACKAGE_NAME}" 97 | OUTER_PACKAGE_DIR="${CWD}/${OUTER_PACKAGE_NAME}" 98 | 99 | if [[ ${OS} == "esxi" ]] ; then 100 | DIR_EXT="esxi" 101 | fi 102 | 103 | # setting up path 104 | PMW_SCRIPTS_DIR="scripts" 105 | PMW_DOCS_DIR="docs" 106 | PMW_SCRIPTS_PATH="${CWD}/${PMW_SCRIPTS_DIR}" 107 | PMW_DOCS_PATH="${CWD}/${PMW_DOCS_DIR}" 108 | BIN_DIR="${CWD}/bin64" 109 | LIB_DIR="${CWD}/lib64" 110 | INCLUDE_DIR="${CWD}/include" 111 | 112 | # scripts 113 | # pmw_vars 114 | PMW_VARS="pmw_vars.sh" 115 | if [[ ${OS} == "esxi" ]] ; then 116 | PMW_VARS="${DIR_EXT}/pmw_vars.sh" 117 | fi 118 | PMW_VARS_PATH="${PMW_SCRIPTS_PATH}/${PMW_VARS}" 119 | 120 | # installer 121 | INSTALL_SCRIPT="install-pmw" 122 | if [[ ${OS} == "esxi" ]] ; then 123 | INSTALL_SCRIPT="${DIR_EXT}/install-pmw" 124 | fi 125 | INSTALL_SCRIPT_PATH="${PMW_SCRIPTS_PATH}/${INSTALL_SCRIPT}" 126 | 127 | # docs 128 | README="README" 129 | if [[ ${OS} == "esxi" ]] ; then 130 | README="${DIR_EXT}/README" 131 | fi 132 | PMW_USER_GUIDE="PMWatch_User_Guide.pdf" 133 | README_INSTALL="README_INSTALL" 134 | if [[ ${OS} == "esxi" ]] ; then 135 | README_INSTALL="${DIR_EXT}/README_INSTALL" 136 | fi 137 | README_PATH="${PMW_DOCS_PATH}/${README}" 138 | README_INSTALL_PATH="${PMW_DOCS_PATH}/${README_INSTALL}" 139 | PMW_USER_GUIDE_PATH="${PMW_DOCS_PATH}/${PMW_USER_GUIDE}" 140 | 141 | # bin 142 | PMW_BIN1_PATH="${CWD}/pmwatch" 143 | PMW_BIN2_PATH="${CWD}/src/pmwatch-stop" 144 | 145 | # libs 146 | PMW_LIB_BASE_PATH="${CWD}/.libs" 147 | PMW_LIB1_NAME="libpmwapi.so*" 148 | PMW_LIB2_NAME="libpmwcollect.so*" 149 | PMW_LIB1_PATH="${PMW_LIB_BASE_PATH}/${PMW_LIB1_NAME}" 150 | PMW_LIB2_PATH="${PMW_LIB_BASE_PATH}/${PMW_LIB2_NAME}" 151 | 152 | # header files 153 | PMW_INCLUDE_BASE_PATH="${CWD}/src/inc" 154 | PMW_HEADER_FILE1="pmw_api.h" 155 | PMW_HEADER_FILE2="pmw_struct.h" 156 | PMW_INCLUDE1_PATH="${PMW_INCLUDE_BASE_PATH}/${PMW_HEADER_FILE1}" 157 | PMW_INCLUDE2_PATH="${PMW_INCLUDE_BASE_PATH}/${PMW_HEADER_FILE2}" 158 | 159 | # tar_name 160 | TAR_EXT=".tar.bz2" 161 | TAR_NAME="${PACKAGE_NAME}${TAR_EXT}" 162 | TAR_PATH="${CWD}/${TAR_NAME}" 163 | OUTER_TAR_NAME="${OUTER_PACKAGE_NAME}${TAR_EXT}" 164 | OUTER_TAR_PATH="${CWD}/${OUTER_TAR_NAME}" 165 | 166 | 167 | mkdir ${PACKAGE_DIR} 168 | if [ $? -ne 0 ] ; then 169 | echo "Package directory not successfully created!" 170 | exit 171 | fi 172 | 173 | if [[ ! -d ${BIN_DIR} ]]; then 174 | mkdir ${BIN_DIR} 175 | if [[ $? -ne 0 ]] ; then 176 | echo "Package creation not successful; mkdir ${BIN_DIR}!" 177 | echo 178 | exit 1 179 | fi 180 | fi 181 | 182 | cp -a {${PMW_BIN1_PATH},${PMW_BIN2_PATH}} ${BIN_DIR} 183 | if [ $? -ne 0 ] ; then 184 | echo "Package creation not successful; ${BIN_DIR} copy!" 185 | echo 186 | exit 1 187 | fi 188 | 189 | if [[ ! -d ${LIB_DIR} ]]; then 190 | mkdir ${LIB_DIR} 191 | if [[ $? -ne 0 ]] ; then 192 | echo "Package creation not successful; mkdir ${LIB_DIR}!" 193 | echo 194 | exit 1 195 | fi 196 | fi 197 | 198 | cp -a ${PMW_LIB1_PATH} ${LIB_DIR} 199 | if [ $? -ne 0 ] ; then 200 | echo "Package creation not successful; ${LIB_DIR} copy!" 201 | echo 202 | exit 1 203 | fi 204 | cp -a ${PMW_LIB2_PATH} ${LIB_DIR} 205 | if [ $? -ne 0 ] ; then 206 | echo "Package creation not successful; ${LIB_DIR} copy!" 207 | echo 208 | exit 1 209 | fi 210 | 211 | if [[ ! -d ${INCLUDE_DIR} ]]; then 212 | mkdir ${INCLUDE_DIR} 213 | if [[ $? -ne 0 ]] ; then 214 | echo "Package creation not successful; mkdir ${LIB_DIR}!" 215 | echo 216 | exit 1 217 | fi 218 | fi 219 | 220 | cp -a ${PMW_INCLUDE1_PATH} ${INCLUDE_DIR} 221 | if [ $? -ne 0 ] ; then 222 | echo "Package creation not successful; ${INCLUDE_DIR} copy!" 223 | echo 224 | exit 1 225 | fi 226 | cp -a ${PMW_INCLUDE2_PATH} ${INCLUDE_DIR} 227 | if [ $? -ne 0 ] ; then 228 | echo "Package creation not successful; ${INCLUDE_DIR} copy!" 229 | echo 230 | exit 1 231 | fi 232 | 233 | mv {${BIN_DIR},${LIB_DIR},${INCLUDE_DIR}} ${PACKAGE_DIR} 234 | if [ $? -ne 0 ] ; then 235 | echo "Package creation not successful; intial copy!" 236 | echo 237 | if [ -d ${PACKAGE_DIR} ] ; then 238 | rm -rf ${PACKAGE_DIR} 239 | fi 240 | exit 1 241 | fi 242 | cp -r {${PMW_VARS_PATH},${README_PATH},${PMW_USER_GUIDE_PATH}} ${PACKAGE_DIR} 243 | if [ $? -ne 0 ] ; then 244 | echo "Package creation not successful; intial copy!" 245 | echo 246 | if [ -d ${PACKAGE_DIR} ] ; then 247 | rm -rf ${PACKAGE_DIR} 248 | fi 249 | exit 1 250 | fi 251 | 252 | echo "Package directory created successfully." 253 | 254 | res=`tar cvfj ${TAR_PATH} ${PACKAGE_NAME}` 255 | if [ $? -ne 0 ] ; then 256 | echo "Core package tar ball cannot be created successfully." 257 | echo 258 | if [ -d ${PACKAGE_DIR} ] ; then 259 | rm -rf ${PACKAGE_DIR} 260 | fi 261 | exit 1 262 | else 263 | echo "Core package tar ball created successfully." 264 | fi 265 | 266 | mkdir ${OUTER_PACKAGE_DIR} 267 | if [ $? -ne 0 ] ; then 268 | echo "Unable to create ${OUTER_PACKAGE_DIR}! Exiting..." 269 | echo 270 | rm -rf ${PACKAGE_DIR} ${TAR_PATH} 271 | if [ -d ${OUTER_PACKAGE_DIR} ] ; then 272 | rm -rf ${OUTER_PACKAGE_DIR} 273 | fi 274 | exit 1 275 | fi 276 | 277 | cp {${TAR_PATH},${README_INSTALL_PATH},${INSTALL_SCRIPT_PATH}} ${OUTER_PACKAGE_DIR} 278 | if [ $? -ne 0 ] ; then 279 | echo "Unable to package ${OUTER_PACKAGE_NAME}; copy failed! Exiting..." 280 | echo 281 | rm -rf ${PACKAGE_DIR} ${TAR_PATH} 282 | if [ -d ${OUTER_PACKAGE_DIR} ] ; then 283 | rm -rf ${OUTER_PACKAGE_DIR} 284 | fi 285 | exit 1 286 | fi 287 | 288 | res=`tar cvfj ${OUTER_TAR_PATH} ${OUTER_PACKAGE_NAME}` 289 | if [ $? -ne 0 ] ; then 290 | echo "Final package tar ball cannot be created successfully." 291 | echo 292 | rm -rf ${PACKAGE_DIR} ${TAR_PATH} ${OUTER_PACKAGE_DIR} 293 | exit 1 294 | else 295 | echo "Final package tar ball created successfully." 296 | fi 297 | 298 | if [[ -n "${remove_package_dir}" ]] ; then 299 | rm -rf ${PACKAGE_DIR} ${OUTER_PACKAGE_DIR} ${TAR_PATH} 300 | if [ $? -ne 0 ] ; then 301 | echo "${PACKAGE_NAME}, ${OUTER_PACKAGE_NAME} and ${TAR_NAME} were not removed." 302 | echo 303 | exit 1 304 | else 305 | echo "${PACKAGE_NAME}, ${OUTER_PACKAGE_NAME} and ${TAR_NAME} were removed." 306 | echo 307 | fi 308 | fi 309 | -------------------------------------------------------------------------------- /prepare_pmwatch_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a 8 | # copy of this software and associated documentation files (the "Software"), 9 | # to deal in the Software without restriction, including without limitation 10 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | # and/or sell copies of the Software, and to permit persons to whom the 12 | # Software is furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | # DEALINGS IN THE SOFTWARE. 24 | 25 | print_usage_and_exit() 26 | { 27 | echo "Usage: ./prepare_pmwatch_release.sh M= m= u= with-libipmctl= with-libipmctl-inc= os= [-r]" 28 | echo "Option:" 29 | echo " -r removes the package directory after tarball creation" 30 | echo 31 | 32 | exit $1 33 | } 34 | 35 | if [ $# = 0 ] ; then 36 | print_usage_and_exit 0 37 | fi 38 | 39 | OS=linux 40 | remove_package_dir= 41 | WITH_IPMCTL= 42 | WITH_IPMCTL_INC= 43 | while [ $# -gt 0 ] ; do 44 | case "$1" in 45 | --help | -h) 46 | print_usage_and_exit 0 47 | ;; 48 | M=*) 49 | MAJOR_VERSION=`echo $1 | sed s?^M=??g` 50 | if [[ ! ${MAJOR_VERSION} =~ ^-?[0-9]+$ ]] ; then 51 | echo "Invalid argument $1! Use integer" 52 | exit 53 | fi 54 | ;; 55 | m=*) 56 | MINOR_VERSION=`echo $1 | sed s?^m=??g` 57 | if [[ ! ${MINOR_VERSION} =~ ^-?[0-9]+$ ]] ; then 58 | echo "Invalid argument $1! Use integer" 59 | exit 60 | fi 61 | ;; 62 | u=*) 63 | UPDATE_NUM=`echo $1 | sed s?^u=??g` 64 | if [[ ! ${UPDATE_NUM} =~ ^-?[0-9]+$ ]] ; then 65 | echo "Invalid argument $1! Use integer" 66 | exit 67 | fi 68 | ;; 69 | with-libipmctl=*) 70 | WITH_IPMCTL=`echo $1 | sed s?^with-libipmctl=??g` 71 | ;; 72 | with-libipmctl-inc=*) 73 | WITH_IPMCTL_INC=`echo $1 | sed s?^with-libipmctl-inc=??g` 74 | ;; 75 | os=*) 76 | OS=`echo $1 | sed s?^os=??g` 77 | if [[ ${OS} != "esxi" && ${OS} != "linux" ]] ; then 78 | echo "" 79 | echo "Invalid OS!" 80 | print_usage_and_exit 81 | fi 82 | ;; 83 | -r) 84 | remove_package_dir=$1 85 | ;; 86 | *) 87 | echo "" 88 | echo "Invalid option: \"$1\"" 89 | print_usage_and_exit 1 90 | ;; 91 | esac 92 | shift 93 | done 94 | 95 | ./build_pmwatch.sh -all os=${OS} with-libipmctl=${WITH_IPMCTL} with-libipmctl-inc=${WITH_IPMCTL_INC} os=${OS} 96 | if [[ $? -ne 0 ]] ; then 97 | exit 1 98 | fi 99 | 100 | ./package_pmwatch.sh M=${MAJOR_VERSION} m=${MINOR_VERSION} u=${UPDATE_NUM} os=${OS} ${remove_package_dir} 101 | if [[ $? -ne 0 ]] ; then 102 | exit 1 103 | fi 104 | -------------------------------------------------------------------------------- /scripts/esxi/install-pmw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # MIT License 4 | # 5 | # Copyright (C) 2018 - 2019 Intel Corporation. All rights reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a 8 | # copy of this software and associated documentation files (the "Software"), 9 | # to deal in the Software without restriction, including without limitation 10 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | # and/or sell copies of the Software, and to permit persons to whom the 12 | # Software is furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | # DEALINGS IN THE SOFTWARE. 24 | 25 | if [[ $# != 1 ]] ; then 26 | echo "Usage: install pmwatch_.tar.bz2" 27 | exit 28 | fi 29 | 30 | if [ "${USER}x" != "rootx" ] ; then 31 | echo "Non-root user. Try again with root access!" 32 | exit 33 | fi 34 | 35 | TARBALL="$1" 36 | if [ ! -f ${TARBALL} ] ; then 37 | echo "Invalid tarball!" 38 | exit 39 | fi 40 | 41 | BIN_DIR="bin64" 42 | LIB_DIR="lib64" 43 | SYSTEM_LIB64="lib64" 44 | INSTALL_BASE="/opt/intel" 45 | INSTALL_BASE_DIR="${INSTALL_BASE}/pmwatch" 46 | PMWATCH_VER=${TARBALL:9:5} 47 | PMWATCH_BASE_NAME=${TARBALL:0:-8} 48 | INSTALL_DIR="${INSTALL_BASE}/${PMWATCH_BASE_NAME}" 49 | LIBIPMCTL="libipmctl.so.3.1.0" 50 | LIBIPMCTL_LESS="libipmctl.so.3" 51 | 52 | if [ ! -d "${INSTALL_BASE}" ] ; then 53 | mkdir ${INSTALL_BASE} 54 | if [ $? -ne 0 ] ; then 55 | echo "Unable to create ${INSTALL_BASE}. Exiting..." 56 | exit 57 | fi 58 | fi 59 | 60 | sh -c "tar xf ${TARBALL} -C ${INSTALL_BASE}" 61 | sh -c "chown root ${INSTALL_DIR}/${BIN_DIR}/pmwatch" 62 | sh -c "chgrp root ${INSTALL_DIR}/${BIN_DIR}/pmwatch" 63 | sh -c "chown root ${INSTALL_DIR}/${BIN_DIR}/pmwatch-stop" 64 | sh -c "chgrp root ${INSTALL_DIR}/${BIN_DIR}/pmwatch-stop" 65 | 66 | find_ipmctl=$( find /${SYSTEM_LIB64}/ -name ${LIBIPMCTL} ) 67 | if [ -z "${find_ipmctl}" ] ; then 68 | sh -c "mv ${INSTALL_DIR}/${LIB_DIR}/${LIBIPMCTL} /${SYSTEM_LIB64}/" 69 | sh -c "ln -s /${SYSTEM_LIB64}/${LIBIPMCTL} /${SYSTEM_LIB64}/${LIBIPMCTL_LESS}" 70 | else 71 | find_ipmctl=$( find /${SYSTEM_LIB64}/ -name ${LIBIPMCTL_LESS} ) 72 | if [ -z "${find_ipmctl}" ] ; then 73 | sh -c "ln -s /${SYSTEM_LIB64}/${LIBIPMCTL} /${SYSTEM_LIB64}/${LIBIPMCTL_LESS}" 74 | fi 75 | fi 76 | 77 | if [ -L ${INSTALL_BASE_DIR} ] ; then 78 | sh -c "rm -f ${INSTALL_BASE_DIR}" 79 | fi 80 | sh -c "ln -s ${INSTALL_DIR} ${INSTALL_BASE_DIR}" 81 | 82 | echo "pmwatch ${PMWATCH_VER} is installed in ${INSTALL_BASE_DIR}" 83 | echo "" 84 | echo "Follow the below steps to start using pmwatch:" 85 | echo " cd ${INSTALL_BASE_DIR}" 86 | echo " source ./pmw_vars.sh" 87 | echo "" 88 | echo "Only root user has the the permission to run pmwatch!" 89 | echo "Please configure ESXi in maintenance mode for pmwatch to work." 90 | echo "" 91 | -------------------------------------------------------------------------------- /scripts/esxi/pmw_vars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ============================================================================= 4 | # Filename: pmw_vars.sh 5 | # Version: 1.0 6 | # Purpose: PMWatch Runtime environment setup script 7 | # Description: This script should be used to set up the run-time environment 8 | # for PMWatch. It requires sh. Run this script before running 9 | # PMWatch executable. 10 | # 11 | # Usage: source pmw_vars.sh 12 | # 13 | # MIT License 14 | # 15 | # Copyright (C) 2018 - 2019 Intel Corporation. All rights reserved. 16 | # 17 | # Permission is hereby granted, free of charge, to any person obtaining a 18 | # copy of this software and associated documentation files (the "Software"), 19 | # to deal in the Software without restriction, including without limitation 20 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 21 | # and/or sell copies of the Software, and to permit persons to whom the 22 | # Software is furnished to do so, subject to the following conditions: 23 | # 24 | # The above copyright notice and this permission notice shall be included in 25 | # all copies or substantial portions of the Software. 26 | # 27 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 32 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 33 | # DEALINGS IN THE SOFTWARE. 34 | # 35 | # ============================================================================= 36 | 37 | echo "Please source this script under pmwatch install directory" 38 | 39 | GREP=grep 40 | 41 | # 42 | # Note: Busybox has a restricted shell environment, and 43 | # conventional system utilities may not be present; 44 | # so need to account for this ... 45 | # 46 | # busybox binary check 47 | BUSYBOX_SHELL=` ${GREP} --help 2>&1 | ${GREP} BusyBox` 48 | 49 | if [ -n "${BUSYBOX_SHELL}" ] ; then 50 | echo "Running on Busybox!" 51 | fi 52 | 53 | SCRIPT_DIR="$( cd "$( dirname "sh" )" && pwd )" 54 | 55 | export PMW_LOC_PATH="${SCRIPT_DIR}" 56 | export PMW_INSTALL_PATH="${PMW_LOC_PATH}/bin64" 57 | export LD_LIBRARY_PATH="${PMW_LOC_PATH}/lib64" 58 | export PATH="${PMW_INSTALL_PATH}:${PATH}" 59 | 60 | # show settings of various environment variables 61 | echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" 62 | echo "PATH=${PATH}" 63 | echo "" 64 | echo "pmwatch is currently installed under ${PMW_INSTALL_PATH}" 65 | echo "" 66 | echo "pmwatch works only when ESXi is in maintenance mode." 67 | echo "" 68 | -------------------------------------------------------------------------------- /scripts/install-pmw: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a 8 | # copy of this software and associated documentation files (the "Software"), 9 | # to deal in the Software without restriction, including without limitation 10 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | # and/or sell copies of the Software, and to permit persons to whom the 12 | # Software is furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | # DEALINGS IN THE SOFTWARE. 24 | 25 | PACKAGE_NAME_REGEX="pmwatch_[0-9\.]+[_a-zA-Z]*.tar.bz2" 26 | TARBALL=$( ls | grep -E ${PACKAGE_NAME_REGEX} ) 27 | if [ ! -f "${TARBALL}" ] ; then 28 | echo "" 29 | echo "Package tarball is not available at the install script location!" 30 | echo "Run the install script from the location of package tarball" 31 | echo "" 32 | 33 | exit 150 34 | fi 35 | 36 | BIN_DIR="bin64" 37 | LIB_DIR="lib64" 38 | INSTALL_BASE="/opt/intel" 39 | PMWATCH_VER=${TARBALL:8:5} 40 | PMWATCH_BASE_NAME=${TARBALL:0:-8} 41 | 42 | print_usage_and_exit() 43 | { 44 | err=${1:-0} 45 | echo "" 46 | echo "Usage: $0 [ options ]" 47 | echo "" 48 | echo " where \"options\" are the following:" 49 | echo "" 50 | echo " -c | --custom-install-loc" 51 | echo " installs the tool at a user-specified location" 52 | echo "" 53 | 54 | exit $err 55 | } 56 | 57 | while [ $# -gt 0 ] ; do 58 | case "$1" in 59 | -c | --custom-install-loc) 60 | custom_install_loc=1 61 | shift 62 | INSTALL_BASE="$1" 63 | ;; 64 | --install-only-binary) 65 | install_only_binary=1 66 | ;; 67 | *) 68 | echo "" 69 | echo "ERROR: unrecognized option \"$1\"" 70 | print_usage_and_exit 151 71 | ;; 72 | esac 73 | shift 74 | done 75 | 76 | if [ ! -d "${INSTALL_BASE}" ] ; then 77 | sudo mkdir ${INSTALL_BASE} 78 | if [ $? -ne 0 ] ; then 79 | echo "Unable to create directory ${INSTALL_BASE}. Exiting..." 80 | exit 152 81 | fi 82 | fi 83 | 84 | INSTALL_DIR="${INSTALL_BASE}/${PMWATCH_BASE_NAME}" 85 | INSTALL_DIR_SOFTLINK="${INSTALL_BASE}/pmwatch" 86 | 87 | sudo sh -c "tar --warning=no-timestamp -xf ${TARBALL} -C ${INSTALL_BASE}" 88 | sudo sh -c "chown root -R ${INSTALL_DIR}" 89 | if [ $? -ne 0 ] ; then 90 | echo 91 | echo "chown command failed!" 92 | echo 93 | sudo sh -c "rm -rf ${INSTALL_DIR}" 94 | exit 1 95 | fi 96 | sudo sh -c "chgrp root -R ${INSTALL_DIR}" 97 | if [ $? -ne 0 ] ; then 98 | echo 99 | echo "chgrp command failed!" 100 | echo 101 | sudo sh -c "rm -rf ${INSTALL_DIR}" 102 | exit 1 103 | fi 104 | 105 | if [ -L ${INSTALL_DIR_SOFTLINK} ] ; then 106 | sudo sh -c "unlink ${INSTALL_DIR_SOFTLINK}" 107 | fi 108 | sudo sh -c "ln -s ${INSTALL_DIR} ${INSTALL_DIR_SOFTLINK}" 109 | 110 | 111 | if [ -n "${install_only_binary}" ] ; then 112 | sudo sh -c "rm -rf ${INSTALL_DIR}/PMWatch_User_Guide.pdf ${INSTALL_DIR}/README ${INSTALL_DIR}/scripts > /dev/null 2>&1" 113 | fi 114 | 115 | # identify the OS 116 | os="default" 117 | cat /etc/*release | grep "SUSE" > /dev/null 2>&1 118 | if [ $? -eq 0 ] ; then 119 | os="suse" 120 | fi 121 | cat /etc/*release | grep "Ubuntu" > /dev/null 2>&1 122 | if [ $? -eq 0 ] ; then 123 | os="ubuntu" 124 | fi 125 | 126 | # check for missing dependencies 127 | tmp=`ldd ${INSTALL_DIR}/${BIN_DIR}/pmwatch | grep "not found"` 128 | if [ $? -eq 0 ] ; then 129 | echo "" 130 | dependent_libs=`ldd ${INSTALL_DIR}/${BIN_DIR}/pmwatch | grep "not found" | cut -d"=" -f1` 131 | echo "pmwatch has dependency on following libs: ${dependent_libs}" 132 | echo "Please install the relevant libraries and re-install pmwatch." 133 | echo "" 134 | echo "Depedency resolution tips:" 135 | echo " Install ndctl tool to resolve libndctl.so and libdaxctl.so dependencies." 136 | 137 | sudo sh -c "rm -rf ${INSTALL_DIR}" 138 | 139 | exit 153 140 | else 141 | echo "" 142 | echo "pmwatch ${PMWATCH_VER} is installed at ${INSTALL_DIR_SOFTLINK}." 143 | echo "Execute \"source ${INSTALL_DIR_SOFTLINK}/pmw_vars.sh\" to start using the tool." 144 | fi 145 | 146 | echo "" 147 | -------------------------------------------------------------------------------- /scripts/pmw_vars.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem =============================================================================================== 3 | 4 | rem Purpose: PMWatch Runtime environment setup script 5 | 6 | rem Description: This script should be used to set up the run-time environment for PMWatch. 7 | 8 | rem MIT License 9 | rem 10 | rem Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 11 | rem 12 | rem Permission is hereby granted, free of charge, to any person obtaining a 13 | rem copy of this software and associated documentation files (the "Software"), 14 | rem to deal in the Software without restriction, including without limitation 15 | rem the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | rem and/or sell copies of the Software, and to permit persons to whom the 17 | rem Software is furnished to do so, subject to the following conditions: 18 | rem 19 | rem The above copyright notice and this permission notice shall be included in 20 | rem all copies or substantial portions of the Software. 21 | rem 22 | rem THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | rem IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | rem FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | rem AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | rem LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | rem FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | rem DEALINGS IN THE SOFTWARE. 29 | 30 | rem =============================================================================================== 31 | 32 | 33 | set PMW_PKG_DIR=%~dp0% 34 | 35 | if "%PROCESSOR_ARCHITECTURE%"=="x86" ( 36 | set PMW_BIN_DIR=bin32 37 | ) 38 | if '%PROCESSOR_ARCHITECTURE%x'=='AMD64x' ( 39 | set PMW_BIN_DIR=bin64 40 | ) 41 | 42 | set PMW_LOC_PATH=%PMW_PKG_DIR% 43 | set PMW_INSTALL_PATH=%PMW_LOC_PATH%%PMW_BIN_DIR% 44 | set PATH=%PMW_INSTALL_PATH%;%PATH% 45 | 46 | rem echo all the variables 47 | rem echo PATH: %PATH% 48 | echo pmwatch is currently installed under %PMW_INSTALL_PATH% 49 | 50 | goto noerr 51 | 52 | :err 53 | echo Error occured during environment setup. 54 | goto :eof 55 | 56 | :noerr 57 | echo pmwatch run time environment successfully set. 58 | -------------------------------------------------------------------------------- /scripts/pmw_vars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ============================================================================= 4 | # Filename: pmw_vars.sh 5 | # Version: 1.0 6 | # Purpose: PMWatch Runtime environment setup script 7 | # Description: This script should be used to set up the run-time environment 8 | # for PMWatch. It requires sh. Run this script before running 9 | # PMWatch executable. 10 | # 11 | # Usage: source pmw_vars.sh 12 | # 13 | # MIT License 14 | # 15 | # Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 16 | # 17 | # Permission is hereby granted, free of charge, to any person obtaining a 18 | # copy of this software and associated documentation files (the "Software"), 19 | # to deal in the Software without restriction, including without limitation 20 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 21 | # and/or sell copies of the Software, and to permit persons to whom the 22 | # Software is furnished to do so, subject to the following conditions: 23 | # 24 | # The above copyright notice and this permission notice shall be included in 25 | # all copies or substantial portions of the Software. 26 | # 27 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 32 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 33 | # DEALINGS IN THE SOFTWARE. 34 | # 35 | # ============================================================================= 36 | 37 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 38 | 39 | export PMW_LOC_PATH="${SCRIPT_DIR}" 40 | export PMW_INSTALL_PATH="${PMW_LOC_PATH}/bin64" 41 | export PMW_SCRIPT_INSTALL_PATH="${PMW_LOC_PATH}/scripts" 42 | export PATH="${PMW_INSTALL_PATH}:${PMW_SCRIPT_INSTALL_PATH}:${PATH}" 43 | 44 | # show settings of various environment variables 45 | echo "PATH=${PATH}" 46 | echo "PMW_SCRIPT_INSTALL_PATH=${PMW_SCRIPT_INSTALL_PATH}" 47 | echo pmwatch is currently installed under ${PMW_INSTALL_PATH} 48 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.lo 3 | .dirstamp 4 | .deps 5 | .libs -------------------------------------------------------------------------------- /src/esxi/pmwatch-stop: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # MIT License 4 | # 5 | # Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a 8 | # copy of this software and associated documentation files (the "Software"), 9 | # to deal in the Software without restriction, including without limitation 10 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | # and/or sell copies of the Software, and to permit persons to whom the 12 | # Software is furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | # DEALINGS IN THE SOFTWARE. 24 | 25 | res=`kill -9 $(pidof -s pmwatch)` 26 | echo 27 | if [[ $? -eq 0 ]] ; then 28 | echo "pmwatch stopped successfully." 29 | else 30 | echo "Unable to stop pmwatch." 31 | fi 32 | echo 33 | -------------------------------------------------------------------------------- /src/inc/pmw_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _PMW_API_H_INC_ 27 | #define _PMW_API_H_INC_ 28 | 29 | #include "pmw_struct.h" 30 | 31 | #ifndef PMW_API 32 | #define PMW_API 33 | #endif 34 | 35 | /* ------------------------------------------------------------------------- */ 36 | /*! 37 | * @fn int PMWAPIGetDIMMCount ( 38 | * int *num_nvdimms 39 | * ) 40 | * 41 | * @brief function to obtain the number of DCPMM DIMMs 42 | * 43 | * @param OUT num_nvdimms: number of DCPMM DIMMs 44 | * 45 | * @return 0 if successful 46 | * 47 | * Special Notes: 48 | * 49 | */ 50 | extern PMW_API int 51 | PMWAPIGetDIMMCount ( 52 | int *num_nvdimms 53 | ); 54 | 55 | /* ------------------------------------------------------------------------- */ 56 | /*! 57 | * @fn int PMWAPIStart ( 58 | * PMWATCH_CONFIG_NODE pmwatch_config 59 | * ) 60 | * 61 | * @brief function to start the collection 62 | * 63 | * @param IN pmwatch_config: struct that provides collection configuration 64 | * 65 | * @return 0 if successful 66 | * 67 | * Special Notes: 68 | * 69 | */ 70 | extern PMW_API int 71 | PMWAPIStart ( 72 | PMWATCH_CONFIG_NODE pmwatch_config 73 | ); 74 | 75 | /* ------------------------------------------------------------------------- */ 76 | /*! 77 | * @fn int PMWAPIStop () 78 | * 79 | * @brief function to stop the collection 80 | * 81 | * @param None 82 | * 83 | * @return 0 if successful 84 | * 85 | * Special Notes: 86 | * 87 | */ 88 | extern PMW_API int 89 | PMWAPIStop (); 90 | 91 | /* ------------------------------------------------------------------------- */ 92 | /*! 93 | * @fn int PMWAPIRead ( 94 | * PMWATCH_OP_BUF* op_buf 95 | * ) 96 | * 97 | * @brief function to obtain the metrics count 98 | * 99 | * @param OUT op_buf: output buffer 100 | * 101 | * @return 0 if successful 102 | * 103 | * Special Notes: 104 | * 105 | */ 106 | extern PMW_API int 107 | PMWAPIRead ( 108 | PMWATCH_OP_BUF* op_buf 109 | ); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/inc/pmw_collect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2018 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _PMW_COLLECT_H_INC_ 27 | #define _PMW_COLLECT_H_INC_ 28 | 29 | #ifdef PMW_DLL 30 | #ifdef PMW_DLL_EXPORTS 31 | #define PMW_API PMW_HELPER_DLL_EXPORT 32 | #else 33 | #define PMW_API PMW_HELPER_DLL_IMPORT 34 | #endif // PMW_DLL_EXPORTS 35 | #else // PMW_DLL 36 | #define PMW_API 37 | #endif // NVM_DLL 38 | 39 | #include "pmw_struct.h" 40 | 41 | extern PMW_API NVM_UINT32 42 | PMW_COLLECT_Set_Global_Vars ( 43 | FILE *l_fout, 44 | int l_loops, 45 | int l_infinite_run, 46 | uint64_t l_usec_sample_time, 47 | uint64_t l_usec_sample_time_prev, 48 | int l_collect_lifetime, 49 | int l_output_format_one, 50 | int l_output_format_two, 51 | int l_collect_health, 52 | char *l_delim 53 | ); 54 | 55 | extern PMW_API int 56 | PMW_COLLECT_Print_Smart_Counters (); 57 | 58 | extern PMW_API int 59 | PMW_COLLECT_Print_Health_Info (); 60 | 61 | extern PMW_API void 62 | PMW_COLLECT_Print_Lifetime_Mem_Info_Counters (); 63 | 64 | #ifndef _WIN32 65 | 66 | extern PMW_API int 67 | PMW_COLLECT_Get_DIMM_Count ( 68 | int *num_nvmdimms 69 | ); 70 | 71 | extern PMW_API int 72 | PMW_COLLECT_Start_Collection ( 73 | PMWATCH_CONFIG_NODE pmwatch_config 74 | ); 75 | 76 | extern PMW_API int 77 | PMW_COLLECT_Stop_Collection (); 78 | 79 | extern PMW_API int 80 | PMW_COLLECT_Read_Data ( 81 | PMWATCH_OP_BUF* op_buf 82 | ); 83 | 84 | #endif 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/inc/pmw_comm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _PMW_COMM_H_INC_ 27 | #define _PMW_COMM_H_INC_ 28 | 29 | #ifdef _WIN32 30 | #define PMW_DLL 1 31 | #define PMW_HELPER_DLL_IMPORT __declspec(dllimport) 32 | #define PMW_HELPER_DLL_EXPORT __declspec(dllexport) 33 | #endif 34 | 35 | #ifdef PMW_DLL 36 | #ifdef PMW_DLL_EXPORTS 37 | #define PMW_API PMW_HELPER_DLL_EXPORT 38 | #else 39 | #define PMW_API PMW_HELPER_DLL_IMPORT 40 | #endif // PMW_DLL_EXPORTS 41 | #else // PMW_DLL 42 | #define PMW_API 43 | #endif // NVM_DLL 44 | 45 | #define _STRINGIFY(x) #x 46 | #define STRINGIFY(x) _STRINGIFY(x) 47 | 48 | // defining ipmctl API macros 49 | #ifdef _WIN32 50 | #define NVM_CREATE_CONTEXT p_nvm_create_context 51 | #define NVM_FREE_CONTEXT p_nvm_free_context 52 | #define NVM_GET_BUILD_NUMBER p_nvm_get_build_number 53 | #define NVM_GET_NUMBER_OF_DEVICES p_nvm_get_number_of_devices 54 | #define NVM_GET_DEVICES p_nvm_get_devices 55 | #define NVM_GET_NVM_CAPACITIES p_nvm_get_nvm_capacities 56 | #define NVM_SEND_DEVICE_PASSTHROUGH_CMD p_nvm_send_device_passthrough_cmd 57 | #define NVM_GET_MAJOR_VERSION p_nvm_get_major_version 58 | #define NVM_GET_MINOR_VERSION p_nvm_get_minor_version 59 | #define NVM_GET_HOTFIX_NUMBER p_nvm_get_hotfix_number 60 | #define NVM_GET_BUILD_NUMBER p_nvm_get_build_number 61 | #else 62 | #define NVM_CREATE_CONTEXT nvm_create_context 63 | #define NVM_FREE_CONTEXT nvm_free_context 64 | #define NVM_GET_BUILD_NUMBER nvm_get_build_number 65 | #define NVM_GET_NUMBER_OF_DEVICES nvm_get_number_of_devices 66 | #define NVM_GET_DEVICES nvm_get_devices 67 | #define NVM_GET_NVM_CAPACITIES nvm_get_nvm_capacities 68 | #define NVM_SEND_DEVICE_PASSTHROUGH_CMD nvm_send_device_passthrough_cmd 69 | #define NVM_GET_MAJOR_VERSION nvm_get_major_version 70 | #define NVM_GET_MINOR_VERSION nvm_get_minor_version 71 | #define NVM_GET_HOTFIX_NUMBER nvm_get_hotfix_number 72 | #define NVM_GET_BUILD_NUMBER nvm_get_build_number 73 | #endif 74 | 75 | #define PMWATCH_SUPPORT_IPMCTL_VERSION_MIN "01.00.00.3262" 76 | 77 | // memory info struct 78 | typedef struct MEMORY_INFO_PAGE_NODE_S MEMORY_INFO_PAGE_NODE; 79 | typedef MEMORY_INFO_PAGE_NODE *MEMORY_INFO_PAGE; 80 | 81 | #if defined(_WIN32) 82 | #pragma pack(push,1) 83 | #endif 84 | struct MEMORY_INFO_PAGE_NODE_S { // output 85 | unsigned char total_bytes_read[16]; // Number of 64 byte reads from the DIMM 86 | unsigned char total_bytes_written[16]; // Number of 64 byte writes from the DIMM 87 | unsigned char total_read_reqs[16]; // Number of DDRT read transactions the DIMM has serviced 88 | unsigned char total_write_reqs[16]; // Number of DDRT write transactions the DIMM has serviced 89 | unsigned char rsvd[64]; 90 | #if defined(_WIN32) 91 | }; 92 | #pragma pack(pop) 93 | #else 94 | } __attribute__((packed)); 95 | #endif 96 | 97 | typedef struct HEALTH_INFO_PAGE_NODE_S HEALTH_INFO_PAGE_NODE; 98 | typedef HEALTH_INFO_PAGE_NODE *HEALTH_INFO_PAGE; 99 | 100 | #if defined(_WIN32) 101 | #pragma pack(push,1) 102 | #endif 103 | struct HEALTH_INFO_PAGE_NODE_S { 104 | unsigned char validation_flags[4]; 105 | unsigned char rsvd1[4]; 106 | unsigned char health_status; 107 | unsigned char percentage_remaining; 108 | unsigned char percentage_used; 109 | unsigned char rsvd2; 110 | unsigned char media_temp[2]; 111 | unsigned char controller_temp[2]; 112 | unsigned char rsvd3[16]; 113 | unsigned char vendor_data_size[4]; 114 | unsigned char rsvd4[8]; 115 | unsigned char power_on_time[8]; 116 | unsigned char uptime[8]; 117 | unsigned char rsvd5[5]; 118 | unsigned char last_shutdown_time[8]; 119 | unsigned char rsvd6[9]; 120 | unsigned char max_media_temp[2]; 121 | unsigned char max_controller_temp[2]; 122 | unsigned char rsvd7[42]; 123 | #if defined(_WIN32) 124 | }; 125 | #pragma pack(pop) 126 | #else 127 | } __attribute__((packed)); 128 | #endif 129 | 130 | #if defined(_WIN32) 131 | #pragma pack(push, 1) 132 | #endif 133 | struct pt_payload_input_memory_info { //input 134 | unsigned char memory_page; // The page of memory information you want to retrieve 135 | unsigned char rsvd[127]; 136 | #if defined(_WIN32) 137 | }; 138 | #pragma pack(pop) 139 | #else 140 | } __attribute__((packed)); 141 | #endif 142 | 143 | /* 144 | * Strcuture to hold the SMART counter values 145 | */ 146 | typedef struct LIFETIME_INFO_COUNTER_NODE_S LIFETIME_INFO_COUNTER_NODE; 147 | typedef LIFETIME_INFO_COUNTER_NODE *LIFETIME_INFO_COUNTER; 148 | 149 | struct LIFETIME_INFO_COUNTER_NODE_S 150 | { 151 | uint64_t bytes_read; // Lifetime number of 64 bytes of data read from the DIMM. 152 | uint64_t bytes_written; // Lifetime number of 64 bytes of data written to the DIMM. 153 | uint64_t host_reads; // Lifetime number of DDRT read requests the DIMM has serviced. 154 | uint64_t host_writes; // Lifetime number of DDRT write requests the DIMM has serviced. 155 | }; 156 | 157 | typedef struct HEALTH_INFO_COUNTER_NODE_S HEALTH_INFO_COUNTER_NODE; 158 | typedef HEALTH_INFO_COUNTER_NODE *HEALTH_INFO_COUNTER; 159 | 160 | struct HEALTH_INFO_COUNTER_NODE_S 161 | { 162 | uint32_t validation_flags; 163 | unsigned char health_status; 164 | unsigned char percentage_remaining; 165 | unsigned char percentage_used; 166 | uint16_t media_temp; 167 | uint16_t controller_temp; 168 | uint32_t vendor_data_size; 169 | uint64_t power_on_time; 170 | uint64_t uptime; 171 | uint64_t last_shutdown_time; 172 | uint16_t max_media_temp; 173 | uint16_t max_controller_temp; 174 | }; 175 | 176 | 177 | extern PMW_API struct device_discovery* 178 | PMW_COMM_Get_DIMM_Topology (); 179 | 180 | extern PMW_API NVM_INT32 181 | PMW_COMM_Get_Memory_Info_Page ( 182 | NVM_INT32 dimm_index, 183 | void *counters, 184 | const unsigned char page_num 185 | ); 186 | 187 | extern PMW_API NVM_INT32 188 | PMW_COMM_Get_Health_Info_Page ( 189 | NVM_INT32 dimm_index, 190 | void *counters 191 | ); 192 | 193 | extern PMW_API void 194 | PMW_COMM_Print_Warning_Message (); 195 | 196 | extern PMW_API NVM_INT32 197 | PMW_COMM_Is_DIMM_Available (); 198 | 199 | extern PMW_API NVM_INT32 200 | PMW_COMM_Read_DIMM_Topology (); 201 | 202 | extern PMW_API NVM_INT32 203 | PMW_COMM_Print_Topology ( 204 | NVM_INT32 get_topo, 205 | NVM_INT32 detailed 206 | ); 207 | 208 | extern PMW_API NVM_INT32 209 | PMW_COMM_Get_Number_of_DIMM ( 210 | NVM_UINT32 *dimm_count 211 | ); 212 | 213 | extern PMW_API NVM_INT32 214 | PMW_COMM_Get_Count_by_Topology ( 215 | NVM_INT32 socket_num, 216 | NVM_INT32 mc_num, 217 | NVM_INT32 channel_num, 218 | NVM_INT32 dimm_num, 219 | LIFETIME_INFO_COUNTER smt_counters 220 | ); 221 | 222 | extern PMW_API int 223 | PMW_COMM_Check_User_Permission (); 224 | 225 | extern PMW_API NVM_INT32 226 | PMW_COMM_Init (); 227 | 228 | extern PMW_API NVM_INT32 229 | PMW_COMM_Cleanup (); 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /src/inc/pmw_struct.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _PMW_STRUCT_H_INC_ 27 | #define _PMW_STRUCT_H_INC_ 28 | 29 | #include 30 | #include 31 | 32 | typedef struct PMWATCH_CONFIG_NODE_S PMWATCH_CONFIG_NODE; 33 | typedef PMWATCH_CONFIG_NODE *PMWATCH_CONFIG; 34 | 35 | struct PMWATCH_CONFIG_NODE_S 36 | { 37 | double interval; 38 | double health_interval; 39 | bool collect_health; 40 | bool collect_perf_metrics; 41 | }; 42 | 43 | #define PMWATCH_CONFIG_INTERVAL(x) (x)->interval 44 | #define PMWATCH_CONFIG_HEALTH_INTERVAL(x) (x)->health_interval 45 | #define PMWATCH_CONFIG_COLLECT_HEALTH(x) (x)->collect_health 46 | #define PMWATCH_CONFIG_COLLECT_PERF_METRICS(x) (x)->collect_perf_metrics 47 | 48 | typedef struct PMWATCH_OP_BUF_NODE_S PMWATCH_OP_BUF_NODE; 49 | typedef PMWATCH_OP_BUF_NODE *PMWATCH_OP_BUF; 50 | 51 | struct PMWATCH_OP_BUF_NODE_S 52 | { 53 | uint64_t epoch; 54 | uint64_t timestamp; 55 | uint64_t total_bytes_read; 56 | uint64_t total_bytes_written; 57 | double read_hit_ratio; 58 | double write_hit_ratio; 59 | uint64_t media_read; 60 | uint64_t media_write; 61 | uint64_t bytes_read; 62 | uint64_t bytes_written; 63 | uint64_t host_reads; 64 | uint64_t host_writes; 65 | unsigned char health_status; 66 | unsigned char percentage_remaining; 67 | unsigned char percentage_used; 68 | uint64_t power_on_time; 69 | uint64_t uptime; 70 | uint64_t last_shutdown_time; 71 | double media_temp; 72 | double controller_temp; 73 | double max_media_temp; 74 | double max_controller_temp; 75 | }; 76 | 77 | #define PMWATCH_OP_BUF_EPOCH(x) (x)->epoch 78 | #define PMWATCH_OP_BUF_TIMESTAMP(x) (x)->timestamp 79 | #define PMWATCH_OP_BUF_TOTAL_BYTES_READ(x) (x)->total_bytes_read 80 | #define PMWATCH_OP_BUF_TOTAL_BYTES_WRITTEN(x) (x)->total_bytes_written 81 | #define PMWATCH_OP_BUF_READ_HIT_RATIO(x) (x)->read_hit_ratio 82 | #define PMWATCH_OP_BUF_WRITE_HIT_RATIO(x) (x)->write_hit_ratio 83 | #define PMWATCH_OP_BUF_MEDIA_READ(x) (x)->media_read 84 | #define PMWATCH_OP_BUF_MEDIA_WRITE(x) (x)->media_write 85 | #define PMWATCH_OP_BUF_BYTES_READ(x) (x)->bytes_read 86 | #define PMWATCH_OP_BUF_BYTES_WRITTEN(x) (x)->bytes_written 87 | #define PMWATCH_OP_BUF_HOST_READS(x) (x)->host_reads 88 | #define PMWATCH_OP_BUF_HOST_WRITES(x) (x)->host_writes 89 | #define PMWATCH_OP_BUF_HEALTH_STATUS(x) (x)->health_status 90 | #define PMWATCH_OP_BUF_PERCENTAGE_REMAINING(x) (x)->percentage_remaining 91 | #define PMWATCH_OP_BUF_PERCENTAGE_USED(x) (x)->percentage_used 92 | #define PMWATCH_OP_POWER_ON_TIME(x) (x)->power_on_time 93 | #define PMWATCH_OP_BUF_UPTIME(x) (x)->uptime 94 | #define PMWATCH_OP_BUF_LAST_SHUTDOWN_TIME(x) (x)->last_shutdown_time 95 | #define PMWATCH_OP_BUF_MEDIA_TEMP(x) (x)->media_temp 96 | #define PMWATCH_OP_BUF_CONTROLLER_TEMP(x) (x)->controller_temp 97 | #define PMWATCH_OP_BUF_MAX_MEDIA_TEMP(x) (x)->max_media_temp 98 | #define PMWATCH_OP_BUF_MAX_CONTROLLER_TEMP(x) (x)->max_controller_temp 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/inc/pmw_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2018 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _PMW_UTILS_H_INC_ 27 | #define _PMW_UTILS_H_INC_ 28 | 29 | #include "nvm_types.h" 30 | #include "pmw_comm.h" 31 | 32 | #define STRNCPY_SAFE PMW_UTILS_Strncpy_Safe 33 | #define STRNCAT_SAFE PMW_UTILS_Strncat_Safe 34 | 35 | #define CALCULATE_DIFFERENCE(cur,pre,diff,overflow_val) { \ 36 | if (cur < pre) { \ 37 | diff = (overflow_val - pre) + 1 + cur; \ 38 | } \ 39 | else { \ 40 | diff = cur - pre; \ 41 | } \ 42 | } 43 | 44 | #define PMWATCH_GET_BIT_VALUE(val, idx) (val >> idx) & 0x1 45 | 46 | #define MICRO_SEC 1000000 47 | #define MILL_SEC 1000 48 | #define SMALL_STR_LEN 128 49 | #define MEDIUM_STR_LEN 512 50 | #define LARGE_STR_LEN 1024 51 | #define CELCIUS_CONV_VAL 0.0625 52 | #define TEMP_SIGN_BIT_INDEX 15 53 | #define TEMP_VALUE_MASK 0x7FFF 54 | #define ROOT_UID 0 55 | 56 | #define HEALTH_INFO_VENDOR_SPECIFIC_DATA_SIZE 11 57 | #define HEALTH_STATUS_FATAL 2 58 | #define HEALTH_REASON_CRITICAL_INTERNAL_STATE 7 59 | 60 | #define NEGATIVE_POSITIVE_BIT_INDEX 15 61 | 62 | // error codes 63 | #define PMW_ERR_DLLLOAD 300 64 | #define PMW_ERR_DLLSYM 301 65 | 66 | /** 67 | * Convert an array of 8 unsigned chars into an unsigned 64 bit value 68 | * @remarks While it doesn't seem right to be casting 8 bit chars to unsigned long 69 | * long, this is an issue with gcc - see http:// gcc.gnu.org/bugzilla/show_bug.cgi?id=47821. 70 | */ 71 | #define PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(arr, val) \ 72 | val = ((unsigned long long)(arr[7] & 0xFF) << 56) + \ 73 | ((unsigned long long)(arr[6] & 0xFF) << 48) + \ 74 | ((unsigned long long)(arr[5] & 0xFF) << 40) + \ 75 | ((unsigned long long)(arr[4] & 0xFF) << 32) + \ 76 | ((unsigned long long)(arr[3] & 0xFF) << 24) + \ 77 | ((unsigned long long)(arr[2] & 0xFF) << 16) + \ 78 | ((unsigned long long)(arr[1] & 0xFF) << 8) + \ 79 | (unsigned long long)(arr[0] & 0xFF); 80 | 81 | #define PMW_4_BYTE_ARRAY_TO_32_BIT_VALUE(arr, val) \ 82 | val = ((unsigned long long)(arr[3] & 0xFF) << 24) + \ 83 | ((unsigned long long)(arr[2] & 0xFF) << 16) + \ 84 | ((unsigned long long)(arr[1] & 0xFF) << 8) + \ 85 | (unsigned long long)(arr[0] & 0xFF); 86 | 87 | #define PMW_3_BYTE_ARRAY_TO_32_BIT_VALUE(arr, val) \ 88 | val = ((unsigned long long)(arr[2] & 0xFF) << 16) + \ 89 | ((unsigned long long)(arr[1] & 0xFF) << 8) + \ 90 | (unsigned long long)(arr[0] & 0xFF); 91 | 92 | #define PMW_2_BYTE_ARRAY_TO_16_BIT_VALUE(arr, val) \ 93 | val = ((unsigned long long)(arr[1] & 0xFF) << 8) + \ 94 | (unsigned long long)(arr[0] & 0xFF); 95 | 96 | /** 97 | * Convert an unsigned 64 bit integer to an array of 8 unsigned chars 98 | */ 99 | #define PMW_64_BIT_VALUE_TO_8_BYTE_ARRAY(val, arr) \ 100 | arr[7] = (unsigned char)((val >> 56) & 0xFF); \ 101 | arr[6] = (unsigned char)((val >> 48) & 0xFF); \ 102 | arr[5] = (unsigned char)((val >> 40) & 0xFF); \ 103 | arr[4] = (unsigned char)((val >> 32) & 0xFF); \ 104 | arr[3] = (unsigned char)((val >> 24) & 0xFF); \ 105 | arr[2] = (unsigned char)((val >> 16) & 0xFF); \ 106 | arr[1] = (unsigned char)((val >> 8) & 0xFF); \ 107 | arr[0] = (unsigned char)(val & 0xFF); 108 | 109 | #if defined(_WIN32) 110 | 111 | #include 112 | #include 113 | 114 | PMW_API uint64_t 115 | PMW_UTILS_Read_TSC(); 116 | 117 | PMW_API HANDLE 118 | PMW_UTILS_DLL_Open (); 119 | 120 | PMW_API void* 121 | PMW_UTILS_DLL_Sym ( 122 | HANDLE handle, 123 | const char *symbol 124 | ); 125 | 126 | PMW_API NVM_UINT32 127 | PMW_UTILS_DLL_Close ( 128 | HANDLE handle 129 | ); 130 | 131 | #else 132 | 133 | extern PMW_API uint64_t 134 | PMW_UTILS_Read_TSC(); 135 | 136 | #endif 137 | 138 | PMW_API NVM_UINT32 139 | PMW_UTILS_Strncpy_Safe ( 140 | char *dst, 141 | size_t dst_size, 142 | const char *src 143 | ); 144 | 145 | PMW_API NVM_UINT32 146 | PMW_UTILS_Strncat_Safe( 147 | char *dst, 148 | size_t dst_size, 149 | const char *src 150 | ); 151 | 152 | PMW_API void 153 | PMW_UTILS_Cal_Diff ( 154 | const LIFETIME_INFO_COUNTER diff, 155 | const LIFETIME_INFO_COUNTER pre, 156 | const LIFETIME_INFO_COUNTER cur 157 | ); 158 | 159 | PMW_API uint64_t 160 | PMW_UTILS_Get_Curr_Time_For_Sleep (); 161 | 162 | PMW_API uint64_t 163 | PMW_UTILS_Get_Curr_Time_In_Sec (); 164 | 165 | PMW_API void 166 | PMW_UTILS_Print_Key (const char* format, ... ); 167 | 168 | PMW_API void 169 | PMW_UTILS_Print_Value (const char* format, ... ); 170 | 171 | void 172 | PMW_UTILS_Sleep (uint64_t sleep_time); 173 | 174 | #endif -------------------------------------------------------------------------------- /src/inc/pmw_version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _PMW_VERSION_H_INC_ 27 | #define _PMW_VERSION_H_INC_ 28 | 29 | #define _STRINGIFY(x) #x 30 | #define STRINGIFY(x) _STRINGIFY(x) 31 | 32 | #define PRODUCT_NAME "Intel(R) PMWatch" 33 | 34 | #define MAJOR_VERSION 3 35 | #define MINOR_VERSION 2 36 | #define UPDATE_VERSION 1 37 | #if UPDATE_VERSION > 0 38 | #define UPDATE_STRING " Update "STRINGIFY(UPDATE_VERSION) 39 | #else 40 | #define UPDATE_STRING "" 41 | #endif 42 | 43 | #define ENG_BUILD "" 44 | 45 | #define OUTPUT_FORMAT_MAJOR_VERSION 1 46 | #define OUTPUT_FORMAT_MINOR_VERSION 0 47 | 48 | #define PRODUCT_TYPE "public" 49 | 50 | #define PRODUCT_TYPE_STR " ("PRODUCT_TYPE")" 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/pmw_api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "nvm_types.h" 34 | #include "pmw_collect.h" 35 | 36 | #ifndef _WIN32 37 | 38 | /* ------------------------------------------------------------------------- */ 39 | /*! 40 | * @fn int PMWAPIGetDIMMCount ( 41 | * int *num_nvdimms 42 | * ) 43 | * 44 | * @brief function to obtain the number of DCPMM DIMMs 45 | * 46 | * @param OUT num_nvdimms: number of DCPMM DIMMs 47 | * 48 | * @return 0 if successful 49 | * 50 | * Special Notes: 51 | * 52 | */ 53 | int 54 | PMWAPIGetDIMMCount ( 55 | int *num_nvdimms 56 | ) 57 | { 58 | return PMW_COLLECT_Get_DIMM_Count(num_nvdimms); 59 | } 60 | 61 | /* ------------------------------------------------------------------------- */ 62 | /*! 63 | * @fn int PMWAPIStart ( 64 | * PMWATCH_CONFIG_NODE pmwatch_config 65 | * ) 66 | * 67 | * @brief function to start the collection 68 | * 69 | * @param IN pmwatch_config: struct that provides collection configuration 70 | * 71 | * @return 0 if successful 72 | * 73 | * Special Notes: 74 | * 75 | */ 76 | int 77 | PMWAPIStart ( 78 | PMWATCH_CONFIG_NODE pmwatch_config 79 | ) 80 | { 81 | return PMW_COLLECT_Start_Collection(pmwatch_config); 82 | } 83 | 84 | /* ------------------------------------------------------------------------- */ 85 | /*! 86 | * @fn int PMWAPIStop () 87 | * 88 | * @brief function to stop the collection 89 | * 90 | * @param None 91 | * 92 | * @return 0 if successful 93 | * 94 | * Special Notes: 95 | * 96 | */ 97 | int 98 | PMWAPIStop () 99 | { 100 | return PMW_COLLECT_Stop_Collection(); 101 | } 102 | 103 | /* ------------------------------------------------------------------------- */ 104 | /*! 105 | * @fn int PMWAPIRead ( 106 | * PMWATCH_OP_BUF* op_buf 107 | * ) 108 | * 109 | * @brief function to obtain the metrics count 110 | * 111 | * @param OUT op_buf: output buffer 112 | * 113 | * @return 0 if successful 114 | * 115 | * Special Notes: 116 | * 117 | */ 118 | int 119 | PMWAPIRead ( 120 | PMWATCH_OP_BUF* op_buf 121 | ) 122 | { 123 | return PMW_COLLECT_Read_Data(op_buf); 124 | } 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/pmw_collect.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef _WIN32 34 | #define PMW_DLL_EXPORTS 1 35 | #else 36 | #include 37 | #include 38 | #endif 39 | 40 | #include "nvm_types.h" 41 | #include "nvm_management.h" 42 | 43 | #include "inc/pmw_utils.h" 44 | #include "inc/pmw_version.h" 45 | #include "inc/pmw_comm.h" 46 | #include "inc/pmw_collect.h" 47 | 48 | FILE *fout; 49 | int loops = 0, infinite_run = 0; 50 | uint64_t usec_sample_time, usec_sample_time_prev; 51 | int collect_lifetime = 0; 52 | char DELIMITER[] = ";"; 53 | int output_format_one = 1; 54 | int output_format_two = 0; 55 | int collect_health = 0; 56 | char *filename; 57 | #ifndef _WIN32 58 | pthread_t thread_id; 59 | int using_api = 0; 60 | static int write_count = 0; 61 | static int collection_done = 0; 62 | static int thread_status = 0; 63 | PMWATCH_OP_BUF output_buf = NULL; 64 | pthread_mutex_t buffer_lock; 65 | #endif 66 | 67 | NVM_UINT32 68 | PMW_COLLECT_Set_Global_Vars ( 69 | FILE *l_fout, 70 | int l_loops, 71 | int l_infinite_run, 72 | uint64_t l_usec_sample_time, 73 | uint64_t l_usec_sample_time_prev, 74 | int l_collect_lifetime, 75 | int l_output_format_one, 76 | int l_output_format_two, 77 | int l_collect_health, 78 | char *l_delim 79 | ) 80 | { 81 | NVM_UINT32 ret_val = NVM_SUCCESS; 82 | 83 | fout = l_fout; 84 | loops = l_loops; 85 | infinite_run = l_infinite_run; 86 | usec_sample_time = l_usec_sample_time; 87 | usec_sample_time_prev = l_usec_sample_time_prev; 88 | collect_lifetime = l_collect_lifetime; 89 | output_format_one = l_output_format_one; 90 | output_format_two = l_output_format_two; 91 | collect_health = l_collect_health; 92 | 93 | ret_val = STRNCPY_SAFE(DELIMITER, sizeof(DELIMITER), l_delim); 94 | 95 | return ret_val; 96 | } 97 | 98 | void print_header_format_one ( 99 | int count 100 | ) 101 | { 102 | int i; 103 | 104 | // 1st header 105 | fprintf(fout, "timestamp%s%s", DELIMITER, DELIMITER); 106 | for (i = 0; i < count; i++){ 107 | fprintf(fout, "DIMM%d%s%s%s%s%s%s%s%s%s%s", i, DELIMITER, DELIMITER, \ 108 | DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER); 109 | } 110 | fprintf(fout, "\n"); 111 | fflush(fout); 112 | 113 | // 2nd header 114 | fprintf(fout, "epoch%stimestamp%s", DELIMITER, DELIMITER); 115 | for (i = 0; i < count; i++){ 116 | fprintf(fout, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 117 | "bytes_read (derived)", 118 | DELIMITER, 119 | "bytes_written (derived)", 120 | DELIMITER, 121 | "read_hit_ratio (derived)", 122 | DELIMITER, 123 | "write_hit_ratio (derived)", 124 | DELIMITER, 125 | "media_read_ops (derived)", 126 | DELIMITER, 127 | "media_write_ops (derived)", 128 | DELIMITER, 129 | "read_64B_ops_received", 130 | DELIMITER, 131 | "write_64B_ops_received", 132 | DELIMITER, 133 | "cpu_read_ops", 134 | DELIMITER, 135 | "cpu_write_ops", 136 | DELIMITER 137 | ); 138 | } 139 | fprintf(fout, "\n"); 140 | fflush(fout); 141 | } 142 | 143 | void print_header_format_two ( 144 | int count 145 | ) 146 | { 147 | struct device_discovery *lp_DIMM_devices; 148 | int num_packages, itr_p, itr_d; 149 | 150 | lp_DIMM_devices = PMW_COMM_Get_DIMM_Topology(); 151 | 152 | // get the total number of packages 153 | num_packages = lp_DIMM_devices[count - 1].socket_id; 154 | ++num_packages; 155 | 156 | // header level 1 157 | fprintf(fout, "timestamp%s%s", DELIMITER, DELIMITER); 158 | for (itr_p = 0; itr_p < num_packages; itr_p++) { 159 | for (itr_d = 0; itr_d < count; itr_d++) { 160 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 161 | fprintf(fout, "Package%d%sPackage%d%sPackage%d%sPackage%d%s", itr_p, DELIMITER, \ 162 | itr_p, DELIMITER, itr_p, DELIMITER, itr_p, DELIMITER); 163 | } 164 | } 165 | for (itr_d = 0; itr_d < count; itr_d++) { 166 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 167 | fprintf(fout, "Package%d%sPackage%d%s", itr_p, DELIMITER, \ 168 | itr_p, DELIMITER); 169 | } 170 | 171 | } 172 | for (itr_d = 0; itr_d < count; itr_d++) { 173 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 174 | fprintf(fout, "Package%d%sPackage%d%s", itr_p, DELIMITER, \ 175 | itr_p, DELIMITER); 176 | } 177 | } 178 | for (itr_d = 0; itr_d < count; itr_d++) { 179 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 180 | fprintf(fout, "Package%d%sPackage%d%s", itr_p, DELIMITER, itr_p, DELIMITER); 181 | } 182 | } 183 | } 184 | fprintf(fout, "\n"); 185 | fflush(fout); 186 | 187 | // header level 2 188 | fprintf(fout, "%s%s", DELIMITER, DELIMITER); 189 | for (itr_p = 0; itr_p < num_packages; itr_p++) { 190 | for (itr_d = 0; itr_d < count; itr_d++) { 191 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 192 | fprintf(fout, "Media%sMedia%sMedia%sMedia%s", DELIMITER, \ 193 | DELIMITER, DELIMITER, DELIMITER); 194 | } 195 | } 196 | for (itr_d = 0; itr_d < count; itr_d++) { 197 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 198 | fprintf(fout, "Buffer%sBuffer%s", DELIMITER, \ 199 | DELIMITER); 200 | } 201 | } 202 | for (itr_d = 0; itr_d < count; itr_d++) { 203 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 204 | fprintf(fout, "Controller%sController%s", DELIMITER, DELIMITER); 205 | } 206 | } 207 | for (itr_d = 0; itr_d < count; itr_d++) { 208 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 209 | fprintf(fout, "CPU%sCPU%s", DELIMITER, DELIMITER); 210 | } 211 | } 212 | } 213 | fprintf(fout, "\n"); 214 | fflush(fout); 215 | 216 | //header level 3 217 | fprintf(fout, "epoch%stimestamp%s", DELIMITER, DELIMITER); 218 | for (itr_p = 0; itr_p < num_packages; itr_p++) { 219 | for (itr_d = 0; itr_d < count; itr_d++) { 220 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 221 | fprintf(fout, "DIMM%d%sDIMM%d%sDIMM%d%sDIMM%d%s", itr_d, DELIMITER, itr_d, DELIMITER, \ 222 | itr_d, DELIMITER, itr_d, DELIMITER); 223 | } 224 | } 225 | for (itr_d = 0; itr_d < count; itr_d++) { 226 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 227 | fprintf(fout, "DIMM%d%sDIMM%d%s", itr_d, DELIMITER, itr_d, DELIMITER); 228 | } 229 | } 230 | for (itr_d = 0; itr_d < count; itr_d++) { 231 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 232 | fprintf(fout, "DIMM%d%sDIMM%d%s", itr_d, DELIMITER, itr_d, DELIMITER); 233 | } 234 | } 235 | for (itr_d = 0; itr_d < count; itr_d++) { 236 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 237 | fprintf(fout, "DIMM%d%sDIMM%d%s", itr_d, DELIMITER, itr_d, DELIMITER); 238 | } 239 | } 240 | } 241 | fprintf(fout, "\n"); 242 | fflush(fout); 243 | 244 | //header level 4 245 | fprintf(fout, "%s%s", DELIMITER, DELIMITER); 246 | for (itr_p = 0; itr_p < num_packages; itr_p++) { 247 | for (itr_d = 0; itr_d < count; itr_d++) { 248 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 249 | fprintf(fout, "bytes_read (derived)%sbytes_written (derived)%smedia_read_ops (derived)%smedia_write_ops (derived)%s", \ 250 | DELIMITER, DELIMITER, DELIMITER, DELIMITER); 251 | } 252 | } 253 | for (itr_d = 0; itr_d < count; itr_d++) { 254 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 255 | fprintf(fout, "read_hit_ratio (derived)%swrite_hit_ratio (derived)%s", DELIMITER, DELIMITER); 256 | } 257 | } 258 | for (itr_d = 0; itr_d < count; itr_d++) { 259 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 260 | fprintf(fout, "read_64B_ops_received%swrite_64B_ops_received%s", \ 261 | DELIMITER, DELIMITER); 262 | } 263 | } 264 | for (itr_d = 0; itr_d < count; itr_d++) { 265 | if (itr_p == lp_DIMM_devices[itr_d].socket_id) { 266 | fprintf(fout, "cpu_read_ops%scpu_write_ops%s", DELIMITER, DELIMITER); 267 | } 268 | } 269 | } 270 | fprintf(fout, "\n"); 271 | fflush(fout); 272 | } 273 | 274 | void print_header_health_info ( 275 | int count 276 | ) 277 | { 278 | int i; 279 | 280 | // 1st header 281 | fprintf(fout, "timestamp%s%s", DELIMITER, DELIMITER); 282 | for (i = 0; i < count; i++){ 283 | fprintf(fout, "DIMM%d%s%s%s%s%s%s%s%s%s%s", i, DELIMITER, DELIMITER, \ 284 | DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER); 285 | } 286 | fprintf(fout, "\n"); 287 | fflush(fout); 288 | 289 | // 2nd header 290 | fprintf(fout, "epoch%stimestamp%s", DELIMITER, DELIMITER); 291 | for (i = 0; i < count; i++){ 292 | fprintf(fout, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 293 | "health_status", 294 | DELIMITER, 295 | "lifespan_used", 296 | DELIMITER, 297 | "lifespan_remaining", 298 | DELIMITER, 299 | "power_on_time", 300 | DELIMITER, 301 | "uptime", 302 | DELIMITER, 303 | "last_shutdown_time", 304 | DELIMITER, 305 | "media_temp", 306 | DELIMITER, 307 | "controller_temp", 308 | DELIMITER, 309 | "max_media_temp", 310 | DELIMITER, 311 | "max_controller_temp", 312 | DELIMITER 313 | ); 314 | } 315 | fprintf(fout, "\n"); 316 | fflush(fout); 317 | } 318 | 319 | void print_info_header () 320 | { 321 | time_t t; 322 | struct tm *local_time; 323 | 324 | // print version and collection time 325 | fprintf(fout, "# Collector: %s v%d.%d.%d\n", PRODUCT_NAME, MAJOR_VERSION, MINOR_VERSION, UPDATE_VERSION); 326 | fprintf(fout, "# Output Format: v%d.%d\n", OUTPUT_FORMAT_MAJOR_VERSION, OUTPUT_FORMAT_MINOR_VERSION); 327 | t = time(NULL); 328 | local_time = localtime(&t); 329 | if (local_time != NULL) { 330 | fprintf(fout, "# Collected on %s", asctime(local_time)); 331 | } 332 | if (output_format_one) { 333 | fprintf(fout, "# Default Format\n"); 334 | } 335 | else if (output_format_two) { 336 | fprintf(fout, "# Metric Grouping Format\n"); 337 | } 338 | 339 | // configuration to calculate avergae value in csv viewer 340 | if (collect_health) { 341 | fprintf(fout, "%s: %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", \ 342 | "# Avg column", \ 343 | "health_status", \ 344 | "lifespan_used", \ 345 | "lifespan_remaining", \ 346 | "power_on_time", \ 347 | "uptime", \ 348 | "last_shutdown_time", \ 349 | "media_temp", \ 350 | "controller_temp", \ 351 | "max_media_temp", \ 352 | "max_controller_temp" \ 353 | ); 354 | } 355 | else { 356 | fprintf(fout, "# Avg column: read_hit_ratio (derived), write_hit_ratio (derived)"); 357 | } 358 | fprintf(fout, "\n"); 359 | } 360 | 361 | int adjust_for_overhead ( 362 | int i, 363 | uint64_t elapsed_time, 364 | int *num_skips 365 | ) 366 | { 367 | int itr = 0; 368 | int sample_time_sec_prev, sample_time_sec, elapsed_time_sec, temp_operand, loops_left; 369 | 370 | #ifdef _WIN32 371 | temp_operand = (int) MILL_SEC; 372 | #else 373 | temp_operand = (int) MICRO_SEC; 374 | #endif 375 | 376 | while (elapsed_time > usec_sample_time) { 377 | usec_sample_time = usec_sample_time * 2; 378 | ++itr; 379 | 380 | elapsed_time_sec = (float) elapsed_time / temp_operand; 381 | sample_time_sec_prev = (float) usec_sample_time_prev / temp_operand; 382 | 383 | if (i + itr > loops && !infinite_run) { 384 | fprintf(stderr, "\nERROR: The collection overhead is more than the remaining time for the run.\n"); 385 | fprintf(stderr, " This system has substantial overhead during collection. Use a minimum sampling time of %d sec. Exiting...\n", \ 386 | elapsed_time_sec + 1); 387 | return 1; 388 | } 389 | } 390 | 391 | sample_time_sec = (int) usec_sample_time / temp_operand; 392 | 393 | if (usec_sample_time != usec_sample_time_prev) { 394 | // calculate skipped loops and loops left 395 | if (!infinite_run) { 396 | // loops left with new sampling time: (number of loops left times previous sampling time) / new sampling time 397 | loops_left = ((loops - i) * sample_time_sec_prev) / sample_time_sec; 398 | // [ number of loops left] - loops_left gives the num_skips 399 | *num_skips += (loops - i) - loops_left; 400 | // total number of loops 401 | loops = i + loops_left; 402 | } 403 | if (!infinite_run && loops_left == 0) { 404 | fprintf(stderr, "\nERROR: The collection overhead is more than the remaining time for the run.\n"); 405 | fprintf(stderr, " This system has substantial overhead during collection. Use a minimum sampling time of %d sec. Exiting...\n", \ 406 | elapsed_time_sec + 1); 407 | } 408 | else { 409 | fprintf(stderr, "\nWARNING: The collection overhead (%d sec) is more than the sampling time.\n", elapsed_time_sec); 410 | fprintf(stderr, " Updating the collection time from %d sec to %d sec\n", sample_time_sec_prev, sample_time_sec); 411 | } 412 | usec_sample_time_prev = usec_sample_time; 413 | } 414 | 415 | return 0; 416 | } 417 | 418 | int 419 | PMW_COLLECT_Print_Smart_Counters () 420 | { 421 | int i, j, itr_p, itr_m, itr_d, num_packages, num_skips = 0, ret_code = 0; 422 | unsigned int count; 423 | unsigned char page_num; 424 | uint64_t pre_tsc, cur_tsc, delta_tsc, start_time, cur_time, elapsed_time, sleep_time, epoch; 425 | uint64_t total_bytes_read, total_bytes_written, media_read, media_write, media_write_adjusted; 426 | double read_hit_ratio, write_hit_ratio; 427 | struct device_discovery *lp_DIMM_devices; 428 | 429 | uint64_t overflow_val = 0xFFFFFFFFFFFFFFFFULL; 430 | 431 | lp_DIMM_devices = PMW_COMM_Get_DIMM_Topology(); 432 | 433 | i = 0; 434 | j = 0; 435 | pre_tsc = 0ULL; 436 | cur_tsc = 0ULL; 437 | delta_tsc = 0ULL; 438 | page_num = 1; 439 | PMW_COMM_Get_Number_of_DIMM(&count); 440 | 441 | #ifndef _WIN32 442 | if (using_api) { 443 | output_buf = (PMWATCH_OP_BUF) calloc (count, sizeof(PMWATCH_OP_BUF_NODE)); 444 | if (output_buf == NULL) { 445 | fprintf(stderr, "ERROR: memory allocation failed!\n"); 446 | 447 | return 1; 448 | } 449 | } 450 | #endif 451 | 452 | // get the total number of packages 453 | num_packages = lp_DIMM_devices[count - 1].socket_id; 454 | ++num_packages; 455 | 456 | LIFETIME_INFO_COUNTER sample1 = (LIFETIME_INFO_COUNTER)calloc(count, sizeof(LIFETIME_INFO_COUNTER_NODE)); 457 | LIFETIME_INFO_COUNTER sample2 = (LIFETIME_INFO_COUNTER)calloc(count, sizeof(LIFETIME_INFO_COUNTER_NODE)); 458 | LIFETIME_INFO_COUNTER diff = (LIFETIME_INFO_COUNTER)calloc(count, sizeof(LIFETIME_INFO_COUNTER_NODE)); 459 | if (!sample1 || !sample2 || !diff) { 460 | fprintf(stderr, "ERROR: memory allocation failed!\n"); 461 | free(sample1); 462 | free(sample2); 463 | free(diff); 464 | #ifndef _WIN32 465 | if (using_api) { 466 | free(output_buf); 467 | } 468 | #endif 469 | 470 | return 1; 471 | } 472 | LIFETIME_INFO_COUNTER pre_sample[count]; 473 | LIFETIME_INFO_COUNTER cur_sample[count]; 474 | 475 | print_info_header(); 476 | 477 | // print header 478 | if (output_format_one) { 479 | print_header_format_one(count); 480 | } 481 | else { 482 | print_header_format_two(count); 483 | } 484 | 485 | for (j = 0; j < count; j++){ 486 | pre_sample[j] = &sample1[j]; 487 | cur_sample[j] = &sample2[j]; 488 | } 489 | for (i = 0; i <= loops || infinite_run; i++){ 490 | if (i == 0) { 491 | start_time = PMW_UTILS_Get_Curr_Time_For_Sleep(); 492 | 493 | pre_tsc = PMW_UTILS_Read_TSC(); 494 | 495 | // get count from all SMART counters 496 | for (j = 0; j < count; j++){ 497 | ret_code = PMW_COMM_Get_Memory_Info_Page(j, pre_sample[j], page_num); 498 | if ( ret_code != NVM_SUCCESS) { 499 | #ifndef _WIN32 500 | if (!using_api) { 501 | #endif 502 | fprintf(stderr, "\nSomething went wrong while collecting metrics.\n"); 503 | #ifndef _WIN32 504 | } 505 | #endif 506 | goto cleanup; 507 | } 508 | } 509 | // sleep for sample time 510 | cur_time = PMW_UTILS_Get_Curr_Time_For_Sleep(); 511 | elapsed_time = cur_time - start_time; 512 | 513 | ret_code = adjust_for_overhead(i, elapsed_time, &num_skips); 514 | if (ret_code != 0) { 515 | goto cleanup; 516 | } 517 | 518 | sleep_time = usec_sample_time - elapsed_time; 519 | 520 | PMW_UTILS_Sleep(sleep_time); 521 | 522 | continue; 523 | } 524 | 525 | cur_tsc = PMW_UTILS_Read_TSC(); 526 | 527 | CALCULATE_DIFFERENCE(cur_tsc, pre_tsc, delta_tsc, overflow_val); 528 | 529 | epoch = PMW_UTILS_Get_Curr_Time_In_Sec(); 530 | fprintf(fout,"%" PRIu64 "%s%" PRIu64 "%s", epoch, DELIMITER, delta_tsc, DELIMITER); 531 | 532 | start_time = PMW_UTILS_Get_Curr_Time_For_Sleep(); 533 | 534 | #ifndef _WIN32 535 | if (using_api) { 536 | pthread_mutex_lock(&buffer_lock); 537 | } 538 | #endif 539 | 540 | for (j = 0; j < count; j++){ 541 | #ifndef _WIN32 542 | if (using_api) { 543 | output_buf[j].timestamp = delta_tsc; 544 | output_buf[j].epoch = epoch; 545 | } 546 | #endif 547 | 548 | // get count from all SMART counters after sleep 549 | ret_code = PMW_COMM_Get_Memory_Info_Page(j, cur_sample[j], page_num); 550 | if ( ret_code != NVM_SUCCESS) { 551 | #ifndef _WIN32 552 | if (using_api) { 553 | pthread_mutex_unlock(&buffer_lock); 554 | } 555 | else { 556 | #endif 557 | fprintf(stderr, "\nSomething went wrong while collecting metrics.\n"); 558 | #ifndef _WIN32 559 | } 560 | #endif 561 | goto cleanup; 562 | } 563 | 564 | // get the difference of before and after sleep count 565 | PMW_UTILS_Cal_Diff(&diff[j], pre_sample[j], cur_sample[j]); 566 | 567 | if (output_format_one) { 568 | // calculate derived output metrics 569 | 570 | // bytes_read less than bytes_written results in negative value for total_bytes_read/media_read 571 | // providing "0" value to avoid displaying bogus negative value in this scenario 572 | if (diff[j].bytes_read > diff[j].bytes_written) { 573 | total_bytes_read = (diff[j].bytes_read - diff[j].bytes_written) * 64; 574 | media_read = (diff[j].bytes_read - diff[j].bytes_written) / 4; 575 | } 576 | else { 577 | total_bytes_read = 0ULL; 578 | media_read = 0ULL; 579 | } 580 | total_bytes_written = diff[j].bytes_written * 64; 581 | media_write = diff[j].bytes_written / 4; 582 | 583 | // calculating read and write hit ratio 584 | if (diff[j].host_reads > media_read) { 585 | double opr_01 = (double) diff[j].host_reads - media_read; 586 | read_hit_ratio = (double) opr_01 / diff[j].host_reads; 587 | } 588 | else { 589 | read_hit_ratio = 0; 590 | } 591 | if (diff[j].host_writes > media_write) { 592 | double opr_02 = (double) diff[j].host_writes - media_write; 593 | write_hit_ratio = (double) opr_02 / diff[j].host_writes; 594 | } 595 | else { 596 | write_hit_ratio = 0; 597 | } 598 | 599 | // print counts 600 | fprintf(fout, "%" PRIu64 "%s%" PRIu64 "%s%.2lf%s%.2lf%s%" PRIu64 "%s%" PRIu64 "%s%" \ 601 | PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s", 602 | total_bytes_read, 603 | DELIMITER, 604 | total_bytes_written, 605 | DELIMITER, 606 | read_hit_ratio, 607 | DELIMITER, 608 | write_hit_ratio, 609 | DELIMITER, 610 | media_read, 611 | DELIMITER, 612 | media_write, 613 | DELIMITER, 614 | diff[j].bytes_read, 615 | DELIMITER, 616 | diff[j].bytes_written, 617 | DELIMITER, 618 | diff[j].host_reads, 619 | DELIMITER, 620 | diff[j].host_writes, 621 | DELIMITER 622 | ); 623 | 624 | #ifndef _WIN32 625 | if (using_api) { 626 | output_buf[j].total_bytes_read = total_bytes_read; 627 | output_buf[j].total_bytes_written = total_bytes_written; 628 | output_buf[j].read_hit_ratio = read_hit_ratio; 629 | output_buf[j].write_hit_ratio = write_hit_ratio; 630 | output_buf[j].media_read = media_read; 631 | output_buf[j].media_write = media_write; 632 | output_buf[j].bytes_read = diff[j].bytes_read; 633 | output_buf[j].bytes_written = diff[j].bytes_written; 634 | output_buf[j].host_reads = diff[j].host_reads; 635 | output_buf[j].host_writes = diff[j].host_writes; 636 | } 637 | #endif 638 | 639 | #ifdef BUILD_DEBUG 640 | #ifndef _WIN32 641 | fprintf(fout, "%" PRIu64 "%s%.2lf%s%.2lf%s%.2lf%s%" PRIu64 "%s%" PRIu64 "%s%" \ 642 | PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s", 643 | output_buf[j].total_bytes_read, 644 | DELIMITER, 645 | output_buf[j].total_bytes_written, 646 | DELIMITER, 647 | output_buf[j].read_hit_ratio, 648 | DELIMITER, 649 | output_buf[j].write_hit_ratio, 650 | DELIMITER, 651 | output_buf[j].media_read, 652 | DELIMITER, 653 | output_buf[j].media_write, 654 | DELIMITER, 655 | output_buf[j].bytes_read, 656 | DELIMITER, 657 | output_buf[j].bytes_written, 658 | DELIMITER, 659 | output_buf[j].host_reads, 660 | DELIMITER, 661 | output_buf[j].host_writes, 662 | DELIMITER 663 | ); 664 | #endif 665 | #endif 666 | } 667 | 668 | //switch pointers for pre and cur smt_counter samples 669 | if (pre_sample[j] == &sample1[j]) { 670 | pre_sample[j] = &sample2[j]; 671 | cur_sample[j] = &sample1[j]; 672 | } 673 | else { 674 | pre_sample[j] = &sample1[j]; 675 | cur_sample[j] = &sample2[j]; 676 | } 677 | } 678 | 679 | #ifndef _WIN32 680 | if (using_api) { 681 | ++write_count; 682 | pthread_mutex_unlock(&buffer_lock); 683 | } 684 | #endif 685 | 686 | if (output_format_two) { 687 | for (itr_p = 0; itr_p < num_packages; itr_p++) { 688 | for (itr_m = 0; itr_m < 4; itr_m++) { 689 | for (itr_d = 0; itr_d < count; itr_d++) { 690 | // calculate derived output metrics 691 | 692 | if (itr_p != lp_DIMM_devices[itr_d].socket_id) { 693 | continue; 694 | } 695 | 696 | if (itr_m == 0) { 697 | // bytes_read less than bytes_written results in negative value for total_bytes_read/media_read 698 | // providing "0" value to avoid displaying bogus negative value in this scenario 699 | if (diff[itr_d].bytes_read > diff[itr_d].bytes_written) { 700 | total_bytes_read = (diff[itr_d].bytes_read - diff[itr_d].bytes_written) * 64; 701 | media_read = (diff[itr_d].bytes_read - diff[itr_d].bytes_written) / 4; 702 | } 703 | else { 704 | total_bytes_read = 0ULL; 705 | media_read = 0ULL; 706 | } 707 | total_bytes_written = diff[itr_d].bytes_written * 64; 708 | media_write = diff[itr_d].bytes_written / 4; 709 | 710 | // print media metrics' counts 711 | fprintf(fout, "%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s", 712 | total_bytes_read, 713 | DELIMITER, 714 | total_bytes_written, 715 | DELIMITER, 716 | media_read, 717 | DELIMITER, 718 | media_write, 719 | DELIMITER 720 | ); 721 | } 722 | else if (itr_m == 1) { 723 | // calculate media_read and media_write again 724 | if (diff[itr_d].bytes_read > diff[itr_d].bytes_written) { 725 | media_read = (diff[itr_d].bytes_read - diff[itr_d].bytes_written) / 4; 726 | } 727 | else { 728 | media_read = 0ULL; 729 | } 730 | media_write = diff[itr_d].bytes_written / 4; 731 | 732 | // calculating read and write hit ratio 733 | if (diff[itr_d].host_reads > media_read) { 734 | double opr_01 = (double) diff[itr_d].host_reads - media_read; 735 | read_hit_ratio = (double) opr_01 / diff[itr_d].host_reads; 736 | } 737 | else { 738 | read_hit_ratio = 0; 739 | } 740 | if (diff[itr_d].host_writes > media_write) { 741 | double opr_02 = (double) diff[itr_d].host_writes - media_write; 742 | write_hit_ratio = (double) opr_02 / diff[itr_d].host_writes; 743 | } 744 | else { 745 | write_hit_ratio = 0; 746 | } 747 | 748 | // print buffer metrics' counts 749 | fprintf(fout, "%.2lf%s%.2lf%s", 750 | read_hit_ratio, 751 | DELIMITER, 752 | write_hit_ratio, 753 | DELIMITER 754 | ); 755 | } 756 | else if (itr_m == 2) { 757 | // print controller metrics' counts 758 | fprintf(fout, "%" PRIu64 "%s%" PRIu64 "%s", 759 | diff[itr_d].bytes_read, 760 | DELIMITER, 761 | diff[itr_d].bytes_written, 762 | DELIMITER 763 | ); 764 | } 765 | else if (itr_m == 3) { 766 | // print CPU metrics' counts 767 | fprintf(fout, "%" PRIu64 "%s%" PRIu64 "%s", 768 | diff[itr_d].host_reads, 769 | DELIMITER, 770 | diff[itr_d].host_writes, 771 | DELIMITER 772 | ); 773 | } 774 | } 775 | } 776 | } 777 | } 778 | 779 | fprintf(fout,"\n"); 780 | fflush(fout); 781 | 782 | pre_tsc = cur_tsc; 783 | cur_time = PMW_UTILS_Get_Curr_Time_For_Sleep(); 784 | elapsed_time = cur_time - start_time; 785 | 786 | ret_code = adjust_for_overhead(i, elapsed_time, &num_skips); 787 | if (ret_code != 0) { 788 | goto cleanup; 789 | } 790 | 791 | sleep_time = usec_sample_time - elapsed_time; 792 | 793 | PMW_UTILS_Sleep(sleep_time); 794 | } 795 | 796 | cleanup: 797 | free(sample1); 798 | free(sample2); 799 | free(diff); 800 | 801 | #ifndef _WIN32 802 | if (using_api) { 803 | pthread_mutex_lock(&buffer_lock); 804 | collection_done = 1; 805 | pthread_mutex_unlock(&buffer_lock); 806 | } 807 | #endif 808 | 809 | if (num_skips) { 810 | fprintf(fout, "%d interval(s) skipped.\n", num_skips); 811 | } 812 | 813 | return ret_code; 814 | } 815 | 816 | int 817 | PMW_COLLECT_Print_Health_Info ( 818 | ) 819 | { 820 | int i, j, k, t, num_packages, num_skips = 0, ret_code = 0; 821 | unsigned int count; 822 | uint64_t pre_tsc, cur_tsc, delta_tsc, start_time, cur_time, elapsed_time, sleep_time, epoch; 823 | struct device_discovery *lp_DIMM_devices; 824 | int temp_val = 0; 825 | char media_temp[SMALL_STR_LEN], controller_temp[SMALL_STR_LEN], max_media_temp[SMALL_STR_LEN], max_controller_temp[SMALL_STR_LEN]; 826 | double media_temp_d, controller_temp_d, max_media_temp_d, max_controller_temp_d; 827 | 828 | uint64_t overflow_val = 0xFFFFFFFFFFFFFFFFULL; 829 | 830 | lp_DIMM_devices = PMW_COMM_Get_DIMM_Topology(); 831 | 832 | i = 0; 833 | j = 0; 834 | pre_tsc = 0ULL; 835 | cur_tsc = 0ULL; 836 | delta_tsc = 0ULL; 837 | PMW_COMM_Get_Number_of_DIMM(&count); 838 | 839 | #ifndef _WIN32 840 | if (using_api) { 841 | output_buf = (PMWATCH_OP_BUF) calloc (count, sizeof(PMWATCH_OP_BUF_NODE)); 842 | if (output_buf == NULL) { 843 | fprintf(stderr, "ERROR: memory allocation failed!\n"); 844 | 845 | return 1; 846 | } 847 | } 848 | #endif 849 | 850 | // get the total number of packages 851 | num_packages = lp_DIMM_devices[count - 1].socket_id; 852 | ++num_packages; 853 | 854 | HEALTH_INFO_COUNTER sample = (HEALTH_INFO_COUNTER)calloc(count, sizeof(HEALTH_INFO_COUNTER_NODE)); 855 | if (sample == NULL) { 856 | fprintf(stderr, "ERROR: memory allocation failed!\n"); 857 | #ifndef _WIN32 858 | if (using_api) { 859 | free(output_buf); 860 | } 861 | #endif 862 | 863 | return 1; 864 | } 865 | HEALTH_INFO_COUNTER health_info[count]; 866 | 867 | print_info_header(); 868 | 869 | print_header_health_info(count); 870 | 871 | for (i = 0; i < count; i++) { 872 | health_info[i] = &sample[i]; 873 | } 874 | 875 | for (i = 0; i <= loops || infinite_run; i++){ 876 | if (i == 0) { 877 | start_time = PMW_UTILS_Get_Curr_Time_For_Sleep(); 878 | 879 | pre_tsc = PMW_UTILS_Read_TSC(); 880 | 881 | cur_time = start_time; 882 | elapsed_time = cur_time - start_time; 883 | 884 | ret_code = adjust_for_overhead(i, elapsed_time, &num_skips); 885 | if (ret_code != 0) { 886 | goto cleanup; 887 | } 888 | 889 | sleep_time = usec_sample_time - elapsed_time; 890 | 891 | PMW_UTILS_Sleep(sleep_time); 892 | 893 | continue; 894 | } 895 | 896 | cur_tsc = PMW_UTILS_Read_TSC(); 897 | 898 | CALCULATE_DIFFERENCE(cur_tsc, pre_tsc, delta_tsc, overflow_val); 899 | 900 | epoch = PMW_UTILS_Get_Curr_Time_In_Sec(); 901 | fprintf(fout,"%" PRIu64 "%s%" PRIu64 "%s", epoch, DELIMITER, delta_tsc, DELIMITER); 902 | 903 | start_time = PMW_UTILS_Get_Curr_Time_For_Sleep(); 904 | 905 | #ifndef _WIN32 906 | if (using_api) { 907 | pthread_mutex_lock(&buffer_lock); 908 | } 909 | #endif 910 | 911 | for (j = 0; j < count; j++) { 912 | #ifndef _WIN32 913 | if (using_api) { 914 | output_buf[j].timestamp = delta_tsc; 915 | output_buf[j].epoch = epoch; 916 | } 917 | #endif 918 | 919 | ret_code = PMW_COMM_Get_Health_Info_Page(j, health_info[j]); 920 | if ( ret_code != NVM_SUCCESS) { 921 | #ifndef _WIN32 922 | if (using_api) { 923 | pthread_mutex_unlock(&buffer_lock); 924 | } 925 | else { 926 | #endif 927 | fprintf(stderr, "\nSomething went wrong while collecting health info.\n"); 928 | #ifndef _WIN32 929 | } 930 | #endif 931 | goto cleanup; 932 | } 933 | 934 | for (k = 0; k <= HEALTH_INFO_VENDOR_SPECIFIC_DATA_SIZE; k++) { 935 | if (PMWATCH_GET_BIT_VALUE(health_info[j]->validation_flags, k)) { 936 | switch (k) { 937 | 938 | case 0: // Health Status 939 | temp_val = 0; 940 | for (t = 0; t <= HEALTH_STATUS_FATAL; t++) { 941 | if (PMWATCH_GET_BIT_VALUE(health_info[j]->health_status, t)) { 942 | temp_val = t + 1; 943 | } 944 | } 945 | health_info[j]->health_status = temp_val; 946 | break; 947 | 948 | case 1: // Percentage Remaining 949 | health_info[j]->percentage_used = 100 - health_info[j]->percentage_remaining; 950 | break; 951 | 952 | case 3: // Media Temperature 953 | media_temp_d = (health_info[j]->media_temp & TEMP_VALUE_MASK) * CELCIUS_CONV_VAL; 954 | memset(media_temp, 0, sizeof(char) * SMALL_STR_LEN); 955 | snprintf(media_temp, sizeof(media_temp) - 1, "%s%.2f", \ 956 | PMWATCH_GET_BIT_VALUE(health_info[j]->media_temp, TEMP_SIGN_BIT_INDEX) ? "-" : "", \ 957 | media_temp_d); 958 | break; 959 | 960 | case 4: // Controller Temperature 961 | controller_temp_d = (health_info[j]->controller_temp & TEMP_VALUE_MASK) * CELCIUS_CONV_VAL; 962 | memset(controller_temp, 0, sizeof(char) * SMALL_STR_LEN); 963 | snprintf(controller_temp, sizeof(controller_temp) - 1, "%s%.2f", \ 964 | PMWATCH_GET_BIT_VALUE(health_info[j]->controller_temp, TEMP_SIGN_BIT_INDEX) ? "-" : "", \ 965 | controller_temp_d); 966 | break; 967 | 968 | case 11: 969 | max_media_temp_d = 0; 970 | max_controller_temp_d = 0; 971 | memset(max_media_temp, 0, sizeof(char) * SMALL_STR_LEN); 972 | memset(max_controller_temp, 0, sizeof(char) * SMALL_STR_LEN); 973 | 974 | if (health_info[j]->vendor_data_size > 0) { 975 | max_media_temp_d = (health_info[j]->max_media_temp & TEMP_VALUE_MASK) * CELCIUS_CONV_VAL; 976 | max_controller_temp_d = (health_info[j]->max_controller_temp & TEMP_VALUE_MASK) * CELCIUS_CONV_VAL; 977 | snprintf(max_media_temp, sizeof(max_media_temp), "%s%.2f", \ 978 | PMWATCH_GET_BIT_VALUE(health_info[j]->max_media_temp, TEMP_SIGN_BIT_INDEX) ? "-" : "", \ 979 | max_media_temp_d); 980 | 981 | snprintf(max_controller_temp, sizeof(max_controller_temp), "%s%.2f", \ 982 | PMWATCH_GET_BIT_VALUE(health_info[j]->max_controller_temp, TEMP_SIGN_BIT_INDEX) ? "-" : "", \ 983 | max_controller_temp_d); 984 | } 985 | else { 986 | health_info[j]->power_on_time = 0; 987 | health_info[j]->uptime = 0; 988 | health_info[j]->last_shutdown_time = 0; 989 | } 990 | break; 991 | 992 | default: 993 | break; 994 | } 995 | } 996 | } 997 | 998 | // Add to this fprintf when want to enable more fields. Data is already obtained 999 | fprintf(fout, "%d%s%d%s%d%s%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s%s%s%s%s%s%s%s%s", \ 1000 | health_info[j]->health_status, \ 1001 | DELIMITER, \ 1002 | health_info[j]->percentage_used, \ 1003 | DELIMITER, \ 1004 | health_info[j]->percentage_remaining, \ 1005 | DELIMITER, \ 1006 | health_info[j]->power_on_time, \ 1007 | DELIMITER, \ 1008 | health_info[j]->uptime, \ 1009 | DELIMITER, \ 1010 | health_info[j]->last_shutdown_time, \ 1011 | DELIMITER, \ 1012 | media_temp, \ 1013 | DELIMITER, \ 1014 | controller_temp, \ 1015 | DELIMITER, \ 1016 | max_media_temp, \ 1017 | DELIMITER, \ 1018 | max_controller_temp, \ 1019 | DELIMITER \ 1020 | ); 1021 | #ifndef _WIN32 1022 | if (using_api) { 1023 | output_buf[j].health_status = health_info[j]->health_status; 1024 | output_buf[j].percentage_remaining = health_info[j]->percentage_remaining; 1025 | output_buf[j].percentage_used = health_info[j]->percentage_used; 1026 | output_buf[j].power_on_time = health_info[j]->power_on_time; 1027 | output_buf[j].uptime = health_info[j]->uptime; 1028 | output_buf[j].last_shutdown_time = health_info[j]->last_shutdown_time; 1029 | output_buf[j].media_temp = media_temp_d; 1030 | output_buf[j].controller_temp = controller_temp_d; 1031 | output_buf[j].max_media_temp = max_media_temp_d; 1032 | output_buf[j].max_controller_temp = max_controller_temp_d; 1033 | } 1034 | #endif 1035 | 1036 | #ifdef BUILD_DEBUG 1037 | #ifndef _WIN32 1038 | if (using_api) { 1039 | fprintf(fout, "|%d%s%d%s%d%s%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s%.2f%s%.2f%s%.2f%s%.2f%s|", 1040 | output_buf[j].health_status, 1041 | DELIMITER, 1042 | output_buf[j].percentage_used, 1043 | DELIMITER, 1044 | output_buf[j].percentage_remaining, 1045 | DELIMITER, 1046 | output_buf[j].power_on_time, 1047 | DELIMITER, 1048 | output_buf[j].uptime, 1049 | DELIMITER, 1050 | output_buf[j].last_shutdown_time, 1051 | DELIMITER, 1052 | output_buf[j].media_temp, 1053 | DELIMITER, 1054 | output_buf[j].controller_temp, 1055 | DELIMITER, 1056 | output_buf[j].max_media_temp, 1057 | DELIMITER, 1058 | output_buf[j].max_controller_temp, 1059 | DELIMITER 1060 | ); 1061 | } 1062 | #endif 1063 | #endif 1064 | 1065 | } 1066 | 1067 | #ifndef _WIN32 1068 | if (using_api) { 1069 | ++write_count; 1070 | pthread_mutex_unlock(&buffer_lock); 1071 | } 1072 | #endif 1073 | fprintf(fout, "\n"); 1074 | fflush(fout); 1075 | 1076 | pre_tsc = cur_tsc; 1077 | cur_time = PMW_UTILS_Get_Curr_Time_For_Sleep(); 1078 | elapsed_time = cur_time - start_time; 1079 | 1080 | ret_code = adjust_for_overhead(i, elapsed_time, &num_skips); 1081 | if (ret_code != 0) { 1082 | goto cleanup; 1083 | } 1084 | 1085 | sleep_time = usec_sample_time - elapsed_time; 1086 | 1087 | PMW_UTILS_Sleep(sleep_time); 1088 | } 1089 | 1090 | cleanup: 1091 | free(sample); 1092 | 1093 | #ifndef _WIN32 1094 | if (using_api) { 1095 | pthread_mutex_lock(&buffer_lock); 1096 | collection_done = 1; 1097 | pthread_mutex_unlock(&buffer_lock); 1098 | } 1099 | #endif 1100 | 1101 | if (num_skips) { 1102 | fprintf(fout, "%d interval(s) skipped.\n", num_skips); 1103 | } 1104 | 1105 | return ret_code; 1106 | } 1107 | 1108 | void 1109 | PMW_COLLECT_Print_Lifetime_Mem_Info_Counters () 1110 | { 1111 | int i, j, ret_code; 1112 | unsigned char page_num; 1113 | unsigned int count; 1114 | 1115 | i = 0; 1116 | j = 0; 1117 | page_num = 1; 1118 | PMW_COMM_Get_Number_of_DIMM(&count); 1119 | 1120 | LIFETIME_INFO_COUNTER_NODE cur_sample[count]; 1121 | 1122 | print_info_header(); 1123 | 1124 | fprintf(fout, "%s%s%s%s%s%s%s%s%s%s\n", 1125 | "DIMM", 1126 | DELIMITER, 1127 | "read_64B_ops_received", 1128 | DELIMITER, 1129 | "write_64B_ops_received", 1130 | DELIMITER, 1131 | "cpu_read_ops", 1132 | DELIMITER, 1133 | "cpu_write_ops", 1134 | DELIMITER); 1135 | 1136 | for (i = 0; i < count; i++) { 1137 | ret_code = PMW_COMM_Get_Memory_Info_Page(i, &cur_sample[i], page_num); 1138 | if ( ret_code != NVM_SUCCESS) { 1139 | fprintf(stderr, "\nSomething went wrong while collecting metrics.\n"); 1140 | return; 1141 | } 1142 | fprintf(fout, 1143 | "%s%d%s%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s%" PRIu64 "%s\n", 1144 | "DIMM", 1145 | i, 1146 | DELIMITER, 1147 | cur_sample[i].bytes_read, 1148 | DELIMITER, 1149 | cur_sample[i].bytes_written, 1150 | DELIMITER, 1151 | cur_sample[i].host_reads, 1152 | DELIMITER, 1153 | cur_sample[i].host_writes, 1154 | DELIMITER 1155 | ); 1156 | } 1157 | 1158 | return; 1159 | } 1160 | 1161 | #ifndef _WIN32 1162 | void* threadable_func ( 1163 | void *arg 1164 | ) 1165 | { 1166 | if (collect_lifetime) { 1167 | thread_status = PMW_COLLECT_Print_Smart_Counters(); 1168 | } 1169 | if (collect_health) { 1170 | thread_status = PMW_COLLECT_Print_Health_Info(); 1171 | } 1172 | 1173 | pthread_exit((void *) &thread_status); 1174 | } 1175 | 1176 | int 1177 | PMW_COLLECT_Get_DIMM_Count ( 1178 | int *num_nvmdimms 1179 | ) 1180 | { 1181 | return PMW_COMM_Get_Number_of_DIMM(num_nvmdimms); 1182 | } 1183 | 1184 | int 1185 | PMW_COLLECT_Start_Collection ( 1186 | PMWATCH_CONFIG_NODE pmwatch_config 1187 | ) 1188 | { 1189 | int ret_code; 1190 | 1191 | loops = 1; 1192 | using_api = 1; 1193 | infinite_run = 1; 1194 | 1195 | collect_health = pmwatch_config.collect_health; 1196 | collect_lifetime = pmwatch_config.collect_perf_metrics; 1197 | 1198 | if (collect_lifetime && collect_health) { 1199 | fprintf(stderr, "WARNING: Health info and performance metrics cannot be collected simultaneously. Continuing with performance metrics collection...\n"); 1200 | collect_health = 0; 1201 | } 1202 | else if (!collect_lifetime && !collect_health) { 1203 | fprintf(stderr, "ERROR: health info and performance metrics collection disabled. Enable atleast one.\n"); 1204 | 1205 | pthread_mutex_lock(&buffer_lock); 1206 | collection_done = 1; 1207 | pthread_mutex_unlock(&buffer_lock); 1208 | 1209 | return 1; 1210 | } 1211 | 1212 | usec_sample_time = (uint64_t) (pmwatch_config.interval * MICRO_SEC); 1213 | // needed during overhead adjustment 1214 | usec_sample_time_prev = usec_sample_time; 1215 | 1216 | filename = (char *) calloc(SMALL_STR_LEN, sizeof(char)); 1217 | if (filename == NULL) { 1218 | fprintf(stderr, "ERROR: memory allocation failed!\n"); 1219 | 1220 | pthread_mutex_lock(&buffer_lock); 1221 | collection_done = 1; 1222 | pthread_mutex_unlock(&buffer_lock); 1223 | 1224 | return 1; 1225 | } 1226 | 1227 | ret_code = STRNCPY_SAFE(filename, SMALL_STR_LEN, "/dev/null"); 1228 | if (ret_code != NVM_SUCCESS) { 1229 | pthread_mutex_lock(&buffer_lock); 1230 | collection_done = 1; 1231 | pthread_mutex_unlock(&buffer_lock); 1232 | 1233 | return ret_code; 1234 | } 1235 | fout = fopen(filename, "w"); 1236 | if (!fout) { 1237 | fprintf(stderr, "ERROR: Unable to create output file %s. Exiting...\n", filename); 1238 | free(filename); 1239 | 1240 | pthread_mutex_lock(&buffer_lock); 1241 | collection_done = 1; 1242 | pthread_mutex_unlock(&buffer_lock); 1243 | 1244 | return 1; 1245 | } 1246 | 1247 | ret_code = PMW_COMM_Init(); 1248 | if (ret_code != 0) { 1249 | PMW_COMM_Cleanup(); 1250 | fclose(fout); 1251 | free(filename); 1252 | 1253 | pthread_mutex_lock(&buffer_lock); 1254 | collection_done = 1; 1255 | pthread_mutex_unlock(&buffer_lock); 1256 | 1257 | return 1; 1258 | } 1259 | 1260 | pthread_create(&thread_id, NULL, threadable_func, NULL); 1261 | 1262 | return 0; 1263 | } 1264 | 1265 | int 1266 | PMW_COLLECT_Stop_Collection () 1267 | { 1268 | infinite_run = 0; 1269 | 1270 | // wait for collection thread to exit 1271 | while (1) { 1272 | pthread_mutex_lock(&buffer_lock); 1273 | if (collection_done || thread_status != 0) { 1274 | break; 1275 | } 1276 | pthread_mutex_unlock(&buffer_lock); 1277 | }; 1278 | pthread_mutex_unlock(&buffer_lock); 1279 | 1280 | loops = 0; 1281 | using_api = 0; 1282 | collect_health = 0; 1283 | collection_done = 0; 1284 | collect_lifetime = 0; 1285 | 1286 | free(output_buf); 1287 | fclose(fout); 1288 | free(filename); 1289 | 1290 | return 0; 1291 | } 1292 | 1293 | int 1294 | PMW_COLLECT_Read_Data ( 1295 | PMWATCH_OP_BUF* op_buf 1296 | ) 1297 | { 1298 | unsigned int count; 1299 | 1300 | // wait to finish the first write to metrics count buffer 1301 | while (1) { 1302 | pthread_mutex_lock(&buffer_lock); 1303 | if (write_count != 0 || thread_status != 0) { 1304 | break; 1305 | } 1306 | pthread_mutex_unlock(&buffer_lock); 1307 | }; 1308 | 1309 | if (thread_status != 0) { 1310 | fprintf(stderr, "%s:%d Collection thread exited with an error!\n", __FUNCTION__, __LINE__); 1311 | pthread_mutex_unlock(&buffer_lock); 1312 | 1313 | return 1; 1314 | } 1315 | 1316 | PMW_COMM_Get_Number_of_DIMM(&count); 1317 | 1318 | memset(*op_buf, 0, count * sizeof(PMWATCH_OP_BUF_NODE)); 1319 | memcpy(*op_buf, output_buf, count * sizeof(PMWATCH_OP_BUF_NODE)); 1320 | 1321 | pthread_mutex_unlock(&buffer_lock); 1322 | 1323 | return 0; 1324 | } 1325 | #endif 1326 | -------------------------------------------------------------------------------- /src/pmw_comm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef _WIN32 34 | #define PMW_DLL_EXPORTS 1 35 | #define __NVM_DLL__ 1 36 | #else 37 | #include 38 | #endif 39 | 40 | #include "nvm_types.h" 41 | #include "nvm_management.h" 42 | 43 | #include "inc/pmw_comm.h" 44 | #include "inc/pmw_utils.h" 45 | 46 | #define LIFETIME_INFO_PAGE_NUM 1 47 | 48 | #define IS_LIFETIME_INFO(x) (x == LIFETIME_INFO_PAGE_NUM ? 1 : 0) 49 | 50 | /* 51 | * Defines the Firmware Command Table opcodes 52 | */ 53 | enum passthrough_opcode { 54 | PT_GET_LOG = 0x08 55 | }; 56 | 57 | /* 58 | * Defines the Sub-Opcodes for PT_GET_LOG 59 | */ 60 | enum get_log_subop { 61 | SUBOP_SMART_HEALTH = 0x00, 62 | SUBOP_MEM_INFO = 0x03 63 | }; 64 | 65 | 66 | #ifdef _WIN32 67 | HANDLE handle; 68 | int (*p_nvm_create_context)(); 69 | int (*p_nvm_free_context)(const NVM_BOOL); 70 | int (*p_nvm_get_build_number)(); 71 | int (*p_nvm_get_number_of_devices)(int*); 72 | int (*p_nvm_get_devices)(struct device_discovery*, const NVM_UINT8); 73 | int (*p_nvm_get_nvm_capacities)(struct device_capacities*); 74 | int (*p_nvm_send_device_passthrough_cmd)(const NVM_UID, struct device_pt_cmd*); 75 | int (*p_nvm_get_major_version)(); 76 | int (*p_nvm_get_minor_version)(); 77 | int (*p_nvm_get_hotfix_number)(); 78 | int (*p_nvm_get_build_number)(); 79 | #endif 80 | 81 | NVM_UINT32 count = 0; 82 | char ipmctl_version_str[15]; 83 | struct device_discovery *gp_DIMM_devices = NULL; 84 | int ipmctl_major_ver, ipmctl_minor_ver, ipmctl_hot_fix_num, ipmctl_build; 85 | 86 | /*! 87 | * @fn struct device_discovery* PMW_COMM_Get_DIMM_Topology 88 | * 89 | * @brief Get the the DIMM information 90 | * 91 | * @param None 92 | * 93 | * @return struct device_discovery* 94 | * 95 | */ 96 | struct device_discovery* 97 | PMW_COMM_Get_DIMM_Topology () 98 | { 99 | return gp_DIMM_devices; 100 | } 101 | 102 | /*! 103 | * @fn NVM_INT32 PMW_COMM_Get_Smart_Counters 104 | * 105 | * @brief Get the the smart couters in the DIMM dimm_index in the default page number 106 | * 107 | * @param NVM_INT32 dimm_index - index of the dimm 108 | * @param LIFETIME_INFO_COUNTER smt_counters - structure of smart counter 109 | * 110 | * @return error code 111 | * 112 | */ 113 | NVM_INT32 114 | PMW_COMM_Get_Memory_Info ( 115 | NVM_INT32 dimm_index, 116 | LIFETIME_INFO_COUNTER smt_counters 117 | ) 118 | { 119 | return PMW_COMM_Get_Memory_Info_Page(dimm_index, smt_counters, LIFETIME_INFO_PAGE_NUM); 120 | } 121 | 122 | /*! 123 | * @fn NVM_INT32 PMW_COMM_Get_Count_by_Topology 124 | * 125 | * @brief Get the the smart couters based on user provided DIMM topology 126 | * 127 | * @param NVM_INT32 socket_num, 128 | * @param NVM_INT32 mc_num, 129 | * @param NVM_INT32 channel_num, 130 | * @param NVM_INT32 dimm_num, 131 | * @param LIFETIME_INFO_COUNTER smt_counters 132 | * 133 | * @return error code 134 | * 135 | */ 136 | NVM_INT32 137 | PMW_COMM_Get_Count_by_Topology ( 138 | NVM_INT32 socket_num, 139 | NVM_INT32 mc_num, 140 | NVM_INT32 channel_num, 141 | NVM_INT32 dimm_num, 142 | LIFETIME_INFO_COUNTER smt_counters 143 | ) 144 | { 145 | int itr, dimm_found = 0; 146 | 147 | for (itr = 0; itr < count; itr++) { 148 | if (gp_DIMM_devices[itr].socket_id != socket_num) { 149 | continue; 150 | } 151 | 152 | if (gp_DIMM_devices[itr].memory_controller_id != mc_num) { 153 | continue; 154 | } 155 | 156 | if (gp_DIMM_devices[itr].channel_id != channel_num) { 157 | continue; 158 | } 159 | 160 | if (gp_DIMM_devices[itr].channel_pos != dimm_num) { 161 | continue; 162 | } 163 | 164 | dimm_found = 1; 165 | PMW_COMM_Get_Memory_Info_Page(itr, smt_counters, LIFETIME_INFO_PAGE_NUM); 166 | break; 167 | } 168 | 169 | if (dimm_found) { 170 | return NVM_SUCCESS; 171 | } 172 | else { 173 | return 1; 174 | } 175 | } 176 | 177 | /*! 178 | * @fn NVM_INT32 pmw_comm_Init_DLL() 179 | * 180 | * @brief Get process address for Windows APIs 181 | * 182 | * @param None 183 | * 184 | * @return NVM_SUCCESS on success 185 | * PMW_ERR_DLLLOAD or PMW_ERR_DLLSYM on error 186 | * 187 | */ 188 | #ifdef _WIN32 189 | NVM_INT32 190 | pmw_comm_Init_DLL () 191 | { 192 | handle = PMW_UTILS_DLL_Open(); 193 | if (handle == INVALID_HANDLE_VALUE) { 194 | return PMW_ERR_DLLLOAD; 195 | } 196 | 197 | p_nvm_create_context = (int (*)()) PMW_UTILS_DLL_Sym(handle, "nvm_create_context"); 198 | if (p_nvm_create_context == NULL) { 199 | return PMW_ERR_DLLSYM; 200 | } 201 | 202 | p_nvm_free_context = (int (*)(const NVM_BOOL)) PMW_UTILS_DLL_Sym(handle, "nvm_free_context"); 203 | if (p_nvm_free_context == NULL) { 204 | return PMW_ERR_DLLSYM; 205 | } 206 | 207 | p_nvm_get_build_number = (int (*)()) PMW_UTILS_DLL_Sym(handle, "nvm_get_build_number"); 208 | if (p_nvm_get_build_number == NULL) { 209 | return PMW_ERR_DLLSYM; 210 | } 211 | 212 | p_nvm_get_nvm_capacities = (int (*)(struct device_capacities*)) PMW_UTILS_DLL_Sym(handle, "nvm_get_nvm_capacities"); 213 | if (p_nvm_get_nvm_capacities == NULL) { 214 | return PMW_ERR_DLLSYM; 215 | } 216 | 217 | p_nvm_get_number_of_devices = (int (*)(int*)) PMW_UTILS_DLL_Sym(handle, "nvm_get_number_of_devices"); 218 | if (p_nvm_get_number_of_devices == NULL) { 219 | return PMW_ERR_DLLSYM; 220 | } 221 | 222 | p_nvm_get_devices = (int (*)(struct device_discovery*, const NVM_UINT8)) PMW_UTILS_DLL_Sym(handle, "nvm_get_devices"); 223 | if (p_nvm_get_devices == NULL) { 224 | return PMW_ERR_DLLSYM; 225 | } 226 | 227 | p_nvm_send_device_passthrough_cmd = (int (*)(const NVM_UID, struct device_pt_cmd*)) PMW_UTILS_DLL_Sym(handle, "nvm_send_device_passthrough_cmd"); 228 | if (p_nvm_send_device_passthrough_cmd == NULL) { 229 | return PMW_ERR_DLLSYM; 230 | } 231 | 232 | p_nvm_get_major_version = (int (*)()) PMW_UTILS_DLL_Sym(handle, "nvm_get_major_version"); 233 | if (p_nvm_get_major_version == NULL) { 234 | return PMW_ERR_DLLSYM; 235 | } 236 | 237 | p_nvm_get_minor_version = (int (*)()) PMW_UTILS_DLL_Sym(handle, "nvm_get_minor_version"); 238 | if (p_nvm_get_minor_version == NULL) { 239 | return PMW_ERR_DLLSYM; 240 | } 241 | 242 | p_nvm_get_hotfix_number = (int (*)()) PMW_UTILS_DLL_Sym(handle, "nvm_get_hotfix_number"); 243 | if (p_nvm_get_hotfix_number == NULL) { 244 | return PMW_ERR_DLLSYM; 245 | } 246 | 247 | p_nvm_get_build_number = (int (*)()) PMW_UTILS_DLL_Sym(handle, "nvm_get_build_number"); 248 | if (p_nvm_get_build_number == NULL) { 249 | return PMW_ERR_DLLSYM; 250 | } 251 | 252 | return NVM_SUCCESS; 253 | } 254 | #endif 255 | 256 | /*! 257 | * @fn NVM_INT32 pmw_comm_compare_topology 258 | * 259 | * @brief compare function for the quick sort 260 | * 261 | * @param const void *a - NVDIMM_TOPOLOGY struct 262 | * @param const void *b - NVDIMM_TOPOLOGY struct 263 | * 264 | * @return NVM_INT32 265 | * 266 | */ 267 | NVM_INT32 268 | pmw_comm_Compare_Topology ( 269 | const void *a, 270 | const void *b 271 | ) 272 | { 273 | struct device_discovery *p_a = (struct device_discovery*) a; 274 | struct device_discovery *p_b = (struct device_discovery*) b; 275 | 276 | return p_a->device_handle.handle - p_b->device_handle.handle; 277 | } 278 | 279 | /*! 280 | * @fn void pmw_comm_Sort_DIMM_Topology 281 | * 282 | * @brief Sort the topology struct array based on socket number 283 | * 284 | * @param const NVM_UINT8 count - number of dimms 285 | * @param NVDIMM_TOPOLOGY p_dimm_topo - dimm topology information 286 | * 287 | * @return void 288 | * 289 | */ 290 | void 291 | pmw_comm_Sort_DIMM_Topology () 292 | { 293 | qsort(gp_DIMM_devices, count, sizeof(struct device_discovery), pmw_comm_Compare_Topology); 294 | } 295 | 296 | /*! 297 | * @fn NVM_INT32 pmw_comm_Get_Mode 298 | * 299 | * @brief Check and print the mode of DCPMM DIMM (App Direct, memory mode or both) 300 | * 301 | * @return NVM_SUCCESS on success 302 | * 303 | */ 304 | NVM_INT32 305 | pmw_comm_Get_Mode () 306 | { 307 | NVM_INT32 is_ad = 0, is_2lm = 0; 308 | 309 | // check to see if DCPMM has memory mode or AD capability. 310 | struct device_capacities *p_dcap; 311 | p_dcap = (struct device_capacities*) calloc(1, sizeof(struct device_capacities)); 312 | if (p_dcap == NULL) { 313 | fprintf(stderr, "ERROR: memory allocation failed!\n"); 314 | return 1; 315 | } 316 | 317 | NVM_GET_NVM_CAPACITIES(p_dcap); 318 | 319 | if (p_dcap->memory_capacity != 0) { 320 | is_2lm = 1; 321 | } 322 | if (p_dcap->app_direct_capacity != 0) { 323 | is_ad = 1; 324 | } 325 | 326 | free(p_dcap); 327 | 328 | PMW_UTILS_Print_Key("%s", "system.nvdimm_mode"); 329 | if (is_ad && is_2lm) { 330 | PMW_UTILS_Print_Value("\"%s\"", "app_direct+memory_mode"); 331 | } 332 | else if (is_ad) { 333 | PMW_UTILS_Print_Value("\"%s\"", "app_direct"); 334 | } 335 | else if (is_2lm) { 336 | PMW_UTILS_Print_Value("\"%s\"", "memory_mode"); 337 | } 338 | else { 339 | PMW_UTILS_Print_Value("\"%s\"", "unknown"); 340 | } 341 | 342 | #if 0 343 | pmw_comm_Get_Device_Mapping(); 344 | #endif 345 | 346 | return NVM_SUCCESS; 347 | } 348 | 349 | NVM_INT32 350 | pmw_comm_Create_Context() 351 | { 352 | NVM_INT32 ret_code = 0; 353 | 354 | ret_code = NVM_CREATE_CONTEXT(); 355 | if (ret_code == NVM_ERR_INVALID_PERMISSIONS || ret_code == NVM_ERR_NO_MEM) { 356 | fprintf(stdout, "Failed to create context. Tool performace may be impacted. Continuing...\n"); 357 | return ret_code; 358 | } 359 | 360 | return NVM_SUCCESS; 361 | } 362 | 363 | NVM_INT32 364 | pmw_comm_Free_Context() 365 | { 366 | NVM_INT32 ret_code; 367 | 368 | ret_code = NVM_FREE_CONTEXT(1); 369 | if (ret_code == NVM_ERR_INVALID_PERMISSIONS || ret_code == NVM_ERR_NO_MEM) { 370 | fprintf(stderr, "Failed to free context\n"); 371 | return ret_code; 372 | } 373 | 374 | return NVM_SUCCESS; 375 | } 376 | 377 | #ifdef BUILD_DEBUG 378 | void 379 | pmw_comm_Print_Payload ( 380 | void *mb_payload, 381 | size_t size 382 | ) 383 | { 384 | const unsigned char * const bytes = mb_payload; 385 | size_t i; 386 | 387 | printf("[ "); 388 | for(i = 0; i < size; i++) 389 | { 390 | printf("%02x ", bytes[i]); 391 | if((i+1)%8 == 0) { 392 | printf(",\n "); 393 | } 394 | } 395 | printf("]\n"); 396 | } 397 | #endif 398 | 399 | void 400 | pmw_comm_Prepare_Command ( 401 | struct device_pt_cmd *cmd, 402 | NVM_UINT8 opcode, 403 | NVM_UINT8 sub_opcode, 404 | NVM_UINT32 input_payload_size, 405 | void *input_payload, 406 | NVM_UINT32 output_payload_size, 407 | void *output_payload 408 | ) 409 | { 410 | cmd->opcode = opcode; 411 | cmd->sub_opcode = sub_opcode; 412 | cmd->input_payload_size = input_payload_size; 413 | cmd->input_payload = input_payload; 414 | cmd->output_payload_size = output_payload_size; 415 | cmd->output_payload = output_payload; 416 | } 417 | 418 | NVM_INT32 419 | PMW_COMM_Get_Memory_Info_Page ( 420 | NVM_INT32 dimm_index, 421 | void *counters, 422 | const unsigned char page_num 423 | ) 424 | { 425 | NVM_INT32 ret_code = NVM_SUCCESS; 426 | struct device_pt_cmd cmd; 427 | void *p_out_payload; 428 | NVM_UINT32 out_payload_size; 429 | MEMORY_INFO_PAGE_NODE mem_info_page; //output mailbox payload 430 | LIFETIME_INFO_COUNTER smt_counters; 431 | 432 | if (IS_LIFETIME_INFO(page_num)) { 433 | memset (&mem_info_page, 0, sizeof(MEMORY_INFO_PAGE_NODE)); 434 | p_out_payload = &mem_info_page; 435 | out_payload_size = sizeof(MEMORY_INFO_PAGE_NODE); 436 | 437 | smt_counters = (LIFETIME_INFO_COUNTER) counters; 438 | } 439 | else { 440 | fprintf(stderr, "ERROR: Incorrect config information. Exiting...\n"); 441 | return 1; 442 | } 443 | 444 | struct pt_payload_input_memory_info mem_input; //input mailbox payload 445 | memset(&cmd, 0, sizeof(struct device_pt_cmd)); 446 | memset(&mem_input, 0, sizeof(struct pt_payload_input_memory_info)); 447 | 448 | mem_input.memory_page = page_num; 449 | 450 | pmw_comm_Prepare_Command(&cmd, \ 451 | PT_GET_LOG, \ 452 | SUBOP_MEM_INFO, \ 453 | sizeof(struct pt_payload_input_memory_info), \ 454 | &mem_input, \ 455 | out_payload_size, \ 456 | p_out_payload); 457 | 458 | ret_code = NVM_SEND_DEVICE_PASSTHROUGH_CMD(gp_DIMM_devices[dimm_index].uid, &cmd); 459 | if (ret_code != NVM_SUCCESS) { 460 | fprintf(stderr, "Failed to obtain count\n"); 461 | return ret_code; 462 | } 463 | 464 | //read data from mem_info_page and copy to smt_counters strcuture 465 | if (IS_LIFETIME_INFO(page_num)) { 466 | PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(mem_info_page.total_bytes_read, smt_counters->bytes_read); 467 | PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(mem_info_page.total_bytes_written, smt_counters->bytes_written); 468 | PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(mem_info_page.total_read_reqs, smt_counters->host_reads); 469 | PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(mem_info_page.total_write_reqs, smt_counters->host_writes); 470 | } 471 | 472 | return ret_code; 473 | } 474 | 475 | NVM_INT32 476 | PMW_COMM_Get_Health_Info_Page ( 477 | NVM_INT32 dimm_index, 478 | void *counters 479 | ) 480 | { 481 | NVM_INT32 ret_code = NVM_SUCCESS; 482 | struct device_pt_cmd cmd; 483 | void *p_out_payload; 484 | NVM_UINT32 out_payload_size; 485 | HEALTH_INFO_PAGE_NODE health_info_page; //output mailbox payload 486 | HEALTH_INFO_COUNTER smt_counters; 487 | 488 | memset (&health_info_page, 0, sizeof(HEALTH_INFO_PAGE_NODE)); 489 | p_out_payload = &health_info_page; 490 | out_payload_size = sizeof(HEALTH_INFO_PAGE_NODE); 491 | 492 | smt_counters = (HEALTH_INFO_COUNTER) counters; 493 | 494 | struct pt_payload_input_memory_info mem_input; //input mailbox payload 495 | memset(&cmd, 0, sizeof(struct device_pt_cmd)); 496 | memset(&mem_input, 0, sizeof(struct pt_payload_input_memory_info)); 497 | 498 | pmw_comm_Prepare_Command(&cmd, \ 499 | PT_GET_LOG, \ 500 | SUBOP_SMART_HEALTH, \ 501 | sizeof(struct pt_payload_input_memory_info), \ 502 | &mem_input, \ 503 | out_payload_size, \ 504 | p_out_payload); 505 | 506 | ret_code = NVM_SEND_DEVICE_PASSTHROUGH_CMD(gp_DIMM_devices[dimm_index].uid, &cmd); 507 | if (ret_code != NVM_SUCCESS) { 508 | fprintf(stderr, "Failed to obtain count\n"); 509 | return ret_code; 510 | } 511 | 512 | PMW_4_BYTE_ARRAY_TO_32_BIT_VALUE(health_info_page.validation_flags, smt_counters->validation_flags); 513 | PMW_2_BYTE_ARRAY_TO_16_BIT_VALUE(health_info_page.media_temp, smt_counters->media_temp); 514 | PMW_2_BYTE_ARRAY_TO_16_BIT_VALUE(health_info_page.controller_temp, smt_counters->controller_temp); 515 | PMW_4_BYTE_ARRAY_TO_32_BIT_VALUE(health_info_page.vendor_data_size, smt_counters->vendor_data_size); 516 | PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(health_info_page.power_on_time, smt_counters->power_on_time); 517 | PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(health_info_page.uptime, smt_counters->uptime); 518 | PMW_8_BYTE_ARRAY_TO_64_BIT_VALUE(health_info_page.last_shutdown_time, smt_counters->last_shutdown_time); 519 | PMW_2_BYTE_ARRAY_TO_16_BIT_VALUE(health_info_page.max_media_temp, smt_counters->max_media_temp); 520 | PMW_2_BYTE_ARRAY_TO_16_BIT_VALUE(health_info_page.max_controller_temp, smt_counters->max_controller_temp); 521 | 522 | smt_counters->health_status = health_info_page.health_status; 523 | smt_counters->percentage_remaining = health_info_page.percentage_remaining; 524 | 525 | return ret_code; 526 | } 527 | 528 | #if !defined(_WIN32) && !defined(BULID_ESXI) 529 | 530 | /*! 531 | * @fn int PMW_COMM_Check_Permission 532 | * 533 | * @brief Check if user has root permission 534 | * 535 | * @return 0 on success 536 | * 537 | */ 538 | int 539 | PMW_COMM_Check_User_Permission () 540 | { 541 | if (getuid() != ROOT_UID) { 542 | fprintf(stderr, "\nNon-root user!\n"); 543 | fprintf(stderr, "Requires root access. Please run as root.\n\n"); 544 | 545 | return 204; 546 | } 547 | 548 | return 0; 549 | } 550 | 551 | #endif 552 | 553 | /*! 554 | * @fn NVM_INT32 pmw_comm_Check_PreR_FW 555 | * 556 | * @brief Check if this is a supported firmware 557 | * 558 | * @return error code 559 | * 560 | */ 561 | NVM_INT32 562 | pmw_comm_Check_PreR_FW ( 563 | NVM_UINT32 *is_prer_fw 564 | ) 565 | { 566 | NVM_UINT32 itr, i_fw_api_minor_ver, i_fw_api_major_ver, prer_fw = 0, status = NVM_SUCCESS; 567 | char *fw_api_version, *token; 568 | char fw_api_minor_ver[10]; 569 | char fw_api_major_ver[10]; 570 | 571 | *is_prer_fw = prer_fw; 572 | 573 | for(itr = 0; itr < count; itr++) { 574 | fw_api_version = strndup(gp_DIMM_devices[itr].fw_api_version, SMALL_STR_LEN); 575 | token = strtok(fw_api_version, "."); 576 | if (!token) { 577 | fprintf(stderr, "ERROR: Failure to parse version information. Exiting...\n"); 578 | free(fw_api_version); 579 | return 1; 580 | } 581 | status = STRNCPY_SAFE(fw_api_major_ver, sizeof(fw_api_major_ver), token); 582 | if (status != NVM_SUCCESS) { 583 | free(fw_api_version); 584 | return status; 585 | } 586 | while(token) { 587 | status = STRNCPY_SAFE(fw_api_minor_ver, sizeof(fw_api_minor_ver), token); 588 | if (status != NVM_SUCCESS) { 589 | free(fw_api_version); 590 | return status; 591 | } 592 | token = strtok(NULL, "."); 593 | } 594 | 595 | i_fw_api_major_ver = atoi(fw_api_major_ver); 596 | i_fw_api_minor_ver = atoi(fw_api_minor_ver); 597 | 598 | // check for unsupported firmware 599 | prer_fw += (i_fw_api_major_ver > 1) ? 1 : 0; 600 | 601 | free(fw_api_version); 602 | } 603 | 604 | if (prer_fw) { 605 | *is_prer_fw = 1; 606 | } 607 | 608 | return status; 609 | } 610 | 611 | NVM_INT32 612 | PMW_COMM_Print_Topology ( 613 | NVM_INT32 get_topo, 614 | NVM_INT32 detailed 615 | ) 616 | { 617 | NVM_INT32 itr, dimm_avail = 1, ret_code = NVM_SUCCESS; 618 | NVM_UINT32 dummy_count, is_prer_fw = 0; 619 | 620 | #if !defined(_WIN32) && !defined(BULID_ESXI) 621 | ret_code = PMW_COMM_Check_User_Permission(); 622 | if (ret_code != NVM_SUCCESS) { 623 | return ret_code; 624 | } 625 | #endif 626 | 627 | #ifdef _WIN32 628 | ret_code = pmw_comm_Init_DLL(); 629 | if (ret_code != NVM_SUCCESS) { 630 | return 200; 631 | } 632 | #endif 633 | 634 | if (get_topo) { 635 | pmw_comm_Create_Context(); 636 | 637 | ipmctl_major_ver = NVM_GET_MAJOR_VERSION(); 638 | ipmctl_minor_ver = NVM_GET_MINOR_VERSION(); 639 | ipmctl_hot_fix_num = NVM_GET_HOTFIX_NUMBER(); 640 | ipmctl_build = NVM_GET_BUILD_NUMBER(); 641 | 642 | snprintf(ipmctl_version_str, sizeof(ipmctl_version_str), "%02d.%02d.%02d.%04d", ipmctl_major_ver, ipmctl_minor_ver, ipmctl_hot_fix_num, ipmctl_build); 643 | 644 | ret_code = PMW_COMM_Is_DIMM_Available(); 645 | if (ret_code != NVM_SUCCESS) { 646 | dimm_avail = 0; 647 | } 648 | if(!dimm_avail) { 649 | PMW_UTILS_Print_Key("%s", "system.nvdimm_availability"); 650 | PMW_UTILS_Print_Value("%d", dimm_avail); 651 | return 200; 652 | } 653 | 654 | ret_code = PMW_COMM_Get_Number_of_DIMM(&dummy_count); 655 | if (ret_code != NVM_SUCCESS) { 656 | count = 0; 657 | } 658 | if(!count) { 659 | PMW_UTILS_Print_Key("%s", "system.num_nvdimm"); 660 | PMW_UTILS_Print_Value("%d", count); 661 | return 200; 662 | } 663 | 664 | ret_code = PMW_COMM_Read_DIMM_Topology(); 665 | if (ret_code != NVM_SUCCESS) { 666 | fprintf(stderr, "Unable to obtain topology\n"); 667 | PMW_COMM_Cleanup(); 668 | return 200; 669 | } 670 | } 671 | 672 | pmw_comm_Check_PreR_FW(&is_prer_fw); 673 | if (is_prer_fw) { 674 | fprintf(stderr, "\nUnsupported firmware.\n\n"); 675 | PMW_COMM_Cleanup(); 676 | return NVM_ERR_INVALID_PERMISSIONS; 677 | } 678 | 679 | if (get_topo) { 680 | PMW_UTILS_Print_Key("%s", "system.nvdimm_availability"); 681 | PMW_UTILS_Print_Value("%d", dimm_avail); 682 | PMW_UTILS_Print_Key("%s", "system.num_nvdimm"); 683 | PMW_UTILS_Print_Value("%d", count); 684 | } 685 | 686 | for(itr = 0; itr < count; itr++) { 687 | PMW_UTILS_Print_Key("system.package[%u].memory_controller[%u].channel[%hu].dimm[%d].name", \ 688 | gp_DIMM_devices[itr].socket_id, \ 689 | gp_DIMM_devices[itr].memory_controller_id, \ 690 | gp_DIMM_devices[itr].channel_id, \ 691 | gp_DIMM_devices[itr].channel_pos 692 | ); 693 | // using channel position for DIMM ID. Not sure if it is the right way. 694 | PMW_UTILS_Print_Value("\"DIMM%d\"", itr); 695 | if (detailed) { 696 | PMW_UTILS_Print_Key("system.package[%u].memory_controller[%u].channel[%hu].dimm[%d].fw_version", \ 697 | gp_DIMM_devices[itr].socket_id, \ 698 | gp_DIMM_devices[itr].memory_controller_id, \ 699 | gp_DIMM_devices[itr].channel_id, \ 700 | gp_DIMM_devices[itr].channel_pos 701 | ); 702 | PMW_UTILS_Print_Value("\"%s\"", gp_DIMM_devices[itr].fw_revision); 703 | PMW_UTILS_Print_Key("system.package[%u].memory_controller[%u].channel[%hu].dimm[%d].fw_api_version", \ 704 | gp_DIMM_devices[itr].socket_id, \ 705 | gp_DIMM_devices[itr].memory_controller_id, \ 706 | gp_DIMM_devices[itr].channel_id, \ 707 | gp_DIMM_devices[itr].channel_pos 708 | ); 709 | PMW_UTILS_Print_Value("\"%s\"", gp_DIMM_devices[itr].fw_api_version); 710 | } 711 | } 712 | 713 | pmw_comm_Get_Mode(); 714 | 715 | if (detailed) { 716 | PMW_UTILS_Print_Key("system.ipmctl_version"); 717 | PMW_UTILS_Print_Value("%s", ipmctl_version_str); 718 | } 719 | 720 | PMW_COMM_Cleanup(); 721 | 722 | return ret_code; 723 | } 724 | 725 | /*! 726 | * @fn NVM_INT32 PMW_COMM_Read_DIMM_Topology 727 | * 728 | * @brief Get the topology info of each DCPMM DIMMs 729 | * 730 | * @param const NVM_UINT8 count - number of dimms 731 | * @param NVDIMM_TOPOLOGY p_dimm_topo - dimm topology information 732 | * 733 | * @return error code 734 | * 735 | */ 736 | NVM_INT32 737 | PMW_COMM_Read_DIMM_Topology () 738 | { 739 | NVM_INT32 ret_code; 740 | 741 | gp_DIMM_devices = (struct device_discovery*) calloc(count, sizeof(struct device_discovery)); 742 | if (gp_DIMM_devices == NULL){ 743 | fprintf(stderr, "Memory allocation failed!\n"); 744 | ret_code = NVM_ERR_NO_MEM; 745 | return ret_code; 746 | } 747 | 748 | ret_code = NVM_GET_DEVICES(gp_DIMM_devices, count); 749 | if (ret_code != NVM_SUCCESS) { 750 | fprintf(stderr, "Obtaining DIMM details failed!\n"); 751 | return NVM_ERR_UNKNOWN; 752 | } 753 | 754 | pmw_comm_Sort_DIMM_Topology(); 755 | 756 | return NVM_SUCCESS; 757 | } 758 | 759 | /*! 760 | * @fn NVM_INT32 PMW_COMM_Get_Number_of_DIMM 761 | * 762 | * @brief Get the number of DCPMM DIMMs 763 | * 764 | * @return error code 765 | * 766 | */ 767 | NVM_INT32 768 | PMW_COMM_Get_Number_of_DIMM ( 769 | NVM_UINT32 *dimm_count 770 | ) 771 | { 772 | NVM_INT32 ret_val = NVM_SUCCESS; 773 | 774 | ret_val = NVM_GET_NUMBER_OF_DEVICES(&count); 775 | 776 | *dimm_count = count; 777 | 778 | if (count == 0) { 779 | fprintf(stderr, "Intel Optane DIMMs are not available in this system.\n"); 780 | return NVM_ERR_DIMM_NOT_FOUND; 781 | } 782 | 783 | if (ret_val != NVM_SUCCESS) { 784 | fprintf(stderr, "Obtaining the number of Intel Optane DIMMs failed!\n"); 785 | } 786 | 787 | return ret_val; 788 | } 789 | 790 | /*! 791 | * @fn NVM_INT32 PMW_COMM_Is_DIMM_Available 792 | * 793 | * @brief Check if DCPMM DIMM is available in the machine or not 794 | * Print DCPMM DIMM details if print_info is enabled 795 | * Check the DCPMM DIMM mode if print_info is enabled 796 | * 797 | * @return error code 798 | * 799 | */ 800 | NVM_INT32 801 | PMW_COMM_Is_DIMM_Available () 802 | { 803 | NVM_INT32 count, ret_val = NVM_SUCCESS; 804 | 805 | ret_val = NVM_GET_NUMBER_OF_DEVICES(&count); 806 | if (count == 0) { 807 | return NVM_ERR_DIMM_NOT_FOUND; 808 | } 809 | 810 | if (ret_val != NVM_SUCCESS) { 811 | fprintf(stderr, "Obtaining the number of Intel Optane DIMMs failed!\n"); 812 | } 813 | 814 | return ret_val; 815 | } 816 | 817 | /*! 818 | * @fn void PMW_COMM_Print_Warning_Message 819 | * 820 | * @brief Print PMWatch pre-requisites as warning 821 | * 822 | * @return void 823 | * 824 | */ 825 | void 826 | PMW_COMM_Print_Warning_Message () 827 | { 828 | fprintf(stderr, "\nWARNING: pmwatch pre-requisites:\n"); 829 | fprintf(stderr, " Firmware version may be unsupported.\n"); 830 | 831 | if (count != 0 && gp_DIMM_devices != NULL) { 832 | fprintf(stderr, " Firmware version found on the system: %s\n", gp_DIMM_devices[0].fw_revision); 833 | } 834 | 835 | fprintf(stderr, "\n ipmctl >= "); 836 | fprintf(stderr, "%s\n", PMWATCH_SUPPORT_IPMCTL_VERSION_MIN); 837 | fprintf(stderr, " ipmctl found on the system: %02d.%02d.%02d.%04d\n", ipmctl_major_ver, ipmctl_minor_ver, ipmctl_hot_fix_num, ipmctl_build); 838 | fprintf(stderr, "\npmwatch is not guaranteed to work with versions less than the ones mentioned above.\n\n"); 839 | 840 | return; 841 | } 842 | 843 | /*! 844 | * @fn NVM_INT32 PMW_COMM_Init 845 | * 846 | * @brief Intialize APIs 847 | * 848 | * @param None 849 | * 850 | * @return error code 851 | * 852 | */ 853 | NVM_INT32 854 | PMW_COMM_Init () 855 | { 856 | NVM_INT32 ret_code = NVM_SUCCESS; 857 | NVM_UINT32 dummy_count, is_prer_fw = 0; 858 | gp_DIMM_devices = NULL; 859 | 860 | #ifdef _WIN32 861 | ret_code = pmw_comm_Init_DLL(); 862 | if (ret_code != NVM_SUCCESS) { 863 | return 201; 864 | } 865 | #endif 866 | 867 | pmw_comm_Create_Context(); 868 | 869 | #if !defined(_WIN32) && !defined(BULID_ESXI) 870 | ret_code = PMW_COMM_Check_User_Permission(); 871 | if (ret_code != NVM_SUCCESS) { 872 | return ret_code; 873 | } 874 | #endif 875 | 876 | ipmctl_major_ver = NVM_GET_MAJOR_VERSION(); 877 | ipmctl_minor_ver = NVM_GET_MINOR_VERSION(); 878 | ipmctl_hot_fix_num = NVM_GET_HOTFIX_NUMBER(); 879 | ipmctl_build = NVM_GET_BUILD_NUMBER(); 880 | 881 | snprintf(ipmctl_version_str, sizeof(ipmctl_version_str), "%02d.%02d.%02d.%04d", ipmctl_major_ver, ipmctl_minor_ver, ipmctl_hot_fix_num, ipmctl_build); 882 | 883 | ret_code = PMW_COMM_Is_DIMM_Available(); 884 | if (ret_code != NVM_SUCCESS) { 885 | fprintf(stderr, "Intel Optane DIMMS are not available on this system.\n"); 886 | return 201; 887 | } 888 | 889 | ret_code = PMW_COMM_Get_Number_of_DIMM(&dummy_count); 890 | if (ret_code != NVM_SUCCESS) { 891 | return 201; 892 | } 893 | 894 | ret_code = PMW_COMM_Read_DIMM_Topology(); 895 | if (ret_code != NVM_SUCCESS) { 896 | fprintf(stderr, "Failed to initialize.\n"); 897 | return 201; 898 | } 899 | 900 | pmw_comm_Check_PreR_FW(&is_prer_fw); 901 | if (is_prer_fw) { 902 | fprintf(stderr, "\nUnsupported firmware.\n\n"); 903 | return NVM_ERR_INVALID_PERMISSIONS; 904 | } 905 | 906 | return ret_code; 907 | } 908 | 909 | NVM_INT32 910 | PMW_COMM_Cleanup () 911 | { 912 | free(gp_DIMM_devices); 913 | 914 | pmw_comm_Free_Context(); 915 | 916 | #ifdef _WIN32 917 | PMW_UTILS_DLL_Close(handle); 918 | #endif 919 | 920 | return NVM_SUCCESS; 921 | } 922 | -------------------------------------------------------------------------------- /src/pmw_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2018 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef _WIN32 33 | #include 34 | #include 35 | #pragma intrinsic(__rdtsc) 36 | #else 37 | #include 38 | #include 39 | #endif 40 | 41 | #if defined(_WIN32) 42 | #ifndef PMW_DLL_EXPORTS 43 | #define PMW_DLL_EXPORTS 1 44 | #endif 45 | #define DLL_FILE_NAME L"libipmctl.dll" 46 | #endif 47 | 48 | #include "pmw_utils.h" 49 | 50 | NVM_UINT32 51 | PMW_UTILS_Strncpy_Safe ( 52 | char *dst, 53 | size_t dst_size, 54 | const char *src 55 | ) 56 | { 57 | if (!dst || !src) { 58 | fprintf(stderr, "ERROR: NULL value for strncpy operation\n"); 59 | 60 | return 1; 61 | } 62 | 63 | size_t to_copy = strlen(src); 64 | 65 | if (to_copy >= dst_size) { 66 | fprintf(stderr, "ERROR: (strncpy) buffer too small\n"); 67 | 68 | return 1; 69 | } 70 | 71 | memcpy(dst, src, to_copy); 72 | dst[to_copy] = '\0'; 73 | 74 | return NVM_SUCCESS; 75 | } 76 | 77 | NVM_UINT32 78 | PMW_UTILS_Strncat_Safe( 79 | char *dst, 80 | size_t dst_size, 81 | const char *src 82 | ) 83 | { 84 | size_t to_copy, copy_at; 85 | 86 | if (!dst || !src) { 87 | fprintf(stderr, "ERROR: NULL value for strncat operation\n"); 88 | 89 | return 1; 90 | } 91 | 92 | to_copy = strlen(src); 93 | copy_at = strlen(dst); 94 | 95 | if (to_copy + copy_at >= dst_size) { 96 | fprintf(stderr, "ERROR: (strncat) buffer too small\n"); 97 | 98 | return 1; 99 | } 100 | 101 | memcpy(dst + copy_at, src, to_copy); 102 | dst[to_copy + copy_at] = '\0'; 103 | 104 | return NVM_SUCCESS; 105 | } 106 | 107 | void 108 | PMW_UTILS_Cal_Diff ( 109 | const LIFETIME_INFO_COUNTER diff, 110 | const LIFETIME_INFO_COUNTER pre, 111 | const LIFETIME_INFO_COUNTER cur 112 | ) 113 | { 114 | uint64_t overflow_val = 0xFFFFFFFFFFFFFFFFULL; 115 | 116 | // bytes_read 117 | CALCULATE_DIFFERENCE(cur->bytes_read, pre->bytes_read, diff->bytes_read, overflow_val); 118 | 119 | // bytes_written 120 | CALCULATE_DIFFERENCE(cur->bytes_written, pre->bytes_written, diff->bytes_written, overflow_val); 121 | 122 | // host_reads 123 | CALCULATE_DIFFERENCE(cur->host_reads, pre->host_reads, diff->host_reads, overflow_val); 124 | 125 | // host_writes 126 | CALCULATE_DIFFERENCE(cur->host_writes, pre->host_writes, diff->host_writes, overflow_val); 127 | 128 | return ; 129 | } 130 | 131 | #ifdef _WIN32 132 | // return in millisec 133 | uint64_t 134 | PMW_UTILS_Get_Curr_Time_For_Sleep () 135 | { 136 | time_t epoch_time; 137 | 138 | time(&epoch_time); 139 | 140 | return epoch_time * MILL_SEC; 141 | } 142 | 143 | uint64_t 144 | PMW_UTILS_Get_Curr_Time_In_Sec () 145 | { 146 | time_t epoch_time; 147 | 148 | time(&epoch_time); 149 | 150 | return epoch_time; 151 | } 152 | #else 153 | // returns in microsec 154 | uint64_t 155 | PMW_UTILS_Get_Curr_Time_For_Sleep () 156 | { 157 | struct timeval time; 158 | 159 | gettimeofday(&time, NULL); 160 | 161 | return (time.tv_sec * MICRO_SEC) + time.tv_usec; 162 | } 163 | 164 | uint64_t 165 | PMW_UTILS_Get_Curr_Time_In_Sec () 166 | { 167 | struct timeval time; 168 | gettimeofday(&time, NULL); 169 | 170 | return time.tv_sec + (time.tv_usec / MICRO_SEC); 171 | } 172 | #endif 173 | 174 | /* ------------------------------------------------------------------------- */ 175 | /*! 176 | * @fn PMW_UTILS_Print_Key 177 | * 178 | * @brief Print the key of a key-value pair (key, value) 179 | * 180 | * @param const char* format - format of the key string 181 | * 182 | * @return void 183 | * Special Notes: 184 | * < None > 185 | */ 186 | void PMW_UTILS_Print_Key (const char* format, ... ) 187 | { 188 | va_list args; 189 | va_start (args, format); 190 | fprintf(stdout, "(\""); 191 | vfprintf (stdout, format, args); 192 | fprintf(stdout, "\", "); 193 | va_end (args); 194 | } 195 | 196 | /* ------------------------------------------------------------------------- */ 197 | /*! 198 | * @fn PMW_UTILS_Print_Value 199 | * 200 | * @brief Print the value of a key-value pair (key, value) 201 | * 202 | * @param const char* format - format of the value string 203 | * 204 | * @return void 205 | * Special Notes: 206 | * < None > 207 | */ 208 | void PMW_UTILS_Print_Value (const char* format, ... ) 209 | { 210 | va_list args; 211 | va_start (args, format); 212 | vfprintf (stdout, format, args); 213 | fprintf(stdout, ")\n"); 214 | va_end (args); 215 | fflush(stdout); 216 | } 217 | 218 | #if !defined(_WIN32) 219 | #if defined(__i386__) 220 | 221 | static __inline__ unsigned long long rdtsc(void) 222 | { 223 | unsigned long long int x; 224 | __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); 225 | return x; 226 | } 227 | 228 | #else 229 | 230 | static __inline__ unsigned long long rdtsc(void) 231 | { 232 | unsigned hi, lo; 233 | __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 234 | return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); 235 | } 236 | 237 | #endif 238 | #endif 239 | 240 | /* ------------------------------------------------------------------------- */ 241 | /*! 242 | * @fn PMW_UTILS_Read_TSC 243 | * 244 | * @brief Read TSC and return 245 | * 246 | * @return TSC 247 | * 248 | * Special Notes: 249 | * < None > 250 | */ 251 | uint64_t PMW_UTILS_Read_TSC () 252 | { 253 | #if _WIN32 254 | return __rdtsc(); 255 | #else 256 | return rdtsc(); 257 | #endif 258 | } 259 | 260 | #if defined(_WIN32) 261 | /* ------------------------------------------------------------------------- */ 262 | /*! 263 | * @fn pmw_api_DLL_Error ( 264 | * wchar_t *out_buffer, 265 | * NVM_UINT32 max_buffer_len 266 | * ) 267 | * 268 | * @brief Convert error code to corresponding error message 269 | * 270 | * @return return code 271 | * 272 | * Special Notes: 273 | * < None > 274 | */ 275 | NVM_UINT32 276 | pmw_api_DLL_Error ( 277 | wchar_t *out_buffer, 278 | NVM_UINT32 max_buffer_len 279 | ) 280 | { 281 | if(!out_buffer) { 282 | return NVM_ERR_UNKNOWN; 283 | } 284 | 285 | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 286 | NULL, 287 | GetLastError(), 288 | 0, 289 | out_buffer, 290 | max_buffer_len, 291 | NULL); 292 | 293 | return NVM_SUCCESS; 294 | } 295 | 296 | /* ------------------------------------------------------------------------- */ 297 | /*! 298 | * @fn HANDLE PMW_UTILS_DLL_Open () 299 | * 300 | * @brief Convert error code to corresponding error message 301 | * 302 | * @return DLL handle 303 | * 304 | * Special Notes: 305 | * < None > 306 | */ 307 | HANDLE 308 | PMW_UTILS_DLL_Open () 309 | { 310 | void *p_mem = NULL; 311 | wchar_t vt_buffer[NVM_EVENT_MSG_LEN]; 312 | 313 | p_mem = LoadLibraryExW(DLL_FILE_NAME, NULL, 0); 314 | if(p_mem == NULL) { 315 | fwprintf(stderr, L"Unable to open shared library: %s\n", DLL_FILE_NAME); 316 | pmw_api_DLL_Error(vt_buffer, NVM_EVENT_MSG_LEN); 317 | fwprintf(stderr, L"Error: %s\n", vt_buffer); 318 | fflush(stderr); 319 | p_mem = (void*) INVALID_HANDLE_VALUE; 320 | } 321 | 322 | return p_mem; 323 | } 324 | 325 | /* ------------------------------------------------------------------------- */ 326 | /*! 327 | * @fn PMW_UTILS_DLL_Sym ( 328 | * HANDLE handle, 329 | * const char *symbol 330 | * ) 331 | * 332 | * @brief Get symbol from DLL 333 | * 334 | * @return pointer to symbol 335 | * 336 | * Special Notes: 337 | * < None > 338 | */ 339 | void* 340 | PMW_UTILS_DLL_Sym ( 341 | HANDLE handle, 342 | const char *symbol 343 | ) 344 | { 345 | void *p_mem = NULL; 346 | wchar_t vt_buffer[NVM_EVENT_MSG_LEN]; 347 | 348 | #if defined(UNICODE) 349 | char a_symbol[NVM_EVENT_MSG_LEN]; 350 | w2a(a_symbol, symbol, NVM_EVENT_MSG_LEN); 351 | p_mem = GetProcAddress((HINSTANCE)handle, a_symbol); 352 | #else 353 | p_mem = GetProcAddress((HINSTANCE)handle, symbol); 354 | #endif 355 | if(p_mem == NULL) { 356 | pmw_api_DLL_Error(vt_buffer, NVM_EVENT_MSG_LEN); 357 | fprintf(stderr, "Unable to find symbol in shared library: %s\n", symbol); 358 | fwprintf(stderr, L"Error: %s\n", vt_buffer); 359 | fflush(stderr); 360 | } 361 | 362 | return p_mem; 363 | } 364 | 365 | /* ------------------------------------------------------------------------- */ 366 | /*! 367 | * @fn PMW_UTILS_DLL_Close ( 368 | * HANDLE handle 369 | * ) 370 | * 371 | * @brief Close DLL handle 372 | * 373 | * @return return code 374 | * 375 | * Special Notes: 376 | * < None > 377 | */ 378 | NVM_UINT32 379 | PMW_UTILS_DLL_Close ( 380 | HANDLE handle 381 | ) 382 | { 383 | NVM_UINT32 ret_val = NVM_SUCCESS; 384 | wchar_t vt_buffer[NVM_EVENT_MSG_LEN]; 385 | 386 | if (handle != INVALID_HANDLE_VALUE && (void*) handle != NULL) { 387 | ret_val = FreeLibrary((HINSTANCE) handle); 388 | if(ret_val != 0) { 389 | ret_val = NVM_SUCCESS; 390 | } 391 | else { 392 | ret_val = NVM_ERR_UNKNOWN; 393 | } 394 | } 395 | 396 | if (ret_val != NVM_SUCCESS) { 397 | pmw_api_DLL_Error(vt_buffer, NVM_EVENT_MSG_LEN); 398 | fprintf(stderr, "Unable to close handle: %p\n", handle); 399 | fwprintf(stderr, L"Error: %s\n", vt_buffer); 400 | fflush(stderr); 401 | } 402 | 403 | return ret_val; 404 | } 405 | 406 | #endif 407 | 408 | /* ------------------------------------------------------------------------- */ 409 | /*! 410 | * @fn PMW_COLLECTION_Sleep ( 411 | * uint64_t sleep_time 412 | * ) 413 | * 414 | * @brief Sleep the flow for the given time 415 | * 416 | * @return No Return 417 | * 418 | * Special Notes: 419 | * < None > 420 | */ 421 | void 422 | PMW_UTILS_Sleep(uint64_t sleep_time) 423 | { 424 | #ifdef _WIN32 425 | Sleep(sleep_time); 426 | #else 427 | usleep(sleep_time); 428 | #endif 429 | } -------------------------------------------------------------------------------- /src/pmwatch-stop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a 8 | # copy of this software and associated documentation files (the "Software"), 9 | # to deal in the Software without restriction, including without limitation 10 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | # and/or sell copies of the Software, and to permit persons to whom the 12 | # Software is furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | # DEALINGS IN THE SOFTWARE. 24 | 25 | PROCESS_NAME="pmwatch" 26 | 27 | pmw_pid_str=`ps -eo pid,comm | grep -w ${PROCESS_NAME} | grep -v ${PROCESS_NAME}-stop` 28 | 29 | if [[ -z ${pmw_pid_str} ]] ; then 30 | echo "pmwatch is not running!" 31 | exit 32 | fi 33 | 34 | itr=1 35 | while [[ itr -le 10 ]] 36 | do 37 | pmw_pid=`echo ${pmw_pid_str} | cut -d " " -f${itr}` 38 | if [[ ! -z ${pmw_pid} ]] ; then 39 | break 40 | fi 41 | itr=`expr ${itr} + 1` 42 | done 43 | 44 | res=`kill -9 ${pmw_pid}` 45 | echo 46 | if [[ $? -eq 0 ]] ; then 47 | echo "pmwatch stopped successfully." 48 | else 49 | echo "Unable to stop pmwatch." 50 | fi 51 | echo 52 | -------------------------------------------------------------------------------- /src/pmwatch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (C) 2017 - 2019 Intel Corporation. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef _WIN32 35 | #include 36 | #include 37 | #pragma intrinsic(__rdtsc) 38 | #else 39 | #include 40 | #include 41 | #include 42 | #endif 43 | 44 | #include "nvm_types.h" 45 | #include "nvm_management.h" 46 | #include "inc/pmw_utils.h" 47 | #include "inc/pmw_comm.h" 48 | #include "inc/pmw_version.h" 49 | #include "inc/pmw_collect.h" 50 | 51 | FILE *fout_m; 52 | int loops_m = 0, infinite_run_m = 0; 53 | uint64_t usec_sample_time_m, usec_sample_time_prev_m; 54 | int collect_lifetime_m = 0; 55 | char SEMICOLON_M[] = ";"; 56 | char TAB_M[] = "\t"; 57 | char DELIMITER_M[] = ";"; 58 | int output_format_one_m = 1; 59 | int output_format_two_m = 0; 60 | int collect_health_m = 0; 61 | 62 | static char output_version[] = PRODUCT_NAME" "STRINGIFY(MAJOR_VERSION)"."STRINGIFY(MINOR_VERSION)""UPDATE_STRING""PRODUCT_TYPE_STR""ENG_BUILD; 63 | static char build_date[] = __DATE__" at "__TIME__; 64 | 65 | void 66 | pmwatch_print_help() 67 | { 68 | fprintf(stdout, "Usage:\npmwatch \n"); 69 | fprintf(stdout, "pmwatch 1 (to run indefinitely; use \"pmwatch-stop\" to stop the collection)\n"); 70 | fprintf(stdout, "pmwatch 0 (to run indefinitely with user specified collection time)\n"); 71 | fprintf(stdout, "pmwatch -a | --avail (tool version and Optane DCPMM DIMM topology information)\n"); 72 | fprintf(stdout, "pmwatch -l (collect lifetime data snapshot of memory performance)\n"); 73 | fprintf(stdout, "Options:\n"); 74 | fprintf(stdout, "\t-hi, --health-info\tcollects health information\n"); 75 | fprintf(stdout, "\t-f \twrite the result to a csv file\n"); 76 | fprintf(stdout, "\t-F \tappend the result to a csv file\n"); 77 | fprintf(stdout, "\t-g, --group-metrics-format\tdisplay the output in metric grouping format\n"); 78 | fprintf(stdout, "\t-td, --tab-delimited\tuse tab as delimiter. Default is comma\n"); 79 | fprintf(stdout, "\n"); 80 | fflush(stdout); 81 | } 82 | 83 | int 84 | pmwatch_parse_cli ( 85 | int argc, 86 | char* argv[] 87 | ) 88 | { 89 | int itr, valid_cli = 0, file_logging = 0, ret_val; 90 | double sample_time = 1; 91 | char *file_name; 92 | char extension[] = ".csv"; 93 | char file_mode[] = "w"; 94 | 95 | ret_val = STRNCPY_SAFE(DELIMITER_M, sizeof(DELIMITER_M), SEMICOLON_M); 96 | if (ret_val != NVM_SUCCESS) { 97 | return ret_val; 98 | } 99 | 100 | // check if sufficient args are available 101 | if(argc < 3){ 102 | // check if indefinite run or single snapshot 103 | if (argc == 2) { 104 | if (strcmp(argv[1], "1") == 0) { 105 | infinite_run_m = 1; 106 | valid_cli = 1; 107 | } 108 | else if (strcmp(argv[1], "-l") == 0) { 109 | collect_lifetime_m = 1; 110 | valid_cli = 1; 111 | } 112 | } 113 | 114 | if (!valid_cli) { 115 | pmwatch_print_help(); 116 | return NVM_ERR_INVALIDPARAMETER; 117 | } 118 | } 119 | 120 | // check if indefinite run or single snapshot (cli: pmwatch 1 -f or pmwatch -l -f) 121 | if (!infinite_run_m && !collect_lifetime_m) { 122 | if (strcmp(argv[2], "-f") == 0 || strcmp(argv[2], "-F") == 0 || \ 123 | strcmp(argv[2], "-td") == 0 || strcmp(argv[2], "--tab-delimited") == 0 || \ 124 | strcmp(argv[2], "-g") == 0 || strcmp(argv[2], "--group-metrics-format") == 0 || \ 125 | strcmp(argv[2], "-hi") == 0 || strcmp(argv[2], "--health-info") == 0) { 126 | if (strcmp(argv[1], "1") == 0) { 127 | infinite_run_m = 1; 128 | valid_cli = 1; 129 | } 130 | else if (strcmp(argv[1], "-l") == 0) { 131 | collect_lifetime_m = 1; 132 | valid_cli = 1; 133 | } 134 | } 135 | } 136 | 137 | // if not indefinite run or single snapshot, parse cli args for sampling time and loops 138 | if (!infinite_run_m && !collect_lifetime_m) { 139 | sample_time = (double) atof(argv[1]); 140 | loops_m = (int) atof(argv[2]); 141 | if (sample_time <= 0 || loops_m < 0) { 142 | fprintf(stderr, "/ should be a positive value.\n"); 143 | return NVM_ERR_INVALIDPARAMETER; 144 | } 145 | if (loops_m == 0) { 146 | infinite_run_m = 1; 147 | } 148 | valid_cli = 1; 149 | } 150 | 151 | if (!valid_cli) { 152 | pmwatch_print_help(); 153 | return NVM_ERR_INVALIDPARAMETER; 154 | } 155 | 156 | #ifdef _WIN32 157 | usec_sample_time_m = (uint64_t) (sample_time * MILL_SEC); 158 | #else 159 | usec_sample_time_m = (uint64_t) (sample_time * MICRO_SEC); 160 | #endif 161 | 162 | // needed during overhead adjustment 163 | usec_sample_time_prev_m = usec_sample_time_m; 164 | 165 | // parse for other options 166 | itr = 2; 167 | while(itr < argc) { 168 | if (strcmp(argv[itr], "-f") == 0) { 169 | file_name = (char*) malloc (SMALL_STR_LEN * sizeof(char)); 170 | if (itr + 1 >= argc) { 171 | fprintf(stderr, "Error: provide an output file name.\n"); 172 | return NVM_ERR_INVALIDPARAMETER; 173 | } 174 | 175 | ret_val = STRNCPY_SAFE(file_name, SMALL_STR_LEN, argv[itr + 1]); 176 | if (ret_val != NVM_SUCCESS) { 177 | return ret_val; 178 | } 179 | file_logging = 1; 180 | } 181 | else if (strcmp(argv[itr], "-F") == 0) { 182 | file_name = (char*) malloc (SMALL_STR_LEN * sizeof(char)); 183 | if (itr + 1 >= argc) { 184 | fprintf(stderr, "Error: provide an output file name.\n"); 185 | return NVM_ERR_INVALIDPARAMETER; 186 | } 187 | 188 | ret_val = STRNCPY_SAFE(file_name, SMALL_STR_LEN, argv[itr + 1]); 189 | if (ret_val != NVM_SUCCESS) { 190 | return ret_val; 191 | } 192 | ret_val = STRNCPY_SAFE(file_mode, sizeof(file_mode), "a"); 193 | if (ret_val != NVM_SUCCESS) { 194 | return ret_val; 195 | } 196 | file_logging = 1; 197 | } 198 | else if (strcmp(argv[itr], "-td") == 0 || strcmp(argv[itr], "--tab-delimited") == 0) { 199 | ret_val = STRNCPY_SAFE(extension, sizeof(extension), ".tsv"); 200 | if (ret_val != NVM_SUCCESS) { 201 | return ret_val; 202 | } 203 | ret_val = STRNCPY_SAFE(DELIMITER_M, sizeof(DELIMITER_M), TAB_M); 204 | if (ret_val != NVM_SUCCESS) { 205 | return ret_val; 206 | } 207 | } 208 | else if (strcmp(argv[itr], "-g") == 0 || strcmp(argv[itr], "--group-metrics-format") == 0) { 209 | output_format_two_m = 1; 210 | output_format_one_m = 0; 211 | } 212 | else if (strcmp(argv[itr], "-hi") == 0 || strcmp(argv[itr], "--health-info") == 0) { 213 | collect_health_m = 1; 214 | } 215 | ++itr; 216 | } 217 | 218 | if (output_format_two_m && collect_lifetime_m) { 219 | fprintf(stderr, "WARNING: -g option cannot be used with -l option. Continuing without -g option ...\n"); 220 | output_format_one_m = 1; 221 | output_format_two_m = 0; 222 | } 223 | 224 | // set stdout or fout 225 | if (file_logging) { 226 | if (strstr(file_name, ".csv") == NULL) { 227 | ret_val = STRNCAT_SAFE(file_name, SMALL_STR_LEN, extension); 228 | if (ret_val != NVM_SUCCESS) { 229 | return ret_val; 230 | } 231 | } 232 | 233 | fout_m = fopen(file_name, file_mode); 234 | if (!fout_m) { 235 | fprintf(stderr, "ERROR: Unable to open output file %s. Output will be sent to the console.\n", file_name); 236 | fout_m = stdout; 237 | } 238 | } 239 | else { 240 | fout_m = stdout; 241 | } 242 | 243 | return NVM_SUCCESS; 244 | } 245 | 246 | void 247 | pmwatch_Close_File () 248 | { 249 | if (fout_m != stdout && fout_m != NULL) { 250 | fclose(fout_m); 251 | } 252 | 253 | return; 254 | } 255 | 256 | int main (int argc, char* argv[]){ 257 | int ret_code = NVM_SUCCESS; 258 | int detailed = 0; 259 | 260 | // check for "-a"/"--avail" option and print in machine-readable format 261 | if ((argc == 2 || argc == 3) && (strcmp(argv[1], "-a") == 0 || strcmp(argv[1], "--avail") == 0)) { 262 | if (argc == 3 && (strcmp(argv[2], "-d") == 0 || strcmp(argv[2], "--detailed") == 0)) { 263 | detailed = 1; 264 | } 265 | PMW_UTILS_Print_Key("%s", "version"); 266 | PMW_UTILS_Print_Value("\"%s\"", output_version); 267 | PMW_UTILS_Print_Key("%s","build_date"); 268 | PMW_UTILS_Print_Value("\"%s\"", build_date); 269 | 270 | ret_code = PMW_COMM_Print_Topology(1, detailed); 271 | if (ret_code != NVM_SUCCESS) { 272 | if (ret_code != NVM_ERR_INVALID_PERMISSIONS) { 273 | PMW_COMM_Print_Warning_Message(); 274 | } 275 | return ret_code; 276 | } 277 | return NVM_SUCCESS; 278 | } 279 | 280 | // parse cli option 281 | if (pmwatch_parse_cli(argc, argv) != NVM_SUCCESS) { 282 | return 1; 283 | } 284 | 285 | ret_code = PMW_COLLECT_Set_Global_Vars (fout_m, 286 | loops_m, 287 | infinite_run_m, 288 | usec_sample_time_m, 289 | usec_sample_time_prev_m, 290 | collect_lifetime_m, 291 | output_format_one_m, 292 | output_format_two_m, 293 | collect_health_m, 294 | DELIMITER_M); 295 | if (ret_code != NVM_SUCCESS) { 296 | return 1; 297 | } 298 | 299 | // initialize: check the availability 300 | // get the DIMM topology 301 | ret_code = PMW_COMM_Init(); 302 | if (ret_code != NVM_SUCCESS) { 303 | if (ret_code != NVM_ERR_INVALID_PERMISSIONS) { 304 | PMW_COMM_Print_Warning_Message(); 305 | } 306 | if (ret_code != PMW_ERR_DLLLOAD && ret_code != PMW_ERR_DLLSYM) { 307 | PMW_COMM_Cleanup(); 308 | } 309 | pmwatch_Close_File(); 310 | return ret_code; 311 | } 312 | 313 | if (collect_lifetime_m) { 314 | PMW_COLLECT_Print_Lifetime_Mem_Info_Counters(); 315 | } 316 | else if (collect_health_m) { 317 | PMW_COLLECT_Print_Health_Info(); 318 | } 319 | else { 320 | // get the counts from 6 SMART counters and output 321 | PMW_COLLECT_Print_Smart_Counters(); 322 | } 323 | 324 | PMW_COMM_Cleanup(); 325 | 326 | pmwatch_Close_File(); 327 | 328 | return 0; 329 | } 330 | --------------------------------------------------------------------------------